Javascript碰撞检测系统不会忽略阻塞的碰撞

时间:2014-03-12 07:33:38

标签: javascript jquery html5 collision-detection easeljs

我在使用 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");
                    }
                });

JSFiddle

2 个答案:

答案 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
            }
        }
    });
    }
}

JSFiddle