Gra jest hostowana tutaj .

Repozytorium GitHub

To moja pierwsza gra / projekt Phaser 3 i wciąż jestem całkiem nowy w Javascript, więc jestem pewien, że jest wiele rzeczy, które mógłbym zrobić lepiej. Najważniejszą rzeczą, którą chciałbym poprawić w moim kodzie, jest wydajność. Następnie efektywność i czytelność kodu, ale wydajność jest najwyższym priorytetem.

Twoja opinia jest cenna, nawet jeśli nie masz żadnego doświadczenia z PhaserJS , ponieważ wiele rzeczy, które prawdopodobnie mógłbym robić lepiej, dotyczy tylko czystego JavaScript.

Mój kod 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); } } } } 

Odpowiedź

A jeśli chcesz dodać funkcję dla maksymalnie 3 graczy?

Ty „Musiałbym utworzyć” player3AnimationStage, player3SpriteSheet itp. Znajduje się on również w „gameState”, co prawdopodobnie ma sens, ale nadal można by je podzielić na własną klasę.

Na przykład:

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

Lub jeszcze lepiej, miej tablicę graczy. Spróbuj tak zaprogramować swoją grę, aby nie miało znaczenia, ilu jest graczy. (Np. Iteruj listę graczy).

Twoje kolory mogą być przekształcone w ENUM lub klasę z score, colorName, colorCode.

Sugerowałbym zadeklarowanie niektórych zmiennych u góry, aby ułatwić konserwację. Na przykład kluczowe elementy div (hiScoreTable). (Lub nawet tylko identyfikatory elementów). Obrazy.

Staraj się unikać „liczb maigc” używając nazwanych zmiennych. Na przykład, co to jest „17” ?:

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

Unikanie „magicNumbers” również zmniejsza powielanie kodu i ułatwia konserwację. Na przykład w celu zwiększenia szybkości odtwarzacza obecnie musielibyśmy to zmienić w co najmniej 2 miejscach.

Odpowiedź

Ten kod świetnie wykorzystuje słowa kluczowe let i const gdzie stosowne. Niektóre funkcje są nieco dłuższe, więc można je potencjalnie podzielić na mniejsze funkcje. Powtarzające się wywołania funkcji, takich jak this.load.image, można by wykonać w pętli po tablicach, jeśli skonfigurowano tablice predefiniowane.


Czy rozważałeś użycie odwołań do elementów , o którym wspomniano w odpowiedzi ślepca na poprzednie pytanie Kalkulator napiwków w czystym JS ?


W metodzie gameState.collisionMethod() w obu przypadkach znajduje się linia, którą można wyciągnąć poza bloki warunkowe: gameState.fallDownCaller(player);


W metodzie gameState.fallDownCaller() obiekt przekazany do this.time.adddEvent() jest taki sam i może być zadeklarowany w jednym miejscu powyżej (lub w osobna funkcja).


Zamiast używać instrukcji switch w displayMedal(), mapowanie nazw kolorów na wartości szesnastkowe mogłoby być użytym.

Na przykład:

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

Następnie użyj go z w operator:

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

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *