更好的2D画布碰撞检测

时间:2016-11-30 17:40:45

标签: javascript canvas 2d

我正在创建一个简单的平台游戏。我试图创建与对象的碰撞,并能够检测到这些。使用下面的代码,我无法正确检测到碰撞并阻止玩家在碰撞时移动。应该发生的是代码应该检查是否与level.Objects数组中的任何对象发生冲突。我现在的代码没有检测到碰撞而你无法进入地面。我如何创建一个能够正确检测碰撞的函数,并在它碰撞的哪一边返回true?



function runGame() {
  var game = document.getElementById('game')
  var ctx = game.getContext("2d")
  var DonaldRest = document.getElementById('DonaldRest')
  var GrassTile = document.getElementById('GrassTile')
  var gravity = 0.5
  var momentum = 0;
  var momentumDown = 0;
  var spacing = 64;
  var speed = 2;
  var maxSpeed = 2;
  var jumpHeight = 3;
  var levels = [{
    Name: "Level 1",
    Objects: [{
      Type: "GrassFloor",
      Location: {
        x: 0,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 1,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 2,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 3,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 4,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 5,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 6,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 7,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 8,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 9,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 10,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 11,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 12,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 13,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 14,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 15,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 16,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 17,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 18,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 19,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 20,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 21,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 22,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 23,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 24,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 25,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 26,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 27,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 28,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, {
      Type: "GrassFloor",
      Location: {
        x: spacing * 29,
        y: 0
      },
      Scale: {
        x: 1,
        y: 1
      },
      Solid: true,
      Height: 3
    }, ]
  }]
  var player = {
    position: {
      x: 0,
      y: 0
    },
    Time: 0
  }
  ctx.canvas.width = window.innerWidth;
  ctx.canvas.height = window.innerHeight;
  var game = setInterval(function() {
    ctx.imageSmoothingEnabled = false
    ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
    ctx.fillStyle = "#adfffa"
    ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height)
    ctx.drawImage(DonaldRest, ctx.canvas.width / 2 - (96 / 2), ctx.canvas.height / 2 - (96 / 2), 96, 96)
    var Level = levels[0]
    var Objects = Level.Objects
    var OnGround = checkCollisions().Bottom
    if (OnGround == false) {
      if (momentumDown <= maxSpeed) {
        momentumDown -= gravity;
        player.position.y += momentumDown;
      } else {
        player.position.y += momentumDown;
      }
    } else {
      momentumDown = 0;
      console.log("collided")
    }
    for (var j = 0; j < Objects.length; j++) {
      if (Objects[j].Type == "GrassFloor") {
        ctx.drawImage(GrassTile, Objects[j].Location.x - player.position.x, (ctx.canvas.height - spacing + player.position.y) - (spacing * Objects[j].Height), spacing, spacing)
        for (var i = -5; i < Objects[j].Height; i++) {
          ctx.drawImage(DirtTile, Objects[j].Location.x - player.position.x, (ctx.canvas.height - spacing) - (i * spacing) + player.position.y, spacing, spacing)
        }
      }
    }
  }, 17); //17

  $(document).keydown(function(e) {
    if (e.which == 32) {
      if (checkCollisions().Bottom == true) {
        console.log(momentumDown);
        momentumDown -= jumpHeight
        console.log(momentumDown);
      }
    }
  })

  function isTouchingFloor(e1, e2) {
    return e1.x < (e2.x + e2.w) && (e1.x + e1.w) > e2.x && e1.y - momentumDown < (e2.y + e2.h) && (e1.y - momentumDown + e1.h) > e2.y;
  }

  function checkCollisions() {
    var Objects = levels[0].Objects;
    var Collision = {
      Top: false,
      Left: false,
      Bottom: false,
      Right: false
    }
    var GrassTileImg = new Image()
    var o1 = {
      y: player.position.y,
      h: 96,
      x: player.position.x,
      w: 96
    }
    for (var i = 0; i < Objects.length; i++) {
      var o2 = {
        y: Objects[i].Location.y,
        x: Objects[i].Location.x,
        h: 64,
        w: 64
      }
      if (isTouchingFloor(o1, o2) == true) {
        Collision.Bottom == true;
      }
      console.log(Collision.Bottom)
    }
    return Collision
  }
}
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:0)

这是我在执行用于检查冲突的组项目时使用的代码片段。基本上我们创建一个函数来检查两个对象和相应的一面之间的碰撞。

碰撞功能

/**
 * Checks for a collision of two objects. Moves objectA if side is a string with the word move.
 * @param objectA The object that needs to move.
 * @param objectB The object that needs to block.
 * @param side If true, makes return a string. If "move", moves objectA.
 * @returns {*} String if side evaluates to true, otherwise boolean.
 */
function checkCollision(objectA, objectB, side) {
  if (side) {
    var vx = objectA.centerX() - objectB.centerX(),
      vy = objectA.centerY() - objectB.centerY(),

      combinedHalfWidths = objectA.halfWidth() + objectB.halfWidth(),
      combinedHalfHeights = objectA.halfHeight() + objectB.halfHeight(),

      collisionSide = "";

    if (Math.abs(vx) < combinedHalfWidths && Math.abs(vy) < combinedHalfHeights) {
      var overlapX = combinedHalfWidths - Math.abs(vx),
        overlapY = combinedHalfHeights - Math.abs(vy);

      if (overlapX > overlapY) {
        if (vy > 0) {
          if (side === "move") {
            objectA.vy = objectB.vy;
            objectA.y += overlapY;
          }

          collisionSide = "top";
        } else {
          if (side === "move") {
            objectA.vy = objectB.vy;
            objectA.y -= overlapY;
          }

          collisionSide = "bottom";
        }
      } else {
        if (vx > 0) {
          if (side === "move") {
            objectA.vx = objectB.vx;
            objectA.x += overlapX;
          }

          collisionSide = "left";
        } else {
          if (side === "move") {
            objectA.vx = objectB.vx;
            objectA.x -= overlapX;
          }

          collisionSide = "right";
        }
      }
    }
    return collisionSide;
  } else {
    return !(objectA.x + objectA.width < objectB.x ||
      objectB.x + objectB.width < objectA.x ||
      objectA.y + objectA.height < objectB.y ||
      objectB.y + objectB.height < objectA.y);
  }
}

此函数检查我们在级别中加载的所有对象。

碰撞检查

function doCollisionChecks() {
  var checkPush = false;
  player.isOnGround = false;
  for (var i = 0; i < solids.length; i++) {
    if (checkCollision(player, solids[i], "move") === "bottom") {
      player.isOnGround = true;
      player.state = player.STANDING;
      if (solids[i].vx) {
        if (solids[i].vx !== player.extraVX) {
          player.extraVX = solids[i].vx
        }
      } else {
        player.extraVX = 0;
      }
    }
  }
  for (i = 0; i < objects.length; i++) {
    if (checkCollision(objects[i], player, true) === "right" || checkCollision(objects[i], player, true) === "left") {
      player.speedLimit = scaleWidth(2);
      objects[i].speedLimit = scaleWidth(2);
      checkPush = true;
    }

    //Letting the player move boxes, while avoiding a "box hop" bug.
    if (checkCollision(objects[i], player, true) === "top") {
      player.y = objects[i].y - player.height;
    } else if (checkCollision(objects[i], player, true) === "bottom") {
      player.y = objects[i].y + objects[i].height;
    } else if (player.centerY() > objects[i].y + objects[i].height * 0.1 && player.centerY() < objects[i].y + objects[i].height * 0.9) {
      checkCollision(objects[i], player, "move");
    }

    for (var j = 0; j < solids.length; j++) {
      if (checkCollision(objects[i], solids[j], "move") === "bottom" && solids[j].vx) {
        objects[i].extraVX = solids[j].vx;
      } else {
        objects[i].extraVX = 0;
      }
      checkCollision(objects[i], solids[j], "move");
    }

    for (j = 0; j < objects.length; j++) {
      if (j !== i) {
        //Avoids boxes falling through one another.
        if (checkCollision(objects[i], objects[j], true) === "top") {
          checkCollision(objects[j], objects[i], "move");
        } else {
          checkCollision(objects[i], objects[j], "move");
        }
      }
    }

    if (checkCollision(player, objects[i], true) === "bottom") {
      player.isOnGround = true;
      player.state = player.STANDING;
      player.extraVX = objects[i].extraVX;
    } else if (checkCollision(player, objects[i], true) === "top") {
      score -= 50;
      gameState = OVER;
    }
    checkCollision(player, objects[i], "move");

    if (objects[i].y > c.height) {
      if (objects[i].correct) {
        score += 100;
        objects.splice(i, 1);
        checkWin();
      } else {
        fRed = 0;
        score -= 100;
        objects.splice(i, 1);
      }
    }
  }

  for (i = 0; i < enemies.length; i++) {
    if (checkCollision(enemies[i], player)) {
      score -= 50;
      gameState = OVER;
    }
    j = 0;
    while (enemies[i] && j < objects.length) {
      if (checkCollision(objects[j], enemies[i], true) === "bottom") {
        score += 75;
        objects[j].vy = -1 * scaleHeight(6);
        enemies.splice(i, 1);
        //score++
      }
      j++;
    }
  }
  if (checkCollision(player, powerUp)) {
    score += 25;
    activatePowerUp();
  }
  if (!checkPush) {
    player.speedLimit = scaleWidth(3);
    for (i = 0; i < objects.length; i++) {
      objects[i].speedLimit = scaleWidth(3);
    }
  }
}

很抱歉,但是使用了很多不相关的属性,例如速度限制等。但是它可以正常工作。

您可以找到整个来源here