2016-11-30 107 views
0

我正在创建一个简单的平台游戏。我正在尝试创建与对象的碰撞并能够检测这些碰撞。通过下面的代码,我无法正确检测碰撞,并在碰撞时阻止玩家移动。应该发生的是代码应该检查是否与level.Objects数组中的任何对象发生冲突。我现在的代码没有检测到碰撞,并且你落入地下无限。我将如何创建一个函数来正确检测碰撞并在碰撞的哪一侧返回true?更好的2D画布碰撞检测

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

回答

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