简体   繁体   中英

How to get a sprite to stop moving while using a setInterval loop in JavaScript

I'm trying to make a game for my friend using sprites i made from a meme. I decided to use an infinite loop to efficiently run my code, but i am not sure how to use the keyup event listener to stop the sprite from moving because when i press on arrow key, the sprite will continue to move forever. How do i fix this? Also, how do i make it so that the sprite moves when i hold down the key, rather than pressing it individually after the main problem is fixed? (Note: the sprite transitions are not an issue, i want to get the movement down first.)

 // setting up basic canvas const cvs = document.getElementById("canvas"); const ctx = cvs.getContext('2d'); // defining images and sources for each one let petscop = new Image(); petscop.src = "https://raw.githubusercontent.com/swedishF1sh/PETSCOP2p/master/petscop-fromside-1.png.png"; let petscop2 = new Image(); petscop2.src = "https://raw.githubusercontent.com/swedishF1sh/PETSCOP2p/master/petscop-fromside-2.png.png"; let petscop3 = new Image(); petscop3.src = "https://raw.githubusercontent.com/swedishF1sh/PETSCOP2p/master/petscop-fromside-3.png.png"; let background1 = new Image(); background1.src = "https://raw.githubusercontent.com/swedishF1sh/PETSCOP2p/master/petscop-background.png" // setting up the direction variable let d; document.addEventListener("keydown", direction); function direction(event) { let key = event.keyCode; if (key == 37) { d = "RIGHT"; } else if (key == 38) { d = "DOWN"; } else if (key == 39) { d = "LEFT"; } else if (key == 40) { d = "UP"; } } // length & width of one box, in half. (regular as 32) let halfbox = 16; // organizing the frames for the current character let currentframe = petscop; let frames = { front: petscop, frontblink: petscop2, back: petscop3 } let petscopsize = { height: petscop.height, width: petscop.width } // setting up the character position let characterpos = { x: halfbox*13, y: halfbox*10, } // setting up the main function which the game will run on. function draw() { currentframe.width = petscop.width; currentframe.height = petscop.height; ctx.drawImage(background1, 0, 0); ctx.drawImage(currentframe, characterpos.x, characterpos.y); if (d == "LEFT") { characterpos.x += halfbox; currentframe = petscop; } else if (d == "UP") { characterpos.y += halfbox; currentframe = petscop; } else if (d == "RIGHT") { characterpos.x -= halfbox; currentframe = petscop; } else if (d == "DOWN") { characterpos.y -= halfbox; currentframe = petscop3; } } setInterval(draw, 50); 
 #canvas { border: 5px; background-color: white; } .canvas-container { margin-left: 25%; } body { background-color: rgb(255, 255, 255); // gray: 40, 68, 68 // white: 255, 255, 255 } 
 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>repl.it</title> <link href="style.css" rel="stylesheet" type="text/css" /> </head> <body> <div class="canvas-container"> <canvas id="canvas" height="512" width="862"></canvas> </div> <script src="script.js"></script> </body> </html> 

I expect the end result to stop moving when i stop holding down the key.

Instead of setInterval(draw, 50); use var loop = setInterval(draw, 50); Then when you want it to stop, use clearInterval(loop);

document.addEventListener("keyup", () => d = null);

放开按钮,应立即禁用移动。

Both answers don't seem to be of much help.

Animation loop

You are correct that an infinite loop is the best way to handle game animation.

However don't use setInverval , use requestAnimationFrame to create the game loop.

You have an interval of 50ms which is 20fps (frames per second). requestAnimationFrame will try to run at 60fps which you can not change. However you can just skip every 2 out of 3 frames to get 20fps.

The snippet shows a basic game loop using requestAnimationFrame with a adjustable frame rate.

const frameRate = 20; // only works for frame rates 60,30,20,15,12,10,6,5,4,3,2,1 per second
var frameCount = 0; // counts requested frames @60fps

requestAnimationFrame(gameLoop); // this will start the game loop
function gameLoop(time) { // time is passed to this function by requestAnimationFrame
    if ((frameCount++) % (60 / frameRate)) {
        draw(); // calls your game code
    }

    // Request the next frame
    requestAnimationFrame(gameLoop);
}

The keyboard

To handle keyboard input you need to listen to both the key down and key up events.

The flowing snippet is a simple keyboard state manager that maintains the state of the keys you are interested in.

You can find key codes at KeyboardEvent.code

const keys = {  // Name of keys code you want to use
    ArrowUp: false,  // set to off at start
    ArrowDown: false,
    ArrowLeft: false,
    ArrowRight: false,
};
// the event listener
function keyEvent(event) {
    if (keys[event.code] !== undefined) { // is this a key we are using?
        keys[event.code] = event.type === "keydown"; // set true if down false if up
        event.preventDefault(); // stops default action (eg scrolling page)
    }
}
// Add the key events to the window object (window is the default object 
// so dont need to name it)
addEventListener("keyup", keyEvent);
addEventListener("keydown", keyEvent);

Now you just change your game code to move the character only when the key is down.

BTW its best to move sprites then draw sprites, rather than draw then move. This reduces the delay between user input and visual feedback.

function draw() {
    ctx.drawImage(background1, 0, 0);
    currentframe = petscop;

    if (keys.ArrowLeft) {
       characterpos.x += halfbox;
    }
    if (keys.ArrowRight) {
       characterpos.x -= halfbox;
    }
    if (keys.ArrowUp) {
       characterpos.y += halfbox;
    }
    if (keys.ArrowDown) {
       characterpos.y -= halfbox;
       currentframe = petscop3;
    }
    ctx.drawImage(currentframe, characterpos.x, characterpos.y);
 }

Example

The snippet puts it all together.

I think you got your key mapping back to front, I left it as your code seemed to imply left moves right, up moves down and so on.

I also made changes to how the images are loaded and made an object that is the character.

Hope it helps and is not to late (I just noticed the question date).

 const frameRate = 20; // only rates 60,30,20,15,12,10,6,5,4,3,2,1 per second var frameCount = 0; const cvs = document.getElementById("canvas"); const ctx = cvs.getContext('2d'); ctx.fillStyle = "#09F"; ctx.textAlign = "center"; ctx.fillText("loading...", cvs.width / 2, cvs.height / 2); const imgLocation = "https://raw.githubusercontent.com/swedishF1sh/PETSCOP2p/master/petscop-"; const images = { forward: "fromside-1.png.png", backward: "fromside-3.png.png", blink: "fromside-2.png.png", background: "background.png", }; function loadImages(imageList, onAllLoaded) { var count = 0; for(const name of Object.keys(imageList)) { const img = new Image; count ++; img.src = imgLocation + imageList[name]; img.onload = () => { imageList[name] = img; img.onload = null; count --; if (count === 0 && onAllLoaded) { onAllLoaded() } } } } // loads images and start main loop when all loaded loadImages(images,() =>requestAnimationFrame(gameLoop)); const keys = { // codes @ https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code ArrowUp: false, ArrowDown: false, ArrowLeft: false, ArrowRight: false, }; function keyEvent(event) { if (keys[event.code] !== undefined) { keys[event.code] = event.type === "keydown"; event.preventDefault(); } } addEventListener("keyup", keyEvent); addEventListener("keydown", keyEvent); focus(); // for SO snippet to get keyboard events without clicking first const halfbox = 16; const blinkOdds = 1/100; // odds of blinking. 1/100 @ 20fps average blink time is 5 seconds const character = { x: halfbox * 13, y: halfbox * 10, image: null, draw() { ctx.drawImage(this.image, this.x, this.y); }, move() { this.image = Math.random() < blinkOdds ? images.blink : images.forward; if (keys.ArrowLeft) { this.x += halfbox; } if (keys.ArrowRight) { this.x -= halfbox; } if (keys.ArrowUp) { this.y += halfbox; } if (keys.ArrowDown) { this.y -= halfbox; this.image = images.backward; } }, } function draw() { ctx.drawImage(images.background, 0, 0); character.move(); character.draw(); } function gameLoop(time) { if ((frameCount++) % (60 / frameRate)) { draw(); } requestAnimationFrame(gameLoop); } 
 #canvas { border: 5px; background-color: white; } .canvas-container { margin-left: 25%; } body { background-color: rgb(255, 255, 255); } 
 <div class="canvas-container"> <canvas id="canvas" height="512" width="862"></canvas> </div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM