简体   繁体   中英

How do i Make a sprite change with keyboard input i'm using vanilla JS

so ive been testing out HTML canvas. im trying to get a sprite to change on keyboard input.

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <canvas id='Game' width='200' height='200' style='border: 2px solid #000000;'></canvas> <script> window.onload = function(){ var Game = document.getElementById('Game'); var context = Game.getContext('2d') var room = new Image(); var lx = 0; var ly = 0; var li = 0; var lo = 0; var lwidth = 100; var lheight = 100; room.onload = function(){ context.drawImage(room,lx,ly,lwidth,lheight,li,lo,200,200); } room.src = 'https://i.ibb.co/D7fL7yN/Room.png'; var sprite = new Image(); var cx = 0; var cy = 125; var sy = 0; var sx = 0; var swidth = 35; var sheight = 34; sprite.onload = function(){ context.drawImage(sprite,sx,sy,swidth,sheight,cx,cy,50,50); } sprite.src = 'https://i.ibb.co/7VhjqPr/John-Sheet.png'; } </script> </body> </html>

ive been searching on how to change the SX with Keyboard input so my character changes sprites. can you help me? a code example would be best!

Tracking keyboard state.

You can create an object that hold the state of the keyboard, specifically the keys you are interested in reacting to. Use the "keydown" and "keyup" KeyboardEvent to update the keyboard state as the events fire. Use the KeyboardEvent property code to workout which key is changing. DO NOT use keyCode as that has depreciated and is Non Standard

You also want to prevent the default behaviour of keys. Eg prevent arrow keys scrolling the page. This is done by calling the event preventDefault function

const keys = {
    ArrowRight: false,
    ArrowLeft: false,
    ArrowUp: false,
    ArrowDown: false,
}
addEventListener("keydown", keyEvent);
addEventListener("keyup", keyEvent);
function keyEvent(event) {
    if (keys[event.code] !== undefined) {
        keys[event.code] = event.type === "keydown";
        event.preventDefault();
    }
}

Then in the game you need only check the keyboard state

if (keys.ArrowRight) { moveRight() }
if (keys.ArrowLeft) { moveLeft() }
// and so on

In the demo below the keys are binded to game actions, meaning that what and how many keys are used are independent of the action. The are also set via configuration, so that key binding can be changed without changing game code. You can also bind other inputs as in example

Animation

To animate you should use the timer function requestAnimationFrame as it is specifically designed to give the best animation results. It will call your rendering function, you can consider the rendering function like a loop, that is call every time you step forward in animation time.

Putting it together

The demo below use the above (modified) methods to get keyboard input and render the scene via animation frame requests.

It also uses some techniques (simple versions of) that help make your game a better product.

  • Encapsulates the player as an object
  • Maintains game state by holding the current rendering function in currentRenderState
  • Has configuration config so all important values are in one place, and could be loaded (from JSON file) to easily change the game without changing code.
  • Has configurable keyboard binding, Note more than one key can be bound to a game action. In example movement is via WASD or arrow keys.
  • All text is configurable (making it language independent)
  • Passes the 2D context to all rendering code.
  • Separates the game from the rendering. This makes it easier to port the game to low end or high end devices or even move it to a server where ctx is replaced with coms and the game can be broadcast . The game does not change only how it is rendered

 var currentRenderState = getFocus; // current game state const config = { player: { start: {x: 100, y:100}, speed: 2, imageURL: "https://i.stack.imgur.com/C7qq2.png?s=64&g=1", }, keys: { // link key code to game action up: ["ArrowUp", "KeyW"], down: ["ArrowDown", "KeyS"], left: ["ArrowLeft", "KeyA"], right: ["ArrowRight", "KeyD"], }, touchableTime: 140, // in ms. Set to 0 or remove to deactivate text: { focus: "Click canvas to get focus", loading: "Just a moment still loading media!", instruct: "Use arrow keys or WASD to move", } }; requestAnimationFrame(mainLoop); // request first frame const ctx = gameCanvas.getContext("2d"); const w = gameCanvas.width, h = gameCanvas.height; const player = { image: (()=> { const img = new Image; img.src = config.player.imageURL; img.addEventListener("load", () => player.size = img.width, {once: true}); return img; })(), x: config.player.start.x, y: config.player.start.y, size: 0, speed: config.player.speed, direction: 0, update() { var oldX = this.x, oldY = this.y; if (actions.left) { this.x -= this.speed } if (actions.right) { this.x += this.speed } if (actions.up) { this.y -= this.speed } if (actions.down) { this.y += this.speed } if (this.x < 0) { this.x = 0 } else if (this.x > w - this.size) { this.x = w - this.size } if (this.y < 0) { this.y = 0 } else if (this.y > h - this.size) { this.y = h - this.size } const mx = this.x - oldX, my = this.y - oldY; if (mx !== 0 || my !== 0) { this.direction = Math.atan2(my, mx) } }, draw(ctx) { if (ctx) { ctx.setTransform(1, 0, 0, 1, this.x + this.size / 2, this.y + this.size / 2); ctx.rotate(this.direction + Math.PI / 2); // rotate 90 deg as image points up ctx.drawImage(this.image,-this.size / 2, -this.size / 2, this.size, this.size); } } } function drawText(ctx, text, size, color) { if (ctx) { ctx.fillStyle = color; ctx.font = size + "px Arial"; ctx.textAlign = "center"; ctx.fillText(text, w / 2, h * (1/4)); } } function getFocus(ctx) { drawText(ctx, config.text.focus, 24, "black"); } function drawScene(ctx) { if (!player.size === 0) { drawText(ctx, config.text.loading, 16, "blue") actions.hasInput = false; // ensure instruction are up when ready } else { if (!actions.hasInput) { drawText(ctx, config.text.instruct, 16, "blue") } player.update(); player.draw(ctx); } } function mainLoop() { ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.clearRect(0, 0, w, h); currentRenderState(ctx); requestAnimationFrame(mainLoop); // request next frame } // keys holds action name for each named key. eg for up action ArrowUp: "up", KeyW: "up", const keys = Object.entries(config.keys) .reduce((keys, [action,codes]) => codes.reduce((keys, code) => (keys[code] = action, keys), keys), {}); // actions are set true when key down. NOTE first up key for action cancels action const actions = Object.keys(config.keys) .reduce((actions,action) => (actions[action] = false, actions),{}); addEventListener("keydown", keyEvent); addEventListener("keyup", keyEvent); function keyEvent(event) { if (keys[event.code] !== undefined) { actions[keys[event.code]] = event.type === "keydown"; event.preventDefault(); actions.hasInput = true; } } if (config.touchableTime) { const actionTimers = {}; touchable.addEventListener("click", (e) => { if (e.target.dataset.action) { actions[e.target.dataset.action] = true; clearTimeout(actionTimers[e.target.dataset.action]); actionTimers[e.target.dataset.action] = setTimeout(() => actions[e.target.dataset.action] = false, config.touchableTime); actions.hasInput=true; if (currentRenderState !== drawScene) { window.focus(); currentRenderState = drawScene; } } }); } else { touchable.classList.add("hide"); } gameCanvas.addEventListener("click", () => currentRenderState = drawScene, {once: true});
 canvas {border: 1px solid black} #game { width:402px; height:182px; font-size: 24px; user-select: none; } .left { position: absolute; top: 160px; left: 10px; cursor: pointer; } .right { position: absolute; top: 160px; left: 355px; cursor: pointer; } #touchable span:hover {color: red} .hide { display: none }
 <div id="game"> <canvas id="gameCanvas" width="400" height="180"></canvas> <div id="touchable"> <div class="left"> <span data-action="up">&#x25B2;</span> <span data-action="down">&#x25BC;</span> </div> <div class="right"> <span data-action="left">&#x25C4;</span> <span data-action="right">&#x25BA;</span> </div> </div> </div>

Click to snippet frame area for focusing keyboard events

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <canvas id='Game' width='200' height='200' style='border: 2px solid #000000;'></canvas> <script> window.onload = function(){ // Keyboard collect const keys = []; document.onkeydown = e => { var code = e.which; if(keys.indexOf(code) < 0){ keys.push(code); } }; document.onkeyup = e => keys.splice(keys.indexOf(e.which),1); // constants const Game = document.getElementById('Game'); const context = Game.getContext('2d') const room = new Image(); const lx = 0; const ly = 0; const li = 0; const lo = 0; const lwidth = 100; const lheight = 100; room.onload = function(){ context.drawImage(room,lx,ly,lwidth,lheight,li,lo,200,200); } room.src = 'https://i.ibb.co/D7fL7yN/Room.png'; const sprite = new Image(); const swidth = 35; const sheight = 34; const sy = 0; sprite.onload = function(){ context.drawImage(sprite,0,sy,swidth,sheight,0,cy,50,50); } sprite.src = 'https://i.ibb.co/7VhjqPr/John-Sheet.png'; // variables let cx = 0; let cy = 125; let sx = 0; // new variables const frames_per_step = 20; let moving = false; // moving flag let step = 0; // frame counter (for steps) // main loop function function tick() { // keyboard process if (keys.length) { keys.forEach(item => { switch(item){ case 68:case 39://D and right arrow cx += 1; // move right // change sprite if (step++ < frames_per_step / 2) { sx = 35; // leg up } else { sx = 70; // leg down if(step > frames_per_step) step = 0; } moving = true; break; case 65:case 37://A and left arrow cx -= 1; // move left // change sprite if (step++ < frames_per_step / 2) { sx = 105; } else { sx = 140; if(step > frames_per_step) step = 0; } moving = true; break; // no sprite mechanics here, just move case 87:case 38://W adn arrow up cy -= 1; break; case 83:case 40://S adn arrow down cy += 1; break; } }); // render context.drawImage(room,lx,ly,lwidth,lheight,li,lo,200,200); context.drawImage(sprite,sx,sy,swidth,sheight,cx,cy,50,50); } else if(moving) { // return sprite to stay position sx = 0; context.drawImage(room,lx,ly,lwidth,lheight,li,lo,200,200); context.drawImage(sprite,sx,sy,swidth,sheight,cx,cy,50,50); moving = false; } // else do nothing requestAnimationFrame(tick); } tick(); } </script> </body> </html>

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