簡體   English   中英

我如何使用鍵盤輸入進行精靈更改我使用的是 vanilla JS

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

所以我一直在測試 HTML 畫布。 我試圖讓一個精靈改變鍵盤輸入。

 <!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>

我一直在尋找如何使用鍵盤輸入更改 SX,以便我的角色更改精靈。 你能幫助我嗎? 最好有代碼示例!

跟蹤鍵盤狀態。

您可以創建一個保存鍵盤狀態的對象,特別是您有興趣對其做出反應的鍵。 使用"keydown""keyup" KeyboardEvent在事件觸發時更新鍵盤狀態。 使用 KeyboardEvent 屬性代碼來測試哪個鍵正在改變。 請勿使用已貶值且非標准的keyCode

您還想防止鍵的默認行為。 例如,防止箭頭鍵滾動頁面。 這是通過調用事件preventDefault函數來完成的

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();
    }
}

然后在游戲中你只需要檢查鍵盤狀態

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

在下面的演示中,鍵綁定到游戲動作,這意味着使用什么鍵和多少鍵與動作無關。 這些也是通過配置設置的,因此可以在不更改游戲代碼的情況下更改鍵綁定。 您還可以綁定其他輸入,例如

動畫片

要制作動畫,您應該使用計時器函數requestAnimationFrame,因為它是專門為提供最佳動畫效果而設計的。 它將調用您的渲染函數,您可以將渲染函數視為循環,即每次在動畫時間前進時調用。

把它放在一起

下面的演示使用上述(修改后的)方法來獲取鍵盤輸入並通過動畫幀請求渲染場景。

它還使用了一些技術(簡單版本)來幫助您的游戲成為更好的產品。

  • player封裝為一個對象
  • 通過在currentRenderState保持當前渲染函數來維護游戲狀態
  • 具有配置config因此所有重要值都在一個地方,並且可以加載(從 JSON 文件)以輕松更改游戲而無需更改代碼。
  • 具有可配置的鍵盤綁定,注意一個游戲動作可以綁定多個鍵。 在示例中,移動是通過 WASD 或箭頭鍵進行的。
  • 所有文本都是可配置的(使其與語言無關)
  • 將 2D 上下文傳遞給所有渲染代碼。
  • 將游戲與渲染分開。 這使得更容易將游戲移植到低端或高端設備,甚至將其移動到 ctx 被 coms 替換並且游戲可以廣播的服務器。 游戲不僅僅改變它的渲染方式

 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>

單擊以提取用於聚焦鍵盤事件的框架區域

 <!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>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM