Spillet hostes her .

GitHub repo

Dette er mit første Phaser 3 spil / projekt, og jeg er stadig ret ny på Javascript, så jeg er sikker på, at der er mange ting, jeg kunne gøre bedre. Nummer 1, som jeg gerne vil forbedre ved min kode, er ydeevnen. Derefter kodeeffektivitet og læsbarhed, men ydeevne er topprioriteten.

Din feedback er værdifuld, selvom du ikke har nogen erfaring med PhaserJS overhovedet , fordi mange ting, som jeg sandsynligvis kunne gøre bedre, har kun at gøre med rent Javascript.

Min JS-kode:

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); } } } } 

Svar

Hvad hvis du vil tilføje funktionaliteten til op til 3 spillere?

Dig “d skal oprette en” player3AnimationStage, player3SpriteSheet osv. Det er også inde i “gameState”, hvilket uden tvivl giver mening, men stadig kan adskilles i sin egen klasse.

For eksempel:

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

Eller endnu bedre, har en række spillere. Prøv at kode dit spil, så det ikke betyder noget, hvor mange spillere der er. (F.eks. Gentag listen over spillere).

Dine farver kan gøres til ENUM eller en klasse med score, colorName, colorCode.

Jeg foreslår at erklære nogle variabler øverst for at gøre vedligeholdelse nemmere. F.eks. centrale div-elementer (hiScoreTable). (Eller endda bare elementernes ider). Billeder.

Prøv at undgå “maigc-numre” ved hjælp af navngivne variabler. Hvad er f.eks. “17” her ?:

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

Undgåelse af “magicNumbers” mindsker også duplikering af kode og gør vedligeholdelse lettere. For eksempel for at øge spillerhastigheden i øjeblikket skulle vi ændre det mindst to steder.

Svar

Denne kode gør stor brug af nøgleordene let og const hvor det er relevant. Nogle af funktionerne er lidt på den lange side, så de kan potentielt opdeles i mindre funktioner. De gentagne opkald til funktioner som this.load.image kunne udføres i en loop over arrays, hvis der blev konfigureret foruddefinerede arrays.


Overvejede du at bruge elementreferencer , som blev nævnt i blindmans svar på dit forrige spørgsmål Tip Lommeregner i ren JS ?


I metoden gameState.collisionMethod() er der i begge tilfælde en linje, der kan trækkes uden for de betingede blokke: gameState.fallDownCaller(player);


I metoden gameState.fallDownCaller() er det objekt, der sendes til this.time.adddEvent() det samme og kunne erklæres et sted ovenfor (ellers i en separat funktion).


I stedet for at bruge en switch udsagn i displayMedal(), kunne en kortlægning af farvenavne til hex-værdier bruges.

For eksempel:

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

Brug det derefter med i operatør:

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

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *