我在使用 EaselJS 与我的碰撞检测系统进行小游戏开发时遇到问题,我需要别人的帮助。当英雄(圆形位图)碰撞一个物体并且第一个物体后面有其他物体时,即使第二次碰撞被阻挡,英雄也会与两个物体发生碰撞。这是一个图像说明:
问题的原因非常简单,即使问题本身不是:
此碰撞检测系统基于圆的未来位置(而不是其实际位置),然后如果圆的下一个位置与矩形相交,则它将反弹。问题在于,如果未来位置与两个矩形相交,则圆圈将在两个矩形中反弹 - 即使实际的圆周运动被另一个矩形阻挡,也无法到达第二个矩形。
更新:请注意,只有在由于矩形创建顺序而保持向上箭头时才会出现此问题。
以下是相关的javascript代码:
rects.forEach(function (rect) { // Affect all rects
// Collision detection:
// (This MUST BE after every change in xvel/yvel)
// Next circle position calculation:
var nextposx = circle.x + event.delta / 1000 * xvel * 20,
nextposy = circle.y + event.delta / 1000 * yvel * 20;
// Collision between objects (Rect and Circle):
if (nextposy + height(circle) > rect.y &&
nextposx + width(circle) > rect.x &&
nextposx < rect.x + rect.width &&
nextposy < rect.y + rect.height) {
if (circle.y + height(circle) < rect.y) {
cls("top");
}
if (circle.x + width(circle) < rect.x) {
cls("left");
}
if (circle.x > rect.x + rect.width) {
cls("right");
}
if (circle.y > rect.y + rect.height) {
cls("bottom");
}
}
// Stage collision:
if (nextposy < 0) { // Collided with TOP of stage. Trust me.
cls("bottom"); // Inverted collision side is proposital!
}
if (nextposx < 0) {
cls("right");
}
if (nextposx + width(circle) > stage.canvas.width) {
cls("left");
}
if (nextposy + height(circle) > stage.canvas.height) {
cls("top");
}
});
答案 0 :(得分:2)
您必须独立处理水平和垂直碰撞。
我对你的JS小提琴做了一些小改动:http://jsfiddle.net/Kf6cv/1/ 它现在应该工作了,我做的是将你的一张支票分成两部分:
if (nextposy + height(circle) > rect.y &&
circle.x + width(circle) > rect.x &&
circle.x < rect.x + rect.width &&
nextposy < rect.y + rect.height) {
if (circle.y + height(circle) < rect.y) {
cls("top");
}
if (circle.y > rect.y + rect.height) {
cls("bottom");
}
}
if (nextposx + width(circle) > rect.x &&
nextposx < rect.x + rect.width &&
circle.y + height(circle) > rect.y &&
circle.y < rect.y + rect.height) {
if (circle.x + width(circle) < rect.x) {
cls("left");
}
if (circle.x > rect.x + rect.width) {
cls("right");
}
}
这样做的原因是,如果同时检查两个方向,它将阻止两个方向的移动(或使其反弹)(如图中的红色图形) - 即使它可能移动到一个方向。检查水平/垂直的顺序通常无关紧要,通常只有当你的“英雄”100%边对边接近另一个对象时才有意义。但你可以先用更高的速度检查方向,所以如果| velX | &GT; | velY |然后你首先检查水平碰撞。
另外我会说在检查后直接申请新职位是明智的,现在它会进行两次独立检查然后应用两个方向的运动 - 我不确定它但我可以想象这个可能会在以后导致一些问题。
答案 1 :(得分:2)
我找到了这个问题的解决方案,而不会影响边对边的碰撞!
首先,我将forEach
循环分成两个forEach
s:第一个处理左右碰撞,第二个处理边缘到边缘的碰撞,因为这样的方式 - 首先将副反应应用于所有对象,然后检查剩余的边对边碰撞。
然后我改变了碰撞检测的核心方式,使用当前帧和最后帧位置之间的比较(而不是使用估计的当前和未来)。
以下是最终相关代码:
function colhndlr(obj1, obj2array){
var sidecollided = false;
// Side-to-side collision handler:
obj2array.forEach(function(obj2){
// Top side:
if(obj1.ypast + obj1.height < obj2.y &&
obj1.y + obj1.height >= obj2.y &&
// Checks if past position X was correct:
obj1.xpast + obj1.width >= obj2.x &&
obj1.xpast <= obj2.x + obj2.width){
// Collided with top
}
// Left side:
if(obj2.clsdir > 2 && // Checks if object is side-collideable
obj1.xpast + obj1.width < obj2.x &&
obj1.x + obj1.width >= obj2.x &&
// Checks if past position Y was correct:
obj1.ypast + obj1.height >= obj2.y &&
obj1.ypast <= obj2.y + obj2.height){
// Collided with left
}
// Right side:
if(obj2.clsdir > 2 && // Checks if object is side-collideable
obj1.xpast > obj2.x + obj2.width &&
obj1.x <= obj2.x + obj2.width &&
// Checks if past position Y was correct:
obj1.ypast + obj1.height >= obj2.y &&
obj1.ypast <= obj2.y + obj2.height){
// Collided with right
}
// Bottom side:
if(obj2.clsdir == 4 && // Checks if object is bottom-collideable
obj1.ypast > obj2.y + obj2.height &&
obj1.y <= obj2.y + obj2.height &&
// Checks if past position X was correct:
obj1.xpast + obj1.width >= obj2.x &&
obj1.xpast <= obj2.x + obj2.width){
// Collided with bottom
}
});
/*
Now that every side-to-side collision change was made,
the edge-to-edge collision handler will not detect
blocked collisions.
*/
if(!sidecollided){
// Edge-to-edge collision handler:
obj2array.forEach(function(obj2){
if(obj2.clsdir != 1){ // If it's 1 then there's no need for edge collisions
// Top left edge:
if(obj1.ypast + obj1.height < obj2.y &&
obj1.xpast + obj1.width < obj2.x &&
obj1.y + obj1.height >= obj2.y &&
obj1.x + obj1.width >= obj2.x){
// Collided with top left edge
}
// Top right edge:
if(obj1.ypast + obj1.height < obj2.y &&
obj1.xpast > obj2.x + obj2.width &&
obj1.y + obj1.height >= obj2.y &&
obj1.x <= obj2.x + obj2.width){
// Collided with top right edge
}
// Bottom right edge:
if(obj2.clsdir == 4 && // Checks if object is bottom collideable
obj1.ypast > obj2.y + obj2.height &&
obj1.xpast > obj2.x + obj2.width &&
obj1.y <= obj2.y + obj2.height &&
obj1.x <= obj2.x + obj2.width){
// Collided with bottom right edge
}
// Bottom left edge:
if(obj2.clsdir == 4 && // Checks if object is bottom collideable
obj1.ypast > obj2.y + obj2.height &&
obj1.xpast + obj1.width < obj2.x &&
obj1.y <= obj2.y + obj2.height &&
obj1.x + obj1.width >= obj2.x){
// Collided with bottom left edge
}
}
});
}
}