Compare commits

...

2 Commits

Author SHA1 Message Date
1042050308 Beta funcional 2024-03-24 11:01:07 +01:00
5e2f525522 alpha 2024-03-24 00:20:25 +01:00
13 changed files with 367 additions and 60 deletions

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"liveServer.settings.port": 5501
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

BIN
assets/coin.ogg Normal file

Binary file not shown.

BIN
assets/flap.ogg Normal file

Binary file not shown.

BIN
assets/gameover.ogg Normal file

Binary file not shown.

BIN
assets/gameover.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
assets/ready.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
assets/taptap.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

408
game.js
View File

@ -1,25 +1,177 @@
const canvas = document.getElementById("lienzo");
const ctx = canvas.getContext("2d");
const width = (canvas.width = 288);
const height = (canvas.height = 512);
ctx.scale(1, 1);
const sprites = [];
const states = {
IDLE: 0,
RUNNING: 1,
GAMEOVER: 2,
};
const background = new Image();
background.src = "./assets/bg.png";
const images = {};
const audiosFx = {};
function cargarRecursos(images, audioFx, callback) {
const recursos = [
{ nombre: "bg", tipo: "imagen" },
{ nombre: "suelo", tipo: "imagen" },
{ nombre: "tuberias", tipo: "imagen" },
{ nombre: "flappybird", tipo: "imagen" },
{ nombre: "logo", tipo: "imagen" },
{ nombre: "ready", tipo: "imagen" },
{ nombre: "gameover", tipo: "imagen" },
{ nombre: "taptap", tipo: "imagen" },
{ nombre: "gameover", tipo: "audio" },
{ nombre: "coin", tipo: "audio" },
{ nombre: "flap", tipo: "audio" },
];
let recursosCargados = 0;
recursos.forEach((recurso) => {
if (recurso.tipo === "imagen") {
const img = new Image();
img.src = `./assets/${recurso.nombre}.png`;
img.onload = () => {
recursosCargados++;
if (recursosCargados === recursos.length) {
callback();
}
};
images[recurso.nombre] = img;
} else if (recurso.tipo === "audio") {
const audio = new Audio();
audio.src = `./assets/${recurso.nombre}.ogg`;
audio.addEventListener("canplaythrough", () => {
recursosCargados++;
if (recursosCargados === recursos.length) {
callback();
}
});
audioFx[recurso.nombre] = audio;
}
});
}
class State {
constructor(state) {
this.state = state;
}
}
class StateIdle extends State {
constructor(sprites) {
super("IDLE");
this.bird = sprites[0];
this.tuberias = [sprites[1], sprites[2]];
sprites.push(new Ready(), new TapTap());
}
enter() {
this.bird.velY = this.bird.gravedad = 0;
this.tuberias.forEach((tuberia) => {
tuberia.velocidad = 0;
});
}
}
class StateRunnig extends State {
constructor(sprites) {
super("RUNNING");
this.bird = sprites[0];
this.tuberias = [sprites[1], sprites[2]];
this.suelo = sprites[3];
sprites.splice(-2);
}
enter() {
this.bird.inicializa();
this.bird.velY = 0;
this.bird.gravedad = 0.2;
this.bird.nFramesAnim = 3;
this.tuberias.forEach((tuberia) => {
tuberia.inicializa();
tuberia.velocidad = 1.5;
});
this.suelo.velocidad = 1.5;
}
}
class StateGameOver extends State {
constructor(sprites) {
super("GAMEOVER");
this.bird = sprites[0];
this.tuberias = [sprites[1], sprites[2]];
this.suelo = sprites[3];
sprites.push(new GameOver(), new TapTap());
}
enter() {
this.bird.velY = this.bird.gravedad = 0;
this.bird.nFramesAnim = 1;
this.tuberias.forEach((tuberia) => {
tuberia.velocidad = 0;
});
this.suelo.velocidad = 0;
}
}
class Logo {
constructor() {
this.logoImg = images["logo"];
this.posX = width / 2 - this.logoImg.width / 2;
this.posY = height - this.logoImg.height - 20;
}
update() {}
draw() {
ctx.drawImage(this.logoImg, this.posX, this.posY);
}
}
class Ready {
constructor() {
this.readyImg = images["ready"];
this.posX = width / 2 - this.readyImg.width / 2;
this.posY = height / 4 - this.readyImg.height / 2;
}
update() {}
draw() {
ctx.drawImage(this.readyImg, this.posX, this.posY);
}
}
class GameOver {
constructor() {
this.gameOverImg = images["gameover"];
this.posX = width / 2 - this.gameOverImg.width / 2;
this.posY = height / 4 - this.gameOverImg.height / 2;
}
update() {}
draw() {
ctx.drawImage(this.gameOverImg, this.posX, this.posY);
}
}
class TapTap {
constructor() {
this.tapTapImg = images["taptap"];
this.posX = width / 2 - this.tapTapImg.width / 2;
this.posY = height / 2 - this.tapTapImg.height / 2;
}
update() {}
draw() {
ctx.drawImage(this.tapTapImg, this.posX, this.posY);
}
}
class Suelo {
constructor(ctx) {
this.ctx = ctx;
this.sueloImg = new Image();
this.sueloImg.src = "./assets/suelo.png";
constructor() {
this.sueloImg = images["suelo"];
this.posX = 0;
this.posY = height - this.sueloImg.height;
this.velocidad = 1;
}
update() {
this.posX--;
if (this.posX + this.sueloImg.width === 0) this.posX = 0;
this.posX -= this.velocidad;
if (this.posX + this.sueloImg.width <= 0) this.posX = 0;
}
draw() {
ctx.drawImage(this.sueloImg, this.posX, this.posY);
@ -28,15 +180,20 @@ class Suelo {
}
class Tuberia {
constructor(ctx, posX, tipo) {
this.ctx = ctx;
this.tuberiaImg = new Image();
this.tuberiaImg.src = "./assets/tuberias.png";
constructor(posX, tipo) {
this.tuberiaImg = images["tuberias"];
this.tipo = tipo;
this.width = 52;
this.height = 320;
this.separacion = 60; //min 60 max 120
this.posX = posX;
this.initialPosX = posX;
this.puntuada = false;
this.inicializa();
}
inicializa() {
this.separacion = 120;
this.velocidad = 0;
this.posX = this.initialPosX;
this.posY =
Math.floor(Math.random() * (300 - 100 + 1)) +
100 -
@ -44,6 +201,7 @@ class Tuberia {
}
generaPos() {
this.puntuada = false;
this.posX = width;
this.posY =
Math.floor(Math.random() * (300 - 100 + 1)) +
@ -52,14 +210,15 @@ class Tuberia {
}
update() {
this.posX--;
this.posX -= this.velocidad;
if (this.posX <= 0 - this.width) {
this.aumentaDificultad();
this.generaPos();
}
}
draw() {
this.ctx.drawImage(
ctx.drawImage(
this.tuberiaImg,
1 * this.width,
this.tipo * this.height,
@ -70,7 +229,7 @@ class Tuberia {
this.width,
this.height
);
this.ctx.drawImage(
ctx.drawImage(
this.tuberiaImg,
0 * this.width,
this.tipo * this.height,
@ -82,59 +241,192 @@ class Tuberia {
this.height
);
}
detectarColision(bird) {
const xA = bird.posX;
const yA = Math.floor(bird.posY);
const wA = bird.width;
const hA = bird.height;
const xB = this.posX;
const yB = this.posY;
const wB = this.width;
const hB = this.height;
const xC = this.posX;
const yC = this.posY + this.height + this.separacion;
const wC = this.width;
const hC = this.height;
const colisionConTuberia1 =
xA < xB + wB && xA + wA > xB && yA < yB + hB && yA + hA > yB;
const colisionConTuberia2 =
xA < xC + wC && xA + wA > xC && yA < yC + hC && yA + hA > yC;
return colisionConTuberia1 || colisionConTuberia2;
}
superada(bird) {
if (bird.posX > this.posX + this.width && this.puntuada === false) {
this.puntuada = true;
return true;
}
return false;
}
aumentaDificultad() {
if (this.separacion >= 80) this.separacion--;
}
}
class Bird {
nFramesAnim = 3;
animFrame = 0;
gameFrame = 0;
constructor(ctx) {
this.gravedad = 0;
this.ctx = ctx;
this.birdImg = new Image();
this.birdImg.src = "./assets/flappybird.png";
this.posY = height / 2;
this.posX = width / 8;
constructor() {
this.birdImg = images["flappybird"];
this.width = 34;
this.height = 24;
this.nFramesAnim = 3;
this.animFrame = 0;
this.gameFrame = 0;
this.inicializa();
}
inicializa() {
this.posY = height / 2;
this.posX = width / 8;
this.gravedad = 0;
this.velY = 0;
}
impulsar() {
this.velY = -4;
}
update() {
this.posY += this.gravedad;
this.velY += this.gravedad;
this.posY += this.velY;
this.animFrame = Math.floor(this.gameFrame / 10) % this.nFramesAnim;
this.gameFrame++;
}
draw() {
this.ctx.drawImage(
this.birdImg,
this.animFrame * this.width,
0,
this.width,
this.height,
this.posX,
this.posY,
this.width,
this.height
);
if (this.velY === 0) {
ctx.drawImage(
this.birdImg,
this.animFrame * this.width,
0,
this.width,
this.height,
this.posX,
this.posY,
this.width,
this.height
);
return;
}
ctx.save();
if (this.velY > 0) {
ctx.translate(this.posX + this.width / 2, this.posY + this.height / 2);
ctx.rotate((30 * Math.PI) / 180);
ctx.drawImage(
this.birdImg,
this.animFrame * this.width,
0,
this.width,
this.height,
-this.width / 2,
-this.height / 2,
this.width,
this.height
);
} else {
ctx.translate(this.posX + this.width / 2, this.posY + this.height / 2);
ctx.rotate((-30 * Math.PI) / 180);
ctx.drawImage(
this.birdImg,
this.animFrame * this.width,
0,
this.width,
this.height,
-this.width / 2,
-this.height / 2,
this.width,
this.height
);
}
ctx.restore();
}
detectarColisiones(tuberias, suelo) {
if (this.posY + this.height >= suelo.posY) {
return true;
}
if (
tuberias[0].detectarColision(this) ||
tuberias[1].detectarColision(this)
) {
return true;
}
return false;
}
pasaTuberia(tuberias) {
if (tuberias[0].superada(this) || tuberias[1].superada(this)) {
return true;
}
return false;
}
}
function gameLoop(timeStamp) {
ctx.clearRect(0, 0, width, height);
ctx.drawImage(background, 0, 0, width, height);
sprites.forEach((sprite) => {
sprite.update();
sprite.draw();
function iniciarJuego() {
let bucle;
let puntuacion = 0;
ctx.font = "24px 'Press Start 2P'";
ctx.fillStyle = "white";
const sprites = [
new Bird(),
new Tuberia(width + 100, 1),
new Tuberia(width + 100 + width * 0.5 + 25, 0),
new Suelo(),
new Logo(),
];
let state = new StateIdle(sprites);
state.enter();
document.addEventListener("keydown", function (event) {
if (event.code === "Space") {
if (state.state === "IDLE" || state.state === "GAMEOVER") {
puntuacion = 0;
state = new StateRunnig(sprites);
state.enter();
}
sprites[0].impulsar();
audiosFx["flap"].play();
}
});
requestAnimationFrame(gameLoop); // Llamar a gameLoop de nuevo en el siguiente fotograma
function gameLoop() {
ctx.clearRect(0, 0, width, height);
ctx.drawImage(images["bg"], 0, 0, width, height);
sprites.forEach((sprite) => {
sprite.update();
sprite.draw();
});
if (sprites[0].pasaTuberia([sprites[1], sprites[2]])) {
audiosFx["coin"].play();
puntuacion++;
}
ctx.fillText(puntuacion, 4, 30);
const colision = sprites[0].detectarColisiones(
[sprites[1], sprites[2]],
sprites[3]
);
if (colision && state.state !== "GAMEOVER") {
state = new StateGameOver(sprites);
state.enter();
audiosFx["gameover"].play();
window.cancelAnimationFrame(bucle);
}
bucle = requestAnimationFrame(gameLoop);
}
gameLoop();
}
sprites.push(
new Bird(ctx),
new Tuberia(ctx, width * 2, 1),
new Tuberia(ctx, width * 2 + width * 0.5 + 25, 0),
new Suelo(ctx)
);
window.onload = () => {
gameLoop();
};
cargarRecursos(images, audiosFx, iniciarJuego);

View File

@ -9,7 +9,8 @@
<title>Flappy bird</title>
</head>
<body>
<canvas id="lienzo"></canvas>
<canvas id="lienzo"></canvas> <br> <br>
<a id="repositorioGit" href="https://git.marklogo.duckdns.org/" target="_blank"> <i class="ti ti-brand-github"></i> Repositorio Git</a>
</body>
<script src="./game.js"></script>
</html>

View File

@ -21,4 +21,15 @@ body {
background-color: #2c5364;
width: auto;
height: 80%;
}
border: 4px solid white;
}
#repositorioGit {
display: inline-block;
background-color: #333;
color: #fff;
padding: 10px 20px;
text-decoration: none;
border-radius: 5px;
transition: background-color 0.3s ease;
}