【问题标题】:How do i Make a sprite change with keyboard input i'm using vanilla JS我如何用键盘输入改变精灵我正在使用香草JS
【发布时间】:2020-09-09 21:56:42
【问题描述】:

所以我一直在测试 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,以便我的角色更改精灵。你能帮助我吗?最好有代码示例!

【问题讨论】:

    标签: javascript html canvas


    【解决方案1】:

    跟踪键盘状态。

    您可以创建一个对象来保存键盘的状态,特别是您感兴趣的按键。使用 "keydown""keyup" KeyboardEvent 在事件触发时更新键盘状态。使用 KeyboardEvent 属性code 来确定哪个键正在改变。不要使用keyCode,因为它已经贬值并且是非标准的

    您还想阻止键的默认行为。例如,防止箭头键滚动页面。这是通过调用event 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>

    【讨论】:

      【解决方案2】:

      点击sn-p帧区域聚焦键盘事件

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-03-30
        • 2016-12-11
        • 1970-01-01
        • 2016-01-21
        • 1970-01-01
        • 2014-06-21
        • 2015-11-23
        相关资源
        最近更新 更多