碰撞 - 识别物体何时停止碰撞

Collision - recognizing when object has stopped colliding

我的意思是,当我与一个实体的侧面发生碰撞并想要跳跃时,我无法移动 right/left 因为我有一个标志可以防止我在 right/left 边,像这样:

这是我用来检测碰撞的代码:

public void onCollision(Wrapper wrapper) {
    if (horizontal_velocity == 0 && vertical_velocity == 0) {
        return;
    }

    Entity e1 = wrapper.thisEntity;  //the object we are controlling
    Entity e2 = wrapper.otherEntity; //the object we are impacting with

    Rectangle rect1 = wrapper.thisEntityRectangle;
    Rectangle rect2 = wrapper.otherEntityRectangle;

    int e1y = (int) rect1.getY(), e1x = (int) rect1.getX(), e1w = (int) rect1.getWidth(), e1h = (int) rect1.getHeight();
    int e2y = (int) rect2.getY(), e2x = (int) rect2.getX(), e2w = (int) rect2.getWidth(), e2h = (int) rect2.getHeight();

    e1x += e1w / 2;
    e1w /= 2;
    e1y += e1h / 2;
    e1h /= 2;


    //horizontal interaction -- x

    /*   ---+\\\+---
     *      |------|
     *      |      |
     *  e1  |  e2  |  e1
     *      |      |
     *      |------|
     *   ---+\\\+---
     */

    boolean touchingSide = false;

    if(e1y > e2y && e1y < e2y + e2h) {
        //touchingSide = true;
        if(e1x > e2x - e1w && e1x < e2x) { //going right || hitting left side
            collisionsHappening.put(wrapper.otherEntity.getID(), "right");
            canMoveRight = false;
            horizontal_velocity = 0;
            setX(e2x - e1w*2);
        }

        if(e1x < e2x + e2w + e1w && e1x > e2x) { //going left || hitting right side
            collisionsHappening.put(wrapper.otherEntity.getID(), "left");

            canMoveLeft = false;
            horizontal_velocity = 0;
            setX(e2x + e2w);
        }
    }

    //vertical interaction -- y

    /*
     *  \|     e1     |\
     *  \+--+------+--+\
     *  \\|      |\\
     *  \\|  e2  |\\
     *  \\|      |\\
     *  \+--+------+--+\
     *  \|     e1     |\
     *
     */

    if(e1x > e2x - e1w && e1x < e2x + e2w && !touchingSide) {
        if(e1y + e1h > e2y && e1y < e2y) { //going down || hitting top side
            collisionsHappening.put(wrapper.otherEntity.getID(), collisionsHappening.get(wrapper.otherEntity.getID()) + "|top");
            setY(e2y - e1h * 2);
            vertical_velocity = 0;
            //readyNextTurn = true;

            isFalling = false;
            onGround = true;
        }
        if(e1y - e1h < e2y + e2h && e1y > e2y) { //going up || hitting bottom side
            collisionsHappening.put(wrapper.otherEntity.getID(), collisionsHappening.get(wrapper.otherEntity.getID()) + "|bottom");
            setY(e2y + e2h + e1h);
            vertical_velocity = 0;
            //readyNextTurn = true;
            isFalling = false;
            onGround = true;

        }
    }
}

..代码受 canMoveRightcanMoveLeft 标志影响的地方:

public void key(int keyCode) {
    try {
        onEdge();

        switch (keyList.get(keyCode)) {

            ...

            case MOVE_RIGHT:
                for(Entity e : objectsCollidingWith) {  //check if still colliding, if not, reset variable canMoveLeft inorder to not impair movement
                    if(collisionsHappening.get(e.getID()) == "right") {
                        canMoveRightNextFrame = true;
                    }
                }
                canMoveLeft = true;
                if (!canMoveRight && !canMoveRightNextFrame) break;

                if(canMoveRightNextFrame) {
                    canMoveRightNextFrame = false;
                    System.out.println(canMoveRightNextFrame);
                }

                if (horizontal_velocity == 0) {
                    horizontal_velocity = getXDisplacement();
                }
                changeX(horizontal_velocity);
                direction = "right";
                //horizontal_velocity = 0;
                break;
            case MOVE_LEFT:
                for(Entity e : objectsCollidingWith) {  //check if still colliding, if not, reset variable canMoveLeft inorder to not impair movement
                    if(collisionsHappening.get(e.getID()) == "left") {
                        canMoveLeftNextFrame = true;
                    }
                }
                canMoveRight = true;
                if (!canMoveLeft && !canMoveLeftNextFrame) {
                    break;
                }

                canMoveLeftNextFrame = canMoveLeftNextFrame ? true : false; //if true, make false

                if (horizontal_velocity == 0) {
                    horizontal_velocity = getXDisplacement();
                }
                changeX(-horizontal_velocity);
                direction = "left";
                //horizontal_velocity = 0;
                break;
            case JUMP:
                jump();
        }
    } catch (NullPointerException e) {
        //ignore - a key with no action pressed
    }
}

我试过的

当我寻找冲突时 - 没有像上面的代码那样对它们做出反应 - 我尝试实现一个 HashMap,在其中我将一个字符串 - "left""right" - 放入key Entity.getID() 其中 returns 一个唯一的名字,就像这样(在相关的代码行之后是 // <<<):

public Wrapper[] collisions(Entity thisEntity) {
    ArrayList<Wrapper> wrappers = new ArrayList<Wrapper>();
    Player p = null;
    boolean isPlayer = thisEntity instanceof Player;
    boolean onGround = false;

    if (isPlayer) {
        p = (Player) thisEntity;
    }
    try {
        for (Entity e : getLevel().getEntities()) {
            if (!thisEntity.equals(e)) {
                collisionsHappening.put(e.getID(), "");  // <<<
                objectsCollidingWith.add(e);             // <<<
                if (thisEntity.getRect().intersects(e.getRect())) {
                    wrappers.add(new Wrapper(thisEntity, e, thisEntity.getRect(), e.getRect(),
                            thisEntity.getRect().intersection(e.getRect())));

                    onGround = true;
                    //collisionsHappening.put(e.getID(), true);
                    //System.out.println("collision between " + e.getID() + " + " + thisEntity.getID());
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    if (isPlayer) {
        p.isTouching = onGround;
        if (onGround) {
            //System.out.println(onGround);
        }

    }
    //Entity[] result = list.toArray(new Entity[1]);
    return wrappers.toArray(new Wrapper[0]);
}

该系统依赖于在碰撞停止后将标志保留 1 帧,以便在代码行

中删除标志 canMoveRightcanMoveLeft
for(Entity e : objectsCollidingWith) {  //check if still colliding, if not, reset variable canMoveLeft inorder to not impair movement
    if(collisionsHappening.get(e.getID()) == "left") {
        canMoveLeftNextFrame = true;
    }
    }
    canMoveRight = true;
    if (!canMoveLeft && !canMoveLeftNextFrame) {
        break;
    }

    if(canMoveLeftNextFrame) {
        canMoveLeftNextFrame = false; //if true, make false
    }

    ...

...canMoveRight 标志也一样...


然而,这是行不通的,我在 "scrape" 实体的边缘后仍然无法在跳跃中移动,除非我向 "reset" 所需标志的相反方向移动方向(行:case MOVE_RIGHT: ... canMoveLeft = true;case MOVE_LEFT: ... canMoveRight = true;),如动画中所示。

所以,总结一下:

谢谢。

对于一款比贪吃蛇更高级的游戏,实际上不可能以这种事件驱动的方式来解决这个问题。我建议您专注于更新每个实体的简单更新循环。然后每个实体更新自己的速度和位置。然后检查碰撞并处理响应。关键事件处理程序不应有任何逻辑,而只是更改实体可以轮询的标志。

至于碰撞和响应部分,这是一个简单的平台游戏引擎的核心所在,并且有许多不同的方法。在现成物理引擎的现代世界中,例如Box2D,你可以简单地让物理系统处理事情。但从事物的声音来看,你不是在寻找那个,你想要的是我所说的基于逻辑的物理学。

如果您的世界和实体由轴对齐的框组成,您可以将每个实体的水平和垂直运动分成两个不连续的步骤。对于每个轴,每个对象都会尝试尽可能远地移动(取决于速度和碰撞),如果对象撞到某物,则生成碰撞事件并将该轴上的速度设置为零。对另一个轴重复,您将拥有一个简单的平台游戏引擎。对于此方法,由于浮点数值错误,您需要仔细处理两个接触对象的状态。我建议使用整数进行碰撞检测(在屏幕坐标的范围内)。

如果您希望拥有一个不受轴对齐框限制的环境和实体,那么您将不得不变得更加复杂,不再依赖于简单地处理 X 然后 Y 运动来为您提供诸如盒子在地板上或墙上滑动。在这种情况下,我建议您跟踪您的实体站立或行走的表面,以便您可以在每种状态下以不同方式处理它们的运动。当它们在空中时,正常运动,但当它们在某个表面上时,运动被限制为水平运动,并调整垂直位置以使其保持在表面上。然后您将需要检测它们何时更改状态。

希望对您有所帮助。事实证明,Google并不是一个容易的话题。 (更新循环、碰撞检测、运动物理学、数值积分和碰撞响应的几何学都很容易 Google)。大多数程序员似乎只是采取不断摆弄它直到它起作用的方法,因此它从来没有得到很好的记录。

P.S。隐式欧拉积分法适用于大多数游戏