首页 文章

HTML JS Canvas游戏:Tile Collision Bug让玩家传送

提问于
浏览
3

我是游戏开发的初学者,并且一直在努力在一系列瓷砖和一个玩家矩形之间完成碰撞 . 这个游戏具有跳跃和重力 . 首先碰撞工作,但非常笨重 . 有时候当玩家最终在一个瓷砖的顶部和一个小的边缘时,它会立即传送到右侧或左侧(取决于什么边缘/角落)和它的下降 . 当与瓷砖底部碰撞时也会发生这种情况;玩家将立即传送到侧面并进一步向上移动 . 根据我的理解,瓦片碰撞检测器将碰撞与一侧或另一侧混淆,因为当玩家碰到瓦片的边缘时,检测器将其读取为好像它与两者碰撞并决定将玩家置于其他基础上的最高坐标速度(又名speedX和speedY) . 我通过设置speedY = 0每次碰到瓷砖的顶部来解决这个问题,这解决了问题,但另一个问题就出来了 . 现在,如果玩家位于牌块的顶部然后掉落并且很快就会向后移动,则它不会与牌块的一侧发生碰撞,但它会很快再次回到它的顶部 .

我只需要一些关于如何解决这个问题的提示,因为我尝试的一切都会导致另一个问题 . 我听说这是开发基于2D磁贴的游戏中常见的错误 .

这是一个jsfiddle与代码在行动:https://jsfiddle.net/8121u356/

这是我整个代码的显示:

function startGame() {
    gameArea.start();
    actor = new player(32, 32, "green", 32, 32);
}

var mapArray = [
    [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,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],
    [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,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],
    [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,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],
    [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,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,0,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,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,0,0,0,0,0,0,1,0,0,0,0,0,1,1,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,1,1,1,0,0,0,1,1,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]

];

var levelRows = 20;
var levelCols = 20;
 var gameArea = {
     canvas : document.getElementById('canvas'),

     start : function() {
         this.context = this.canvas.getContext("2d");
         document.body.insertBefore(this.canvas, document.body.childNodes[0]);
         requestAnimationFrame(updateGameArea);
         window.addEventListener('keydown', function (e) {
             gameArea.keys = (gameArea.keys || []);
             gameArea.keys[e.keyCode] = true;
         });

         window.addEventListener('keyup', function (e) {
             gameArea.keys = (gameArea.keys || []);
             gameArea.keys[e.keyCode] = false;
         })
     },

     clear : function(){
         this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
     },
     render : function() {
         context = this.canvas.getContext("2d");
         var tileSize = 32;
         for(i=0;i<levelRows;i++){
             for(j=0;j<levelCols;j++){
                 if(mapArray[i][j]==1){
                     context.fillStyle = "gray";
                     context.fillRect(j*tileSize,i*tileSize,tileSize,tileSize);
                 }
             }
         }
     }
 };

function TileCollisionManager(object) {
    let tileSize = 32;
    let baseCol = Math.floor(object.x / tileSize);
    let baseRow = Math.floor(object.y / tileSize);
    let colOverlap = object.x % tileSize;
    let rowOverlap = Math.floor(object.y % tileSize);

    if (object.speedX > 0) { 
        if ((mapArray[baseRow][baseCol + 1] && !mapArray[baseRow][baseCol]) ||
            (mapArray[baseRow + 1][baseCol + 1] && !mapArray[baseRow + 1][baseCol] && rowOverlap)) {
            object.x = baseCol * tileSize;
        }
    }

    if (object.speedX < 0) {
        if ((!mapArray[baseRow][baseCol + 1] && mapArray[baseRow][baseCol]) ||
            (!mapArray[baseRow + 1][baseCol + 1] && mapArray[baseRow + 1][baseCol] && rowOverlap)) {
            object.x = (baseCol + 1) * tileSize;
        }
    }

    if (object.speedY > 0) { 
        if ((mapArray[baseRow + 1][baseCol] && !mapArray[baseRow][baseCol]) || 
            (mapArray[baseRow + 1][baseCol + 1] && !mapArray[baseRow][baseCol + 1] && colOverlap)) {
            object.y = ((baseRow) * tileSize);
            object.jumping = false;
            object.speedY = 0;
        }
    }

    if (object.speedY < 0) { 
        if ((!mapArray[baseRow + 1][baseCol] && mapArray[baseRow][baseCol]) ||
            (!mapArray[baseRow + 1][baseCol + 1] && mapArray[baseRow][baseCol + 1] && colOverlap)) {
            object.y = (baseRow + 1) * tileSize;
            object.speedY = 5;
        }
    }
 }
  function updateGameArea() {
      gameArea.clear();
      gameArea.render();
      actor.update();
      actor.newPos();
      actor.speedX = 0;
      actor.speedY += actor.gravity;


      if (gameArea.keys && gameArea.keys[39]) {
          actor.speedX = 4;
      }
      if (gameArea.keys && gameArea.keys[37]) {
          actor.speedX = -4;
      }
      if (gameArea.keys && gameArea.keys[32]) { 
          if (!actor.jumping) {
              actor.jumping = true;
              actor.speedY = -actor.speed * 3;
          }
      }


      TileCollisionManager(actor);
      requestAnimationFrame(updateGameArea);
  }


  function player (width, height, color, x, y) { 
      this.width = width;
      this.height = height;
      this.x = x;
      this.y = y;
      this.speedX=0;
      this.speedY=0;
      this.gravity=0.3;
      this.speed=3;
      this.jumping=false;
      this.color = color;
      this.update = function () {
          ctx = gameArea.context;
          ctx.fillStyle = this.color;
          ctx.fillRect(
              this.x,
              this.y,
              this.width, this.height);
      };
      this.newPos = function () {
          this.x += this.speedX;
          this.y += this.speedY;
      };

1 回答

  • 2

    快速解决方案 .

    我见过你第三次发布这个问题 . 您没有得到答案,因为最好的解决方案是相当多的代码,复杂,并且需要对代码进行大量更改 .

    所以我所做的就是创建一个非常快速和简单的解决方案 .

    以正确的顺序解决冲突 .

    我没有检查移动结束时的位置,而是更改了代码以检查移动的每个像素 . 这是必需的,因为当玩家从一个位置移动到下一个位置时,你必须以正确的顺序找到碰撞 . 如果你撞到顶部或底部之前的一侧墙壁,或者绕过它的另一边有所不同,那就是导致你遇到问题的原因 . 你先检查x然后检查y,这在很多情况下是错误的 .

    我还向一个名为 canMove 的actor添加了一个对象 . 它有4个属性,在每个帧的开头设置,用于防止玩家向被阻挡的方向移动 . 如果你让玩家沿着一个被阻挡的方向移动,那么当你将钥匙向下朝这个方向移动时,它将被卡在墙上 .

    我入侵了你的代码

    对不起,我有点乱,但准时很短 .

    另外为了帮助我编写我做了一些其他mod的更改,我缩放了该游戏以适应窗口(缩放和调整大小都在 clear 函数中完成) . 我更改了键盘界面,以防止按下按键的默认设置,并设置箭头跳跃以及空间(我讨厌使用空格跳转:P) . 还要更改 Map 以使用字符串,因为在更改中输入数组是一种痛苦 .

    我不确定你是怎么想让演员在击中头部时做出反应的 . 我这样做是为了让它以与向上移动相同的速度反弹,但它确实使得它更难以跳跃并滑入狭窄的通道 .

    所以我认为我完成了大部分工作,所以你可以继续你的游戏 .

    如果您有任何疑问,请在评论中提问 .

    // NOTE  var | 0 is the same as Math.floor(var)
    
    var mapArray = [
            "#                  #",
            "#                  #",
            "#  ###             #",
            "#           #      #",
            "#       ##  #####  #",
            "#                  #",
            "#                  #",
            "#    ##            #",
            "#      ##          #",
            "#                  #",
            "#      #####       #",
            "#                  #",
            "#                  #",
            "#       #####      #",
            "#                  #",
            "#                  #",
            "#       #     ##   #",
            "#      ###         #",
            "#     ##### ##     #",
            "####################",
        ].map(row => row.split("").map(cell=>cell==="#" ? 1 : 0));
    
    
        var levelRows = 20;
        var levelCols = 20;
        var tileX = 32;
        var tileY = 32;
        var gameArea = {
         canvas : document.getElementById('canvas'),
         ctx : document.getElementById('canvas').getContext("2d"),
         keys : {  // set them here so that can block defaults
            "37" : false,
            "38" : false,     // also jump
            "39" : false,
            "32" : false,     // jump
         },
    
         start : function() {
             document.body.insertBefore(this.canvas, document.body.childNodes[0]);
             requestAnimationFrame(updateGameArea);
             function keyEvent(e) { 
                 if(gameArea.keys["" + e.keyCode] !== undefined){
                  gameArea.keys["" + e.keyCode] = e.type === "keydown" 
                  e.preventDefault();
                 }
             }
             addEventListener('keydown', keyEvent);
             addEventListener('keyup', keyEvent);
             focus();
            },
    
            clear(){
                var minSize = Math.min(innerWidth,innerHeight);
                if (this.ctx.canvas.width !== minSize|| this.ctx.canvas.height !== minSize) {
                    this.ctx.canvas.width = minSize;
                    this.ctx.canvas.height = minSize;
                }
                    
                this.ctx.setTransform(1,0,0,1,0,0);
                this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
                
                // the next line scales the canvas rendering to fit.
                this.ctx.setTransform(
                    minSize / (levelCols * tileX),
                    0,
                    0,
                    minSize/ (levelRows * tileY),
                    0,0
                );
            },
            render() {
                var ctx = this.ctx;
                for(i=0;i<levelRows;i++){
                    for(j=0;j<levelCols;j++){
                        if(mapArray[i][j]==1){
                            ctx.fillStyle = "gray";
                            ctx.fillRect(j*tileX,i*tileY,tileX,tileY);
                        }
                    }
                }
            }
        };
    
        function updateGameArea() { 
            gameArea.clear();
            actor.canMove.check();
            actor.speedX = 0;
            if(actor.canMove.down){
                actor.speedY += actor.gravity;
            }
    
            if (gameArea.keys[39] && actor.canMove.right) {
                actor.speedX = 4;
            }
            if (gameArea.keys[37]  && actor.canMove.left) {
                actor.speedX = -4;
            }
            if (actor.canMove.up && (gameArea.keys[32] || gameArea.keys[38])) { //jump
                if (!actor.jumping) {
                    actor.jumping = true;
                    actor.speedY = -actor.speed * 3;
                }
            }
            actor.move();  // collision is done here
            
            gameArea.render();
            actor.draw();
    
            requestAnimationFrame(updateGameArea);
        }
    
        function Player (width, height, color, x, y) { //player component
            this.width = width;
            this.height = height;
            this.x = x;
            this.y = y;
            this.speedX=0;
            this.speedY=0;
            this.gravity=0.3;
            this.speed=3;
            this.jumping=false;
            this.color = color;
            this.canMove = {
               left : true,
               right : true,
               up : true,
               down : true,
               actor : this,
               clear(){
                  this.left = true;
                  this.right = true;
                  this.up = true;
                  this.down = true;
               },
               check(){
                  this.clear();
                  var x = this.actor.x | 0;
                  var y = this.actor.y | 0;
                  var cx = x / tileX | 0;
                  var cy = y / tileY | 0;
                  if(x % tileX === 0){
                     if(getMap(cx-1,cy) === 1){                 
                       this.left = false;
                       if(y % tileY !== 0 && getMap(cx-1,cy +1) === 1){
                          this.left = false;
                       }
                     }
                     if(getMap(cx+1,cy) === 1){
                       this.right = false;
                       if(y % tileY !== 0 && getMap(cx+1,cy +1) === 1){
                          this.right = false;
                       }
                     }
                  }
                  if(y % tileY === 0){
                     if(getMap(cx,cy-1) === 1){                 
                       this.up = false;
                       if(x % tileX !== 0 && getMap(cx+1,cy -1) === 1){
                          this.up = false;
                       }
                     }
                     if(getMap(cx,cy+1) === 1){
                       this.down = false;
                       if(x % tileX !== 0 && getMap(cx+1,cy +1) === 1){
                          this.down = false;
                       }
                     }
                  }
               }
            };
    
            this.draw = function () {
                var ctx = gameArea.ctx;
                ctx.fillStyle = this.color;
                ctx.fillRect(  this.x,this.y, this.width, this.height);
            };
            this.move = function() {
              var x = this.x;
              var y = this.y;
              var sx = this.speedX;
              var sy = this.speedY;
              var speed = Math.sqrt(sx * sx + sy * sy);
              if(speed > 0){
                sx /= speed;
                sy /= speed;
                for(var i = 0; i < speed; i++){
                  var xx = (x + sx * i) | 0;
                  var yy = (y + sy * i) | 0;
                  var cx = xx / tileX | 0;
                  var cy = yy / tileY | 0;
                  if(sy > 0){
                    if(getMap(cx,cy+1) === 1 || (xx % tileX !== 0 && getMap(cx + 1,cy+1))){
                      this.y = y = cy * tileY;
                      this.speedY = sy = 0;
                      this.jumping = false;
                    }
                  }else if(sy < 0){
                    if(getMap(cx,cy) === 1 || (xx % tileX !== 0 && getMap(cx + 1,cy))){
                      cy += 1;
                      this.y = y = cy * tileY;
                      this.speedY = sy = -sy;  // changing -sy to 0 will stick momentarily to the roof.
                    }
                  }
                  if(sx > 0){
                    if(getMap(cx+1,cy) === 1 ||  (yy % tileY !== 0 && getMap(cx + 1,cy+1))){
                      this.x = x = cx * tileX;
                      this.speedX = sx = 0;
                    }
    
                  }else if(sx < 0){
                    if(getMap(cx,cy) === 1 || (yy % tileY !== 0 && getMap(cx,cy+1))){
                      cx += 1;
                      this.x = x = cx * tileX;
                      this.speedX = sx = 0;
                    }          
                  }
                }
              }
              this.x += this.speedX;
              this.y += this.speedY;
            }
        }
    
        function getMap(x,y){
          if(y < 0 || y >= levelRows || x < 0 || x >= levelCols){
             return 1;
          }
          return mapArray[y][x];
        }
     
    
        gameArea.start();
        actor = new Player(32, 32, "green", 32, 32);
    
    canvas { 
       position : absolute;
       top : 0px;
       left : 0px;
    }
    
    <canvas id = "canvas" width="640" height="640"></canvas>
    

相关问题