2

我一直在尝试使用我的碰撞检测来阻止物体相互穿过。我不知道该怎么做。

当物体碰撞时,我尝试反转它们的速度矢量的方向(这样它就会远离碰撞的地方),但有时物体会相互卡住。

我试过改变他们的速度,但这只是父母互相反对。

有没有一种简单的方法来限制物体的运动,使它们不会穿过其他物体?我一直在使用矩形相交进行碰撞,我也尝试过圆形碰撞检测(使用对象之间的距离)。

想法?

package objects;

import java.awt.Rectangle;

import custom.utils.Vector;
import sprites.Picture;
import render.Window;



// Super class (game objects)
public class Entity implements GameObject{


    private Picture self;
    protected Vector position;
    protected Vector velocity = new Vector(0,0);
    private GameObject[] obj_list = new GameObject[0];

    private boolean init = false;

    // Takes in a "sprite"
    public Entity(Picture i){
        self = i;
        position = new Vector(i.getXY()[0],i.getXY()[1]);
        ObjectUpdater.addObject(this);
    }



    public Object getIdentity() {
        return this;
    }

    // position handles
    public Vector getPosition(){
        return position;
    }

    public void setPosition(double x,double y){
        position.setValues(x,y);
        self.setXY(position);
    }

    public void setPosition(){
        position.setValues((int)Window.getWinSize()[0]/2,(int)Window.getWinSize()[1]/2);
    }

    // velocity handles
    public void setVelocity(double x,double y){ // Use if you're too lazy to make a vector
        velocity.setValues(x, y);
    }
    public void setVelocity(Vector xy){ // Use if your already have a vector
        velocity.setValues(xy.getValues()[0], xy.getValues()[1]);
    }

    public Vector getVelocity(){
        return velocity;
    }


    // inferface for all game objects (so they all update at the same time)

    public boolean checkInit(){
        return init;
    }

    public Rectangle getBounds() {
        double[] corner = position.getValues(); // Get the corner for the bounds
        int[] size = self.getImageSize(); // Get the size of the image

        return new Rectangle((int)Math.round(corner[0]),(int)Math.round(corner[1]),size[0],size[1]); // Make the bound
    }

    // I check for collisions where, this grabs all the objects and checks for collisions on each.

    private void checkCollision(){
        if (obj_list.length > 0){
        for (GameObject i: obj_list){
            if (getBounds().intersects(i.getBounds()) && i != this){
                // What happens here?
            }
        }
        }
    }

    public void updateSelf(){
        checkCollision();
        position = position.add(velocity);
        setPosition(position.getValues()[0],position.getValues()[1]);
        init = true;
    }


    public void pollObjects(GameObject[] o){
        obj_list = o;
    }

}

希望阅读起来不会太难。

编辑: 所以我一直在使用矩形相交方法来计算对象的位置并修改速度。它工作得很好。唯一的问题是某些物体会推动其他物体,但这太重要了。碰撞对于我正在创建的迷你游戏来说几乎是一个额外的东西。非常感谢您的帮助。

尽管如此,我仍然非常感谢对所提到的想法进行详细说明,因为我不完全确定如何将它们实施到我的项目中。

4

1 回答 1

3

没有看到你的代码,我只能猜测发生了什么。我怀疑您的对象被卡住了,因为它们超出了其他对象的边界,最终进入了内部。确保每个对象的步长不仅仅是速度 * delta_time,而是步长受到潜在碰撞的限制。当发生碰撞时,计算它发生的时间(在 delta_time 中的某处)并跟随反弹来确定最终的对象位置。或者,只需将物体设置为接触,速度根据动量守恒定律变化。

编辑看到你的代码后,我可以扩展我的答案。首先,让我澄清一下您询问的一些术语。由于每次调用都updateSelf只是将速度矢量添加到当前位置,因此您实际上是一个单位时间增量(增量时间始终为 1)。换句话说,您的“速度”实际上是自上次调用updateSelf. 我建议使用显式(浮动)时间增量作为模拟的一部分。

其次,跟踪多个运动物体之间的碰撞的一般问题非常困难。无论使用什么时间增量,一个对象都可能在该增量中经历许多碰撞。(想象一个物体挤在另外两个物体之间。在任何给定的时间间隔内,物体在两个周围物体之间来回反弹的次数没有限制。)此外,一个物体可能(在分辨率的范围内)计算)同时与多个对象发生碰撞。如果对象在移动时实际改变大小(正如您的代码所暗示的那样),问题会更加复杂。

第三,您有一个重要的错误来源,因为您将所有对象位置四舍五入为整数坐标。我建议用浮点对象(Rectangle2D.Float而不是Rectangle;Point2D.Float而不是Vector)来表示你的对象。我还建议将position字段替换为同时捕获位置和大小的矩形bounds字段。这样,您不必在每次调用getBounds(). 如果对象大小是恒定的,这也将简化边界更新。

最后,每个对象内部都有碰撞检测逻辑存在一个重大问题:当对象 A 发现它会撞到对象 B 时,那么对象 B 也会撞到对象 A!但是,对象 B 会独立于对象 A 进行自己的计算。如果先更新 A,则 B 可能会错过碰撞,反之亦然。将整个碰撞检测和对象移动逻辑移动到全局算法并保持每个游戏对象相对简单会更好。

一种方法(我推荐)是编写一个“updateGame”方法,将游戏状态推进给定的时间增量。它将使用记录碰撞的辅助数据结构,可能如下所示:

public class Collision {
    public int objectIndex1;  // index of first object involved in collision
    public int objectIndex2;  // index of second object
    public int directionCode; // encoding of the direction of the collision
    public float time;        // time of collision
}

整个算法将游戏从当前时间推进到由参数定义的新时间deltaTime。它的结构可能是这样的:

void updateGame(float deltaTime) {
    float step = deltaTime;
    do (
        Collision hit = findFirstCollision(step);
        if (hit != null) {
            step = Math.max(hit.time, MIN_STEP);
            updateObjects(step);
            updateVelocities(hit);
        } else {
            updateObjects(step);
        }
        deltaTime -= step;
        step = deltaTime;
    } while (deltaTime > 0);
}

/**
 * Finds the earliest collision that occurs within the given time
 * interval. It uses the current position and velocity of the objects
 * at the start of the interval. If no collisions occur, returns null.
 */
Collision findFirstCollision(float deltaTime) {
    Collision result = null;
    for (int i = 0; i < obj_list.length; ++i) {
        for (int j = i + 1; j < obj_list.length; ++j) {
            Collision hit = findCollision(i, j, deltaTime);
            if (hit != null) {
                if (result == null || hit.time < result.time) {
                    result = hit;
                }
            }
        }
    }
    return result;
}

/**
 * Calculate if there is a collision between obj_list[i1] and
 * obj_list[i2] within deltaTime, given their current positions
 * and velocities. If there is, return a new Collision object
 * that records i1, i2, the direction of the hit, and the time
 * at which the objects collide. Otherwise, return null.
 */
Collision findCollision(int i1, int i2, float deltaTime) {
    // left as an exercise for the reader
}

/**
 * Move every object by its velocity * step
 */
void updateObjects(float step) {
    for (GameObject obj : obj_list) {
        Point2D.Float pos = obj.getPosition();
        Point2D.Float velocity = obj.getVelocity();
        obj.setPosition(
            pos.getX() + step * velocity.getX(),
            pos.getY() + step * velocity.getY()
        );
    }
}

/**
 * Update the velocities of the two objects involved in a
 * collision. Note that this does not always reverse velocities
 * along the direction of collision (one object might be hit
 * from behind by a faster object). The algorithm should assume
 * that the objects are at the exact position of the collision
 * and just update the velocities.
 */
void updateVelocities(Collision collision) {
    // TODO - implement some physics simulation
}

MIN_STEP常数是一个最小时间增量,以确保游戏更新循环不会因更新如此小的时间步而无法取得进展而卡住。(对于浮点,它可能deltaTime -= step;保持deltaTime不变。)

关于物理模拟:关于弹性碰撞的维基百科文章为这个问题提供了一些很好的数学。

于 2013-02-15T05:18:28.930 回答