El juego se aloja aquí .

Repositorio de GitHub

Este es mi primer juego / proyecto de Phaser 3 y todavía soy bastante nuevo en Javascript, así que estoy seguro de que hay muchas cosas que podría estar haciendo mejor. Lo número 1 que me gustaría mejorar de mi código es el rendimiento. Luego, la efectividad y la legibilidad del código, pero el rendimiento es la máxima prioridad.

Sus comentarios son valiosos incluso si no tiene experiencia con PhaserJS , porque muchas cosas que probablemente podría estar haciendo mejor solo tienen que ver con Javascript puro.

Mi código JS:

const width = window.innerWidth; const height = window.innerHeight; let hiScore = localStorage["hiScore"] || 0; const config = { width: width, height: height, backgroundColor: 0x50C875, scene: { preload, create, update, }, physics: { default: "arcade", arcade: { gravity: { y: 50 }, }, } } const gameState = { gameOver: false, score: 0, scoreText: false, player1AnimationStage: 0, player2AnimationStage: 0, player1SpriteSheet: ["upflap", "midflap", "downflap",], player2SpriteSheet: ["player2_upflap", "player2_midflap", "player2_downflap",], player1Y: (height / 2 * 0.5), player2Y: (height / 2 * 0.5), secondPlayerSpawned: false, player1Dead: false, player2Dead: false, } const game = new Phaser.Game(config, "root"); game.clearBeforeRender = false; function preload() { this.load.image("background", "assets/images/background.png"); this.load.image("ground", "assets/images/ground.png"); this.load.image("pipe", "assets/images/pipe.png"); this.load.image("upflap", "assets/images/upflap.png"); this.load.image("midflap", "assets/images/midflap.png"); this.load.image("downflap", "assets/images/downflap.png"); this.load.image("player2_upflap", "assets/images/player2_upflap.png"); this.load.image("player2_midflap", "assets/images/player2_midflap.png"); this.load.image("player2_downflap", "assets/images/player2_downflap.png"); this.load.audio("hit", "assets/audio/hit.mp3"); this.load.audio("point", "assets/audio/point.mp3"); this.load.audio("wing", "assets/audio/wing.mp3"); this.load.audio("die", "assets/audio/die.mp3"); } function create() { gameState.hitSound = this.sound.add("hit"); gameState.pointSound = this.sound.add("point"); gameState.wingSound = this.sound.add("wing"); gameState.dieSound = this.sound.add("die"); // Hide Score Table document.getElementById("hiScoreTable").style.display = "none"; const colliderTile = this.physics.add.staticGroup(); gameState.colliderTile = colliderTile.create(50, 0, "pipe").setScale(0.1, 80).refreshBody(); gameState.colliderTile2 = colliderTile.create(1, 0, "pipe").setScale(0, 80).refreshBody(); gameState.bgTile = this.add.tileSprite(0, height, width, height, "background").setScale(2); gameState.ground = this.physics.add.staticGroup(); gameState.ground.create(0, height, "ground").setScale((8.6, 1)).refreshBody(); gameState.groundTile = this.add.tileSprite(0, height, width, null, "ground").setScale(8.6, 1); gameState.gameOver = false; gameState.player1 = this.physics.add.sprite(100, gameState.player1Y, "midflap").setScale(2); gameState.player1.body.acceleration.y = 1500; gameState.pipes = this.physics.add.group(); gameState.scoreText = this.add.text((width / 2) - 100, 100, `Score: ${gameState.score}`, { fontSize: "40px", fontWeight: "bold", }); gameState.secondPlayerSpawned = false; // Layers gameState.groundTile.setDepth(1); gameState.pipes.setDepth(2); gameState.scoreText.setDepth(3); gameState.playSoundMethod = (sound) => { this.sound.play(sound); } const addRowOfPipes = () => { const hole = Math.floor(Math.random() * 7) + 3; for (let i = 0; i < 17; i++) { if (i !== hole && i !== hole + 1 && i !== hole + 2) { let pipe = gameState.pipes.create(width - 60, i * 50 + 25, "pipe"); pipe.body.setVelocityX(-200); pipe.outOfBoundsKill = true; pipe.body.allowGravity = false; pipe.body.immovable = true; this.physics.add.collider(pipe, gameState.colliderTile2, (item) => { if (i === 16) { gameState.pointSound.play(); gameState.score++; if (gameState.scoreText) gameState.scoreText.destroy(); gameState.scoreText = this.add.text((width / 2) - 100, 100, `Score: ${gameState.score}`, { fontSize: "40px", fontWeight: "bold", }); } item.destroy(); }) if (i === 16) { pipe.onWorldBounds = true; } } } } gameState.fallDown = () => { if (gameState.player1Dead) { gameState.player1.y += 5; if (gameState.player1.y > height) gameState.player1fallDownCaller.destroy(); } if (gameState.player2Dead) { gameState.player2.y += 5; if (gameState.player2.y > height) gameState.player2fallDownCaller.destroy(); } } addRowOfPipes(); gameState.gameOverMethod = () => { this.physics.pause(); gameState.scoreText.destroy(); if (gameState.score > hiScore) localStorage["hiScore"] = gameState.score; hiScore = localStorage.getItem("hiScore"); document.getElementById("hiScoreTable").style.display = "initial"; document.getElementById("score").innerHTML = gameState.score; document.getElementById("hiScore").innerHTML = hiScore; birdAnimation.destroy(); gameState.gameOver = true; this.add.text(); pipeGen.destroy(); gameState.player1.setVelocityY(150); gameState.player1.setVelocityX(0); if (gameState.secondPlayerSpawned) { gameState.player2.setVelocityY(150); gameState.player2.setVelocityX(0); } if (gameState.score > 10) { if (gameState.score > 20) { if (gameState.score > 30) { displayMedal("gold"); } displayMedal("silver"); } displayMedal("bronze"); } function displayMedal(medal) { let medalColor; document.getElementById("medalContainer").style.display = "initial"; switch (medal) { case "bronze": medalColor = "#cd7f32"; break; case "silver": medalColor = "#c0c0c0"; break; case "gold": medalColor = "#ccac00"; break; } document.getElementById("medal").style.backgroundColor = medalColor; } gameState.score = 0; } gameState.fallDownCaller = (player) => { if (player === "player1") { gameState.player1fallDownCaller = this.time.addEvent({ delay: 10, callback: gameState.fallDown, loop: true, }) } else { gameState.player2fallDownCaller = this.time.addEvent({ delay: 10, callback: gameState.fallDown, loop: true, }) } } gameState.collisionMethod = (player) => { if (player === "player1") { gameState.player1Dead = true; if ((gameState.player1Dead && gameState.player2Dead) || !gameState.secondPlayerSpawned) { gameState.gameOverMethod(); } gameState.fallDownCaller(player); } else { gameState.player2Dead = true; if (gameState.player1Dead && gameState.player2Dead) { gameState.gameOverMethod(); } gameState.fallDownCaller(player); } } // Colliders gameState.player1.setCollideWorldBounds(true); this.physics.add.collider(gameState.player1, gameState.ground, () => { gameState.dieSound.play(); gameState.collisionMethod("player1") }); this.physics.add.collider(gameState.player1, gameState.pipes, () => { gameState.hitSound.play(); gameState.collisionMethod("player1"); }); // Initialize input keys gameState.cursors = this.input.keyboard.createCursorKeys(); const pipeGen = this.time.addEvent({ callback: addRowOfPipes, delay: 1500, callbackScope: this, loop: true, }) // Animation const animateBird = () => { gameState.player1AnimationStage++; if (gameState.player1AnimationStage > 2) gameState.player1AnimationStage = 0; if (gameState.secondPlayerSpawned) { gameState.player2AnimationStage++; if (gameState.player2AnimationStage > 2) gameState.player2AnimationStage = 0; } gameState.player1.setTexture(gameState.player1SpriteSheet[gameState.player1AnimationStage]); if (gameState.secondPlayerSpawned) gameState.player2.setTexture(gameState.player2SpriteSheet[gameState.player2AnimationStage]); } const birdAnimation = this.time.addEvent({ callback: animateBird, delay: 100, callbackScope: this, loop: true, }) } function update() { if (!gameState.gameOver) { gameState.bgTile.tilePositionX += 0.1; gameState.groundTile.tilePositionX += 1; } // Press spacebar to fly up if (gameState.cursors.space.isDown) { if (gameState.gameOver) { this.scene.restart(); } else { gameState.wingSound.play(); gameState.player1.setVelocityY(-350); } } const spawnSecondPlayer = () => { gameState.secondPlayerSpawned = true; gameState.player2 = this.physics.add.sprite(100, gameState.player2Y, "player2_midflap").setScale(2); gameState.player2.body.acceleration.y = 1500; gameState.player2.setCollideWorldBounds(true); this.physics.add.collider(gameState.player2, gameState.ground, () => { gameState.dieSound.play(); gameState.collisionMethod("player2") }); this.physics.add.collider(gameState.player2, gameState.pipes, () => { gameState.hitSound.play(); gameState.collisionMethod("player2"); }); } if (!gameState.secondPlayerSpawned) { if (gameState.cursors.shift.isDown) { spawnSecondPlayer(); } } else { if (gameState.cursors.shift.isDown) { if (gameState.gameOver) { this.scene.restart(); } else { gameState.player2.setVelocityY(-350); } } } } 

Respuesta

¿Qué sucede si desea agregar la funcionalidad para hasta 3 jugadores?

Usted «Tendría que crear un» player3AnimationStage, player3SpriteSheet, etc. También está dentro de «gameState», lo que posiblemente tiene sentido, pero aún podría separarse en su propia clase.

Por ejemplo:

class Player { constructor(spriteSheet, animationStage) { this.SpriteSheet = spriteSheet; this.AnimationStage = animationStage; } } const gameState = { player1: new Player(...); player2: new Player(...); 

O mejor aún, tenga una variedad de reproductores. Intenta codificar tu juego para que no importe cuántos jugadores haya. (Por ejemplo, iterar a través de la lista de jugadores).

Tus colores podrían convertirse en un ENUM, o una clase con puntuación, nombre de color, código de color.

Sugiero declarar algunas variables en la parte superior, para facilitar el mantenimiento. Como elementos div clave (hiScoreTable). (O incluso solo los identificadores de los elementos). Imágenes.

Trate de evitar «números maigc» utilizando variables con nombre. Por ejemplo, ¿qué es «17» aquí ?:

for (let i = 0; i < 17; i++) 

Evitar «magicNumbers» también disminuye la duplicación de código y facilita el mantenimiento. Por ejemplo, para aumentar la velocidad del reproductor actualmente tendríamos que cambiarlo en al menos 2 lugares.

Respuesta

Este código hace un gran uso de las palabras clave let y const donde corresponda. Algunas de las funciones son un poco largas, por lo que podrían dividirse en funciones más pequeñas. Las llamadas repetidas a funciones como this.load.image podrían realizarse en un bucle sobre matrices si se configuran matrices predefinidas.


¿Consideró usar referencias de elementos? , que se mencionó en la respuesta del ciego a su pregunta anterior Calculadora de propinas en JS puro ?


En el método gameState.collisionMethod() hay una línea en ambos casos que podría sacarse de los bloques condicionales: gameState.fallDownCaller(player);


En el método gameState.fallDownCaller(), el objeto pasado a this.time.adddEvent() es el mismo y podría declararse en un lugar arriba (o en una función separada).


En lugar de usar una instrucción switch en displayMedal(), una asignación de nombres de colores a valores hexadecimales podría ser usado.

Por ejemplo:

const colorToHexMap = { bronze: "#cd7f32", silver: "#c0c0c0", gold: "#ccac00", }; 

Luego úselo con en operador:

if (color in colorToHexMap) { medalColor = colorToHexMap[color]; } 

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *