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です。シミュレーションの一部として、明示的な (フロート) 時間増分を使用することをお勧めします。

第二に、複数の移動オブジェクト間の衝突を追跡する一般的な問題は非常に困難です。どのような時間増分を使用しても、その増分内でオブジェクトが多くの衝突を受ける可能性があります。(オブジェクトが 2 つの他のオブジェクトの間に挟まれていると想像してください。任意の時間間隔で、オブジェクトが周囲の 2 つのオブジェクトの間を行ったり来たりする回数に制限はありません。) また、オブジェクトは (解像度の範囲内で)計算) は同時に複数のオブジェクトと衝突します。オブジェクトが移動すると実際にサイズが変わる場合 (コードが示唆しているように)、問題はさらに複雑になります。

第 3 に、すべてのオブジェクトの位置を整数座標に丸めているため、重大なエラーの原因となります。オブジェクトを浮動小数点オブジェクト (Rectangle2D.FloatではなくRectangle;Point2D.FloatではなくVector) で表現することをお勧めします。フィールドを、位置とサイズの両方をキャプチャpositionする長方形のフィールドに置き換えることもお勧めします。boundsそうすれば、 を呼び出すたびに新しいオブジェクトを作成する必要がなくなりますgetBounds()。オブジェクトのサイズが一定の場合、境界の更新も簡素化されます。

最後に、各オブジェクト内に衝突検出ロジックを配置することには重大な問題があります。オブジェクト A がオブジェクト B に衝突することを発見すると、オブジェクト B がオブジェクト A に衝突することになります! ただし、オブジェクト B はオブジェクト A とは独立して独自の計算を行います。最初に A を更新すると、B が衝突を見逃す可能性があり、その逆も同様です。衝突検出とオブジェクトの移動ロジック全体をグローバル アルゴリズムに移行し、各ゲーム オブジェクトを比較的シンプルに保つ方がよいでしょう。

1 つのアプローチ (これをお勧めします) は、ゲームの状態を特定の時間単位で進める「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 に答える