# 如何在Canvas动画的While循环的每次迭代之间添加延迟？

See here for a live review: https://codepen.io/SkylerSpark/pen/GRpdzBZ

``````case "ArrowLeft":
if (map[playerCoords[1]][playerCoords[0] - 1] != 1) {
while (map[playerCoords[1]][playerCoords[0] - 1] != 1) {
playerCoords[0]--;
}
}
``````

``````map[playerCoords[1]][playerCoords[0] - 1] != 1

map[] - Main Map Data (1s and 0s that determine the game layout)
playerCoords[0 / 1] - Location of the Player

map[ pc[1] ] (going to get the sub array of map[playerCoords[1]]) > [pc[0] - 1] (-1 to look for the block to the left of the player)

then Im selecting all of that into one statement and detecting if its equal to 1 (1 is a brick block) to see if the player should MOVE or NOT MOVE.
``````

``````const cvs = document.querySelector(".bastione"),
ctx = cvs.getContext("2d");

const cvs2 = document.querySelector(".basPlayer"),
ctx2 = cvs2.getContext("2d");

ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.webkitImageSmoothingEnabled = false;
ctx2.imageSmoothingEnabled = ctx2.mozImageSmoothingEnabled = ctx2.webkitImageSmoothingEnabled = false;

var img = new Image();
img.setAttribute("crossorigin", "anonymous");
img.src = src;
return img;
}

function ran(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}

const map = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
];

const drawEnvironment = {
init: () => {
drawEnvironment.renderBack();
},
renderBack: () => {
let cx = 0, cy = 0;
map.forEach(e => {
for (var i = 0; i < e.length; i++) {
if (e[i] == 1) {
let v = ran(0, 10);
if (v > 0 && v < 8) {
ctx.drawImage(spriteImage, 0, 0, 32, 32, cx, cy, 32, 32);
} else {
ctx.drawImage(spriteImage, 32, 0, 32, 32, cx, cy, 32, 32);
}
cx += 32;
} else if (e[i] == 2 || e[i] == 3) {
ctx.drawImage(spriteImage, 64, 64, 32, 32, cx, cy, 32, 32);
cx += 32;
} else {
let v = ran(0, 10);
if (v > 0 && v < 5) {
ctx.drawImage(spriteImage, 128, 64, 32, 32, cx, cy, 32, 32);
if (v == 10) {
ctx.drawImage(spriteImage, 128, 32, 32, 32, cx, cy, 32, 32);
}
} else {
ctx.drawImage(spriteImage, 128, 32, 32, 32, cx, cy, 32, 32);
}
cx += 32;
}
}
cx = 0;
cy += 32;
});
ctx.drawImage(spriteImage, 0, 0, 32, 32, 0, 0, 32, 32);
}
};

let playerCoords = [1, 1];

const drawPlayer = {
init: () => {
drawPlayer.playerLoc();
drawPlayer.playerMove();
},
playerLoc: () => {
ctx2.drawImage(spriteImage, 0, 64, 32, 32, playerCoords[0] * 32, playerCoords[1] * 32, 32, 32);
window.requestAnimationFrame(drawPlayer.playerLoc);
},
playerMove: () => {
ctx2.clearRect(0, 0, cvs2.width, cvs2.height);
event.preventDefault();
const key = event.key;
switch (key) {
case "ArrowLeft":
if (map[playerCoords[1]][playerCoords[0] - 1] != 1) {
while (map[playerCoords[1]][playerCoords[0] - 1] != 1) {
playerCoords[0]--;
}
}
break;
case "ArrowRight":
if (map[playerCoords[1]][playerCoords[0] + 1] != 1) {
while (map[playerCoords[1]][playerCoords[0] + 1] != 1) {
playerCoords[0]++;
}
}
break;
case "ArrowUp":
if (map[playerCoords[1] - 1][playerCoords[0]] != 1) {
while (map[playerCoords[1] - 1][playerCoords[0]] != 1) {
playerCoords[1]--;
}
}
break;
case "ArrowDown":
if (map[playerCoords[1] + 1][playerCoords[0]] != 1) {
while (map[playerCoords[1] + 1][playerCoords[0]] != 1) {
playerCoords[1]++;
}
}
break;
}
});
}
}

"https://cdn.jsdelivr.net/gh/FunctFlow/Bastione-Game@1d0514c968a737061916ae5e160b20eaf3a6b8b4/Sprites/Bastione_Sprites.png",
() => {
drawEnvironment.init();
drawPlayer.init();
}
);``````
``````* {
box-sizing: border-box;
overflow: hidden;
}

body {
text-align: center;
background: black;
}

canvas {
display: inline-block;
}

.basPlayer {
position: absolute;
margin-left: -1024px;
}``````
``````<canvas class=bastione width=1024 height=512></canvas>
<canvas class=basPlayer width=1024 height=512></canvas>``````

• overo 回复

这是大话题

通常要做的是为每件事（玩家，怪物等）赋予它们某种更新功能

``````const allTheThings = [];

function loop() {
for (const thing of things) {
thing.update();
}
requestAnimationFrame(loop);
}
``````

In each of those `update` functions you would do whatever is appropriate for that thing doing only what is need at this moment. So for example the player might have a update function like this

``````class Player() {
constructor() {
this.coords = [1, 1];
this.delta = [0, 0];
}
update() {
if (this.waiting) {
this.waiting -= 1;
} else if (this.moving) {
this.coords[0] = this.delta[0];
this.coords[1] = this.delta[1];
this.waiting = 10;
} else {
// check the keys
// and set this.delta and moving appropriately
}
}
}
``````

Then you can make a player and add it to this array of `allTheThings`

``````const player = new Player();
allTheThings.push(player);
``````

Note that is way over simplified. Most games don't directly update in an object like that. Instead, just like `allTheThings` calls update for each thing, a thing itself might have a list of subthings (components) each of which also has an update function. A thing, or a GameThing, is a collection of these components.

Further, there are all kinds of ways to help organize what those update functions do. In the example above there were 2 flags `moving` and `waiting` but as the game gets more and more complicated there get to be too many flags so people have come up with things like Finite State Machines and Coroutines and may other techniques to help make that stuff simpler.

Maybe not useful but here is an article that uses some of these techniques.

• xiste 回复

You can use `setInterval` to create a loop which runs at a specific frequency.

``````case "ArrowLeft":
if (map[playerCoords[1]][playerCoords[0] - 1] != 1) {
let interval = setInterval(() => {
if (map[playerCoords[1]][playerCoords[0] - 1] != 1) {
// Stop the interval, and do nothing.
clearInterval(interval)
return
}
playerCoords[0]--;
}, 100) // <- 100 here is delay in milliseconds
}
``````

This code is now running `asynchronously`. This means that while we are waiting for the delay, other code can run. If we are not careful, this can introduce bugs. For example: You need to think about what would happen if the player presses arrowRight while the arrowLeft is still being processed. If you do not fix that case, the character will move both left and right at the same time, which would mean that he would never hit a wall and you would be stuck in an endless loop.