我正在创建一个基本的射击游戏,在这个游戏中你可以杀死怪物来获得分数。在创建新敌人的过程中,我想要一个系统,当你的分数达到某个数字时,它们就会产生,但由于某种原因,分数不会在我的动画功能之外更新,导致新敌人不会产生。这是针对我的情况的相关代码。这是我的第一个问题,所以如果我在提问时做错了什么,请告诉我。非常感谢。
let score = 0;
//enemy variables
let enemyX;
let enemyY;
let enemyRadius;
let enemyColor;
let enemySpeed;
let enemyHealth;
//big enemy variables
let bigenemyX;
let bigenemyY;
let bigenemyRadius;
let bigenemyColor;
let bigenemySpeed;
let bigenemyHealth;
//spawn enemies function; responsible for spawning the basic enemies
const spawnEnemies = () => {
setInterval(() => {
//enemy variables
enemyY = Math.random() * canvas.height;
enemyRadius = 50;
enemyX = canvas.width + enemyRadius;
enemyColor = 'green';
enemySpeed = 7;
enemyHealth = 150;
//creates a new enemy into the enemies array every second
enemies.push(new Enemy(enemyX, enemyY, enemyRadius, enemyColor, enemySpeed, enemyHealth))
}, 1000)
}
//spawn big enemies function; responsible for spawning the big enemies
const spawnBigEnemies = () => {
setInterval(() => {
//big enemy variables
bigenemyY = Math.random() * canvas.height;
bigenemyRadius = 100;
bigenemyX = canvas.width + bigenemyRadius;
bigenemyColor = 'pink';
bigenemySpeed = 3;
bigenemyHealth = 500;
//creates a new big enemy into the enemies array every ten seconds
enemies.push(new Enemy(bigenemyX, bigenemyY, bigenemyRadius, bigenemyColor, bigenemySpeed, bigenemyHealth))
}, 10000)
}
const animate = () => {
enemies.forEach((enemy, index) => {
//enemy dies
if (enemy.health === 0) {
//removes enemy from enemies array
enemies.splice(index, 1)
//increases score by 1 if regular enemy is killed
if (enemy.radius === enemyRadius) {
score ++;
scoreHTML.innerHTML = score;
}
//increases score by 5 if big enemy is killed
else if (enemy.radius === bigenemyRadius) {
score += 5;
scoreHTML.innerHTML = score;
}
}
//game ends if rocket and enemy collide or no lives remaining
if (collides(rocket, enemy) || lives === 0) {
//pauses animation on canvas
cancelAnimationFrame(animationId)
//game over screen displays
gameOverModal.style.display = 'flex'
//displays score on game over screen
pointsHTML.innerHTML = score;
}
//if enemy goes off screen
if (enemy.x - enemy.radius <= 0) {
//deletes the enemy from enemies array
enemies.splice(index, 1);
//lives go down
lives --;
//lives counter is updated
livesHTML.innerHTML = lives;
}
})
}
}
//spawns enemies on screen
spawnEnemies()
//TESTING for big enemy spawning
if (score >= 50) {
spawnBigEnemies()
}
因此,当浏览器解析代码时,最后一条if
语句只运行一次。然而,到那时,用户显然不会达到50分,因此if
语句永远不会运行spawnBigEnemies()
。
如何解决:你需要一种方法;手表;分数,以确定何时产生新的敌人。我不能预先给你一个解决方案,因为我看不到你所有的代码。但如果你想";手表;JS中的一个变量:当一个变量在JavaScript中改变值时,我如何检测?
p/S:您也只希望在分数达到50后调用函数spawnBigEnemies()
一次,因为您在此函数中使用了setInterval
编辑
let isSpawning = true;
const spawnEnemies = () => {
setInterval(() => {
//your codes
...
if (score >= 50 && isSpawning) {
spawnBigEnemies();
isSpawning = false;
}
}, 1000)
}
我不是专家,但我猜分数不会更新,因为您使用的是let
术语。您可能需要使用var
来使作用域全局化。按照代码现在的方式,分数只会在函数内部发生变化。
// let is declared here.
let i = 0;
for (let i=0; i<5; i++) {
console.log("let iteration");
}
// i will still be zero.
console.log("i is", i);
// var is declared here
var j = 0;
for (var j=0; j<5; j++) {
console.log("var iteration");
}
// j will still be four.
console.log("j is", j);
我可以看到你在检查分数>50。由于分数最初为零,这将永远不会奏效。相反,在更改分数时进行此检查。在你的情况下,只有当普通敌人被杀死时,你的分数才会改变(在大敌人之前(,所以只需在那里添加复选框。
在此处添加了检查-
if (enemy.radius === enemyRadius) {
score ++;
//TESTING for big enemy spawning
if (score >= 5) { // do additional check with a flag so that this condition only evaluates to true only once
spawnBigEnemies()
}
scoreHTML.innerHTML = score;
}
最终代码-
//canvas setup
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d', {alpha: false});
//sets canvas dimensions
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
//global variables
let score = 0;
let lives = 10;
let up = false;
let down = false;
let spawn = false;
//dom variables
let scoreHTML = document.getElementById('score');
let livesHTML = document.getElementById('lives');
const startGameButton = document.getElementById('startgamebtn');
const gameOverModal = document.getElementById('gameovermodal');
let pointsHTML = document.getElementById('points');
//player variables
let playerX = 100;
let playerY = 100;
let playerWidth = 100;
let playerHeight = 100;
let playerColor = 'red';
let playerSpeed = 12;
//bullet variables
let bulletRadius = 5;
let bulletColor = 'white';
let bulletSpeed = 20;
//enemy variables
let enemyX;
let enemyY;
let enemyRadius;
let enemyColor;
let enemySpeed;
let enemyHealth;
//big enemy variables
let bigenemyX;
let bigenemyY;
let bigenemyRadius;
let bigenemyColor;
let bigenemySpeed;
let bigenemyHealth;
//Rocket class
class Rocket {
constructor(x, y, width, height, color, dy) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
this.dy = dy;
}
//rocket draw func
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
//rocket update func
update() {
this.draw();
//makes rocket move up and down
this.y += this.dy;
}
}
//Bullet class
class Bullet {
constructor(x, y, radius, color, dx) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.dx = dx;
}
//bullet draw func
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
ctx.fillStyle = this.color;
ctx.fill();
ctx.closePath();
}
//bullet update func
update() {
this.draw();
//makes bullet move right
this.x += this.dx;
}
}
//Enemy class
class Enemy {
constructor(x, y, radius, color, dx, health) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.dx = dx;
this.health = health;
}
//enemy draw func
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
ctx.fillStyle = this.color;
ctx.fill();
ctx.closePath();
}
//enemy update func
update() {
this.draw();
//makes enemies move left
this.x -= this.dx;
}
}
//rocket declaration
let rocket = new Rocket(playerX, playerY, playerWidth, playerHeight, playerColor, 0);
//bullet array declaration
let bullets = [];
//enemies array declaration
let enemies = [];
//initiation function: called when game is restarted; used to reset everything
const init = () => {
rocket = new Rocket(100, 100, 100, 100, 'red', 0);
bullets = [];
enemies = [];
lives = 10;
score = 0;
scoreHTML.innerHTML = score;
livesHTML.innerHTML = lives;
pointsHTML.innerHTML = score;
spawn = false;
}
//spawn enemies function; responsible for spawning the basic enemies
const spawnEnemies = () => {
setInterval(() => {
//enemy variables
enemyY = Math.random() * canvas.height;
enemyRadius = 50;
enemyX = canvas.width + enemyRadius;
enemyColor = 'green';
enemySpeed = 7;
enemyHealth = 150;
//creates a new enemy into the enemies array every second
enemies.push(new Enemy(enemyX, enemyY, enemyRadius, enemyColor, enemySpeed, enemyHealth))
}, 1000)
}
//spawn big enemies function; responsible for spawning the big enemies (WORK IN PROGRESS)
const spawnBigEnemies = () => {
setInterval(() => {
//big enemy variables
bigenemyY = Math.random() * canvas.height;
bigenemyRadius = 100;
bigenemyX = canvas.width + bigenemyRadius;
bigenemyColor = 'pink';
bigenemySpeed = 3;
bigenemyHealth = 500;
//creates a new big enemy into the enemies array every ten seconds
enemies.push(new Enemy(bigenemyX, bigenemyY, bigenemyRadius, bigenemyColor, bigenemySpeed, bigenemyHealth))
}, 10000)
}
//collides function; responsible for collision between rocket and enemies
const collides = (rect, circle, collide_inside) => {
// compute a center-to-center vector
let half = { x: rect.width / 2, y: rect.height / 2 };
let center = {
x: circle.x - (rect.x + half.x),
y: circle.y - (rect.y + half.y)};
// check circle position inside the rectangle quadrant
let side = {
x: Math.abs (center.x) - half.x,
y: Math.abs (center.y) - half.y
};
if (side.x > circle.radius || side.y > circle.radius) // outside
return false;
if (side.x < -circle.radius && side.y < -circle.radius) // inside
return collide_inside;
if (side.x < 0 || side.y < 0) // intersects side or corner
return true;
// circle is near the corner
return side.x * side.x + side.y * side.y < circle.radius * circle.radius;
}
//used for pausing animation frames
let animationId;
const animate = () => {
animationId = requestAnimationFrame(animate);
//clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
//canvas background
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
//rockets update function so it can move
rocket.update();
//bullet foreach iterator
bullets.forEach((bullet, index) => {
//allows all bullets to move
bullet.update()
//deletes bullets if it moves outside canvas
if (bullet.x - bullet.radius > canvas.width) {
setTimeout(() => {
bullets.splice(index, 1);
}, 0)
}
})
//enemies foreach iterator
enemies.forEach((enemy, index) => {
//allows all enemies to move
enemy.update()
//responsible for bullet and enemy collision
bullets.forEach((bullet, bulletIndex) => {
//variable used to measure distance between bullet and enemy
const dist = Math.hypot(bullet.x - enemy.x, bullet.y - enemy.y);
if (dist - enemy.radius - bullet.radius < 1) {
//deletes the bullet from bullets array
bullets.splice(bulletIndex,1)
//decreases enemy's health
enemy.health -= 50;
}
})
//enemy dies
if (enemy.health === 0) {
//removes enemy from enemies array
enemies.splice(index, 1)
//increases score by 1 if regular enemy is killed
if (enemy.radius === enemyRadius) {
score ++;
//TESTING for big enemy spawning
if (score >= 5) {
spawnBigEnemies()
}
scoreHTML.innerHTML = score;
}
//increases score by 5 if big enemy is killed
else if (enemy.radius === bigenemyRadius) {
score += 5;
scoreHTML.innerHTML = score;
}
}
//game ends if rocket and enemy collide or no lives remaining
if (collides(rocket, enemy) || lives === 0) {
//pauses animation on canvas
cancelAnimationFrame(animationId)
//game over screen displays
gameOverModal.style.display = 'flex'
//displays score on game over screen
pointsHTML.innerHTML = score;
}
//if enemy goes off screen
if (enemy.x - enemy.radius <= 0) {
//deletes the enemy from enemies array
enemies.splice(index, 1);
//lives go down
lives --;
//lives counter is updated
livesHTML.innerHTML = lives;
}
})
}
//responsible for character movement
addEventListener('keydown', evt => {
//W key is pressed
if (evt.code === "KeyW") {
//player moves up
rocket.dy = -playerSpeed;
//stops player from moving up too far
if (rocket.y < 10) {
//used for condition for keeping character in canvas
up = true
}
if (up) {
//brings rocket back to canvas
rocket.y = 0
//rocket velocity is 0
rocket.dy = 0
//resets up bool
up = false;
}
}
//S key is pressed
else if (evt.code === "KeyS") {
//player moves down
rocket.dy = playerSpeed;
//stops player from moving down too far
if (rocket.y > canvas.height - rocket.height) {
//used for condition for keeping character in canvas
down = true;
}
if (down) {
//brings rocket back to canvas
rocket.y = canvas.height - rocket.height + 10
//rocket velocity is 0
rocket.dy = 0
//resets up bool
down = false;
}
}
});
//when keys are released
addEventListener('keyup', evt => {
if (evt.code === "KeyW" || evt.code === "KeyS") {
//stops player from moving if keys are released
rocket.dy = 0;
}
})
//shooting
addEventListener('click', event => {
//if mouse is clicked new bullet is created and added to bullets array
bullets.push(new Bullet(rocket.x + rocket.width, rocket.y + (rocket.height / 2), bulletRadius, bulletColor, bulletSpeed))
});
//start game button in game over modal; used to restart the game
startGameButton.addEventListener('click', () => {
//calls the function to reset all data
init();
//makes the game start animating again
animate();
//makes game over modal go away
gameOverModal.style.display = 'none';
})
//spawns enemies on screen
spawnEnemies()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>New Game</title>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
<style>
body {
background: black;
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div class="fixed text-white m-3 select-none">
<span>Score: </span>
<span id="score">0</span>
</div>
<div class="fixed text-white mt-9 ml-3 select-none">
<span>Lives: </span>
<span id="lives">10</span>
</div>
<div class="fixed inset-0 flex items-center justify-center select-none" id="gameovermodal">
<div class="bg-white max-w-md w-full p-6 text-center">
<h2 class="text-3xl font-bold mt-2 leading-none" id="points">0</h1>
<p class="text-sm">Points</p>
<div>
<button class="bg-red-500 w-full py-3 rounded-full mt-2 text-white focus:outline-none" id="startgamebtn">Start Game</button>
</div>
</div>
</div>
<canvas id="game"></canvas>
<script src="game.js" type="text/javascript"></script>
</body>
</html>