1

ミニオン オブジェクトの ArrayList があり、シールドがミニオンと衝突したときに、そのミニオンを ArrayList から削除したいと考えています。ただし、1つの方法でしか機能させることができず、他の方法では機能しません。誰でも理由を説明できますか?

3 つのケースすべてで、Android の Renderer の onDrawFrame() メソッドを使用しているため、いつ呼び出されるかを制御できません。ただし、3つの方法すべてのコードは次のとおりです。

方法 1: (機能しません)

public void onDrawFrame(GL10 gl) {
    List<Integer> indexesToRemove = new ArrayList<Integer>();
    int len = minions.size();
    for(int i=0; i<len; i++){
        if( OverlapTester.overlapCircleRectangle( (Circle)shield1.bounds,  (Rectangle)minions.get(i).bounds) ){ //this tests out to work just fine
            indexesToRemove.add(i);
        }
    }
    for(int i=indexesToRemove.size()-1; i>=0; i--){
        minions.remove(indexesToRemove.get(i)); //<------ why doesn't this work?
    }
}

問題は、最後の行minions.remove(indexesToRemove.get(i));が実際にはミニオンを削除しないことです。適切なインデックスで呼び出されます。デバッガーをステップ実行してまっすぐに実行しましたが、配列リストはまったく変更されていません。どうしてこれなの?実際、デバッガーでは、その行「minions.remove(indexesToRemove.get(i));」何十億回も呼ばれます。

方法 2: (まだ機能しません)

public void onDrawFrame(GL10 gl) {
    synchronized(minions){
        List<Integer> indexesToRemove = new ArrayList<Integer>();
        int len = minions.size();
        for(int i=0; i<len; i++){
            if( OverlapTester.overlapCircleRectangle( (Circle)shield1.bounds,  (Rectangle)minions.get(i).bounds) ){ //this tests out to work just fine
                indexesToRemove.add(i);
            }
        }
        for(int i=indexesToRemove.size()-1; i>=0; i--){
            minions.remove(indexesToRemove.get(i)); //<------ why doesn't this work?
        }
    }
}

ここで、私は考えました...「おそらく、完全に同期されていないため、drawFrame が何度も呼び出され、間違ったタイミングで配列リストにアクセスしているため、ロックする必要があります。しかし、それでも機能しません。再び、その行minions.remove(indexesToRemove.get(i));は適切なインデックスで適切に呼び出されますが、実際にはオブジェクトを削除しません.画面上の盾がミニオンにぶつかるのを見ていますが、ミニオンには何も起こりません(配列リストから削除されません)

方法#3(これは実際に機能します)

public void onDrawFrame(GL10 gl) {
    ArrayList<Minion> colliders = new ArrayList<Minion>(minions);
    int len = colliders.size();
    for(int i=0; i<len; i++){
        GameObject collider = colliders.get(i);
        if(OverlapTester.overlapCircleRectangle((Circle)shield1.bounds, (Rectangle)collider.bounds)){
            minions.remove(collider); // <---- why does THIS work instead?
        }
    }
}

このコードは完全に機能します。シールドがミニオンを叩き、ミニオンは死んでしまいます。ここでわかるように、唯一の違いは、ArrayList.remove(object)インデックスで削除するのではなく、オーバーロードされたメソッドを使用していることです。行のようにminions.remove(collider);。なぜこれが機能するのですか?

誰でも説明できますか?

余談ですが、arraylist の別のインスタンス変数のコピーを格納する以外に、管理するためのより良い方法はありArrayList<Minion> colliders = new ArrayList<Minion>(minions);ますか?

注: Shield と Minion はどちらも、境界として長方形の形状を持つ通常の Java オブジェクトです。そのすべての数学は問題なくチェックアウトします。デバッガーでテストしましたが、衝突検出は正確です。onDrawFrame()また、メソッドで正確な境界/位置を更新しています。

4

3 に答える 3

10

ArrayList以下の 2 つのメソッドを提供するためです。

public E remove(int index)
public boolean remove(Object o)

を呼び出すminions.remove(indexesToRemove.get(i))と、indexesToRemoveは であるList<Integer>ため、呼び出しは、オブジェクトを直接指定して要素を削除する 2 番目の署名にバインドされます。自動ボックス化解除ではIntegerint要素が見つからず、何も起こりません。

試してみてください:minions.remove((int)indexesToRemove.get(i))メソッドの静的バインディングが正しく適用されるようにします。

于 2013-01-22T20:36:22.177 に答える
7

@ジャックの答えは正しいです。後世のために、ループIteratorで削除できる here を使用する必要があります。

// synchronization wrapper here
Iterator<Minion> iterator = minions.iterator();
while (iterator.hasNext()) {
    Minion minion = iterator.next();
    if( OverlapTester.overlapCircleRectangle(..., minion.bounds)) {
        iterator.remove();
    }
}
于 2013-01-22T20:38:44.467 に答える
3

最初の2つの例では、Integerをオブジェクト参照として扱い、intにキャストしています

于 2013-01-22T20:35:45.050 に答える