// Commented because does not work in Sandbox
// window.localStorage; //Ignore this line
// Where all my variables have been assigned
var c = document.getElementById("GameScreen");
var ctx = c.getContext("2d");
var charY = 220;
const gravity = 10;
var score = 0;
var time = 0;
var speed = 5;
var cloneID = 0;
var clonePos = [600];
var clonePoints = [0];
var animationBounce = 0;
var jump = 10;
var charDead = 0;
var dataCharY = [];
var dataDisObst = [];
var disObst = 1000;
var lowestLoopDis;
var jumpFactor = 0;
var disDeath;
var AIgames = 1;
var bestScoreAI = 0;
ctx.translate(c.width / 2, c.height / 2);
// Was going to use this for background trees but haven't done it yet
new obj(50, 50, 30, 30);
// Runs most functions
function runAll() {
if (charDead == 0) {
clearAll(); //This function runs most of the code
updateChar();
createGround();
updateObj();
groundDetect();
updateScore();
hitDetect();
addData();
testBetterAI();
getDisObst();
jumpAI();
removeUnusedObst();
}
}
// Was going to use this for trees but haven't yet
function obj(x, y, width, height) {
this.width = width;
this.height = height;
this.x = x;
this.y = y;
ctx.beginPath();
ctx = c.getContext("2d");
ctx.fillStyle = "brown";
ctx.fillRect(this.x, this.y, this.width, this.height);
ctx.fillStyle = "green";
ctx.arc(-293, 150, 50, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
this.cloneID = cloneID;
}
new obj(-293, 212, 0, 2 * Math.PI);
// Creates the floor (IKR)
function createGround() {
ctx.fillStyle = "green";
ctx.fillRect(-635, 250, c.width, 50);
}
// Creates the character every milisecond (or 10, I can't remember)
function updateChar() {
ctx.fillStyle = "blue";
ctx.fillRect(-300, charY - animationBounce, 15, 30);
ctx.fillStyle = "pink";
ctx.beginPath();
ctx.arc(-293, charY - animationBounce - 15, 15, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
}
// Removes everything in order to be redrawn in new position
function clearAll() {
ctx.clearRect(-700, -700, 2000, 2000);
}
// Redraws every square / object
function updateObj() {
for (var i = 0; i != clonePos.length; i++) {
ctx.fillStyle = "green";
ctx.fillRect(clonePos[i], 220, 30, 30);
}
}
// Creates new square (I also decided to rename them half way through with obstacle instead of object)
function createObst() {
clonePos.push(600);
cloneID++;
}
// Changes the squares / obstacles position relative to the movement
function moveObst() {
for (var ii = 0; ii != clonePos.length; ii++) {
clonePos[ii] -= speed;
}
}
// Tests to see if the character is on the ground
function groundDetect() {
if (charY > 220) {
charY = 220;
}
}
// Makes gravity actually work
function charGravity() {
if (charY < 220) {
charY += gravity;
}
}
// Updates the score counter text
function updateScore() {
document.getElementById("scoreText").innerHTML = score;
}
// Gives the character a little bounce when moving
function charBounce() {
setTimeout(function() {
animationBounce++;
}, 100);
setTimeout(function() {
animationBounce++;
}, 200);
setTimeout(function() {
animationBounce++;
}, 300);
setTimeout(function() {
animationBounce--;
}, 400);
setTimeout(function() {
animationBounce--;
}, 500);
setTimeout(function() {
animationBounce--;
}, 600);
}
// Makes the character jump
function charJump() {
if (charY == 220) {
jump = 4;
setTimeout(function() {
charY -= jump;
}, 20);
jump = 8;
setTimeout(function() {
charY -= jump;
}, 40);
jump = 12;
setTimeout(function() {
charY -= jump;
}, 60);
jump = 16;
setTimeout(function() {
charY -= jump;
}, 80);
setTimeout(function() {
charY -= jump;
}, 100);
setTimeout(function() {
charY -= jump;
}, 120);
}
}
// Detects when the character has a hit a square
function hitDetect() {
for (var iB = 0; iB != clonePos.length; iB++) {
if (clonePos[iB] > -320 && clonePos[iB] < -280 && charY > 200) {
charDied();
}
}
}
// Runs when character dies
function charDied() {
disDeath = disObst;
charDead = 1;
charRevive();
testBetterAI();
decideAdjustments();
}
// Adds score very interval
function addingScore() {
if (charDead == 0) {
score += 100;
}
}
// Adds to an array that I will use later
function addData() {
dataCharY.push(charY);
dataDisObst.push(disObst);
}
// Test to see if one of my AI's (which hasn't been made yet) scores is better than the previous best
function testBetterAI() {
// Commented because does not work in Sandbox
// if (score > localStorage.getItem("bestScore")) {
// }
}
// Calculates the distance to the nearest square / obstacle
function getDisObst() {
lowestLoopDis = 1000;
for (var iiA = 0; iiA != clonePos.length; iiA++) {
if (clonePos[iiA] > -320) {
if (clonePos[iiA] > 0) {
if (Math.abs(clonePos[iiA]) < lowestLoopDis) {
lowestLoopDis = Math.abs(clonePos[iiA]);
}
} else {
if (Math.abs(clonePos[iiA]) < lowestLoopDis) {
lowestLoopDis = Math.abs(clonePos[iiA]);
}
}
}
}
if (lowestLoopDis < disObst) {
disObst = lowestLoopDis;
}
}
// Increments the speed of the obstacles / squares and the character
function addSpeed() {
if (speed < 25) {
speed++;
}
}
// Restarts the game
function charRevive() {
clonePos = [600];
charDead = 0;
score = 0;
time = 0;
speed = 5;
AIgames++;
}
// I accidently did this twice, whoops
function testBetterAI() {
if (score > bestScoreAI) {
bestScoreAI = score;
}
}
// Makes the unfinished AI jump when it wants to
function jumpAI() {
if (disObst <= disDeath + jumpFactor) {
charJump();
}
}
// What changes need to be made in order to improve the AI
function decideAdjustments() {
jumpFactor += Math.floor(Math.random() * 10) - 5;
if (jumpFactor < 0) {
jumpFactor = 0;
}
}
// Removing blocks that are off the screen
function removeUnusedObst() {
if (clonePos[0] < -650) {
clonePos.shift();
}
}
// Intervals here
setInterval(function() {
time++;
}, 1000);
setInterval(function() {
runAll();
}, 10);
setInterval(function() {
moveObst();
}, 50);
setInterval(function() {
charGravity();
}, 25);
setInterval(function() {
createObst();
}, 3000);
setInterval(function() {
charBounce();
}, 650);
setInterval(function() {
addingScore();
}, 3500);
setInterval(function() {
addSpeed();
}, 25000);
#GameScreen {
background-color: CornflowerBlue;
}
#scoreText {
text-align: center;
font-size: 35px;
}
<div id="scoreText"></div>
<canvas id="GameScreen" width="1270px" height="550px"></canvas>
Blex给作者的注释:我编辑了您的问题,以使您的代码在Stack Overflow上可以运行。在此过程中,我必须使用
localStorage
注释两行,因为该行被阻止,将引发错误。由于尚未使用,因此不重要。但是请确保之后再取消注释。在第一行,并在testBetterAI
功能中。
会发生什么
在hitDetect
功能中:
function hitDetect() {
for (var iB = 0; iB != clonePos.length; iB++) {
if (clonePos[iB] > -320 && clonePos[iB] < -280 && charY > 200) {
charDied();
}
}
}
您在clonePos
数组上循环,直到iB
等于该数组的长度。如果满足条件(冲突),则执行charDied
,然后执行charRevive
:
function charRevive() {
clonePos = [600];
// ...
}
同时,hitDetect
循环继续。在某一时刻(似乎是在开始增加速度时],iB
现在高于1
,这是clonePos
的新长度。现在,会发生什么?好吧,您陷入了无限循环:
经验教训
如果您要循环的数组可能在循环中发生变化(并更改其长度),则从不]使用这种条件:
i != myArr.length
如果希望循环在某个时刻结束,总是希望使用更严格的条件:
i < myArr.length
并且,甚至更好,如果在满足条件后循环没有意义,请通过返回结束该循环。例如:
function hitDetect() { for (var iB = 0; iB < clonePos.length; iB++) { if (clonePos[iB] > -320 && clonePos[iB] < -280 && charY > 200) { return charDied(); } } }
固定代码
// Commented because does not work in Sandbox // window.localStorage; //Ignore this line // Where all my variables have been assigned var c = document.getElementById("GameScreen"); var ctx = c.getContext("2d"); var charY = 220; const gravity = 10; var score = 0; var time = 0; var speed = 5; var cloneID = 0; var clonePos = [600]; var clonePoints = [0]; var animationBounce = 0; var jump = 10; var charDead = 0; var dataCharY = []; var dataDisObst = []; var disObst = 1000; var lowestLoopDis; var jumpFactor = 0; var disDeath; var AIgames = 1; var bestScoreAI = 0; ctx.translate(c.width / 2, c.height / 2); // Was going to use this for background trees but haven't done it yet new obj(50, 50, 30, 30); // Runs most functions function runAll() { if (charDead == 0) { clearAll(); //This function runs most of the code updateChar(); createGround(); updateObj(); groundDetect(); updateScore(); hitDetect(); addData(); testBetterAI(); getDisObst(); jumpAI(); removeUnusedObst(); } } // Was going to use this for trees but haven't yet function obj(x, y, width, height) { this.width = width; this.height = height; this.x = x; this.y = y; ctx.beginPath(); ctx = c.getContext("2d"); ctx.fillStyle = "brown"; ctx.fillRect(this.x, this.y, this.width, this.height); ctx.fillStyle = "green"; ctx.arc(-293, 150, 50, 0, 2 * Math.PI); ctx.fill(); ctx.stroke(); this.cloneID = cloneID; } new obj(-293, 212, 0, 2 * Math.PI); // Creates the floor (IKR) function createGround() { ctx.fillStyle = "green"; ctx.fillRect(-635, 250, c.width, 50); } // Creates the character every milisecond (or 10, I can't remember) function updateChar() { ctx.fillStyle = "blue"; ctx.fillRect(-300, charY - animationBounce, 15, 30); ctx.fillStyle = "pink"; ctx.beginPath(); ctx.arc(-293, charY - animationBounce - 15, 15, 0, 2 * Math.PI); ctx.fill(); ctx.stroke(); } // Removes everything in order to be redrawn in new position function clearAll() { ctx.clearRect(-700, -700, 2000, 2000); } // Redraws every square / object function updateObj() { for (var i = 0; i < clonePos.length; i++) { ctx.fillStyle = "green"; ctx.fillRect(clonePos[i], 220, 30, 30); } } // Creates new square (I also decided to rename them half way through with obstacle instead of object) function createObst() { clonePos.push(600); cloneID++; } // Changes the squares / obstacles position relative to the movement function moveObst() { for (var ii = 0; ii < clonePos.length; ii++) { clonePos[ii] -= speed; } } // Tests to see if the character is on the ground function groundDetect() { if (charY > 220) { charY = 220; } } // Makes gravity actually work function charGravity() { if (charY < 220) { charY += gravity; } } // Updates the score counter text function updateScore() { document.getElementById("scoreText").innerHTML = score; } // Gives the character a little bounce when moving function charBounce() { setTimeout(function() { animationBounce++; }, 100); setTimeout(function() { animationBounce++; }, 200); setTimeout(function() { animationBounce++; }, 300); setTimeout(function() { animationBounce--; }, 400); setTimeout(function() { animationBounce--; }, 500); setTimeout(function() { animationBounce--; }, 600); } // Makes the character jump function charJump() { if (charY == 220) { jump = 4; setTimeout(function() { charY -= jump; }, 20); jump = 8; setTimeout(function() { charY -= jump; }, 40); jump = 12; setTimeout(function() { charY -= jump; }, 60); jump = 16; setTimeout(function() { charY -= jump; }, 80); setTimeout(function() { charY -= jump; }, 100); setTimeout(function() { charY -= jump; }, 120); } } // Detects when the character has a hit a square function hitDetect() { for (var iB = 0; iB < clonePos.length; iB++) { if (clonePos[iB] > -320 && clonePos[iB] < -280 && charY > 200) { return charDied(); } } } // Runs when character dies function charDied() { disDeath = disObst; charDead = 1; charRevive(); testBetterAI(); decideAdjustments(); } // Adds score very interval function addingScore() { if (charDead == 0) { score += 100; } } // Adds to an array that I will use later function addData() { dataCharY.push(charY); dataDisObst.push(disObst); } // Test to see if one of my AI's (which hasn't been made yet) scores is better than the previous best function testBetterAI() { // Commented because does not work in Sandbox // if (score > localStorage.getItem("bestScore")) { // } } // Calculates the distance to the nearest square / obstacle function getDisObst() { lowestLoopDis = 1000; for (var iiA = 0; iiA < clonePos.length; iiA++) { if (clonePos[iiA] > -320) { if (clonePos[iiA] > 0) { if (Math.abs(clonePos[iiA]) < lowestLoopDis) { lowestLoopDis = Math.abs(clonePos[iiA]); } } else { if (Math.abs(clonePos[iiA]) < lowestLoopDis) { lowestLoopDis = Math.abs(clonePos[iiA]); } } } } if (lowestLoopDis < disObst) { disObst = lowestLoopDis; } } // Increments the speed of the obstacles / squares and the character function addSpeed() { if (speed < 25) { speed++; } } // Restarts the game function charRevive() { clonePos = [600]; charDead = 0; score = 0; time = 0; speed = 5; AIgames++; } // I accidently did this twice, whoops function testBetterAI() { if (score > bestScoreAI) { bestScoreAI = score; } } // Makes the unfinished AI jump when it wants to function jumpAI() { if (disObst <= disDeath + jumpFactor) { charJump(); } } // What changes need to be made in order to improve the AI function decideAdjustments() { jumpFactor += Math.floor(Math.random() * 10) - 5; if (jumpFactor < 0) { jumpFactor = 0; } } // Removing blocks that are off the screen function removeUnusedObst() { if (clonePos[0] < -650) { clonePos.shift(); } } // Intervals here setInterval(function() { time++; }, 1000); setInterval(function() { runAll(); }, 10); setInterval(function() { moveObst(); }, 50); setInterval(function() { charGravity(); }, 25); setInterval(function() { createObst(); }, 3000); setInterval(function() { charBounce(); }, 650); setInterval(function() { addingScore(); }, 3500); setInterval(function() { addSpeed(); }, 25000);
#GameScreen { background-color: CornflowerBlue; width: 380px; height: 200px; } #scoreText { text-align: center; font-size: 16px; }
<div id="scoreText"></div> <canvas id="GameScreen" width="1270px" height="550px"></canvas>