【问题标题】:Android AndEngine: handling Collission Detection properlyAndroid AndEngine:正确处理碰撞检测
【发布时间】:2012-09-14 11:03:00
【问题描述】:

我正在开发一个涉及 ff 的简单 AndEngine 游戏。精灵

a.) 坦克 b.) 士兵 c.) 炸弹

我在这里有一个类似的问题:Android AndEngine: Simple sprite collision

游戏是什么样子的:

然而在解决了最初的问题后,又出现了另一个问题:

当炸弹(在飞机当前所在的位置生成并垂直下降,直到通过鼠标点击到达目标或地板)击中目标时,例如士兵,士兵精灵必须分离并在其身后留下一个血溅精灵持续 1 秒,以及炸弹的爆炸精灵。但是,游戏力量关闭并给出 indexOutOfBoundError。我知道这可能是炸弹与其目标之间的精灵计数差异导致数组越界错误,但 logCat 根本没有帮助。

09-22 11:13:37.585: E/AndroidRuntime(735): FATAL EXCEPTION: UpdateThread
09-22 11:13:37.585: E/AndroidRuntime(735): java.lang.IndexOutOfBoundsException: Invalid  index 5, size is 5
09-22 11:13:37.585: E/AndroidRuntime(735):  at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251)
09-22 11:13:37.585: E/AndroidRuntime(735):  at java.util.ArrayList.get(ArrayList.java:304)
09-22 11:13:37.585: E/AndroidRuntime(735):  at org.andengine.entity.Entity.onManagedUpdate(Entity.java:1402)
09-22 11:13:37.585: E/AndroidRuntime(735):  at org.andengine.entity.Entity.onUpdate(Entity.java:1167)
09-22 11:13:37.585: E/AndroidRuntime(735):  at org.andengine.entity.Entity.onManagedUpdate(Entity.java:1402)
09-22 11:13:37.585: E/AndroidRuntime(735):  at org.andengine.entity.scene.Scene.onManagedUpdate(Scene.java:284)
09-22 11:13:37.585: E/AndroidRuntime(735):  at org.andengine.entity.Entity.onUpdate(Entity.java:1167)
09-22 11:13:37.585: E/AndroidRuntime(735):  at org.andengine.engine.Engine.onUpdateScene(Engine.java:591)
09-22 11:13:37.585: E/AndroidRuntime(735):  at org.andengine.engine.Engine.onUpdate(Engine.java:586)
09-22 11:13:37.585: E/AndroidRuntime(735):  at org.andengine.engine.Engine.onTickUpdate(Engine.java:548)
09-22 11:13:37.585: E/AndroidRuntime(735):  at org.andengine.engine.Engine$UpdateThread.run(Engine.java:820)

如此处所示,logCat 并没有在我的代码上给出错误,而是在 AndEngine 本身上给出了错误,而且很可能不是这种情况。

我在 onCreateScene 更新处理程序中运行的新代码(根据上面链接的先前问题的帮助):

protected void checkSoldierCollision() {
    // TODO Auto-generated method stub

    int numBombs = bombGroup.getChildCount();
    int numTroops = troopsGroup.getChildCount();
    final ArrayList<Sprite> toBeDetached = new ArrayList<Sprite>();
    for (int i = 0; i < numBombs; i++) {
        Sprite s = (Sprite) bombGroup.getChildByIndex(i);
        for (int j = 0; j < numTroops; j++) {
            Sprite s2 = (Sprite) troopsGroup.getChildByIndex(j);

            if (s.collidesWith(s2)) {

                /*Sprite splat = createSplat();
                splat.setPosition(s.getX(), s.getY());
                getEngine().getScene().attachChild(splat);*/

                Sprite splode = createExplosion();
                splode.setPosition(s.getX(), s.getY());
                getEngine().getScene().attachChild(splode);

                // WARNING: cannot detach from the list
                 //while looping through the list
                toBeDetached.add(s);
                toBeDetached.add(s2);

            }
        }
    }
    runOnUpdateThread(new Runnable() {
        @Override
        public void run() {
            for (Sprite s : toBeDetached) {
                s.detachSelf();
                Sprite splode = createExplosion();
                splode.setPosition(s.getX(), s.getY());
                getEngine().getScene().attachChild(splode);

            }
            toBeDetached.clear();
        }
    });

}

我注意到它只能是 splat 或爆炸,因为它没有错误。如果 splat 和爆炸都在碰撞时填充场景,则会发生错误。

此外,即使炸弹没有击中士兵(但在击中地板时仍然分离并替换为爆炸云),它也会给出类似的错误:

我的 createBomb 函数:

    public Sprite createBomb(float x, float y) {
    Sprite bombSprite = new Sprite(x, y, this.mBombTextureRegion,
            getVertexBufferObjectManager());

    MoveYModifier downModBomb = new MoveYModifier(1, 60, FLOOR);

    downModBomb.addModifierListener(new IModifierListener<IEntity>() {

        @Override
        public void onModifierStarted(IModifier<IEntity> pModifier,
                IEntity pItem) {

        }

        @Override
        public void onModifierFinished(IModifier<IEntity> pModifier,
                final IEntity pItem) {
             pItem.detachSelf();
             AnimatedSprite explodeSprite = createExplosion();
             explodeSprite.setPosition(pItem.getX(), FLOOR);
             getEngine().getScene().attachChild(explodeSprite);
        }
    });

    bombSprite.registerEntityModifier(downModBomb); // register action
    return bombSprite;
}

我的 onCreateScene 炸弹函数和碰撞 updateHandler:

    scene.setOnSceneTouchListener(new IOnSceneTouchListener() {

        @Override
        public boolean onSceneTouchEvent(Scene pScene,
                TouchEvent pSceneTouchEvent) {
            if (pSceneTouchEvent.getAction() == TouchEvent.ACTION_UP) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Sprite bomb = createBomb(f16Sprite.getX(),
                                f16Sprite.getY());
                        bombGroup.attachChild(bomb);
                    }
                });

                return true;
            }
            return false;
        }
    });

    // periodic checks
    scene.registerUpdateHandler(new IUpdateHandler() {

        @Override
        public void onUpdate(float pSecondsElapsed) {
            // checkTankCollision();
            // checkSoldierBackCollision();
            checkSoldierCollision();
        }

        @Override
        public void reset() {
        }
    });

我的 createExplosion 方法:

            public AnimatedSprite createExplosion() {
    AnimatedSprite boomSprite = new AnimatedSprite(0, 0,
            this.mExplodeTextureRegion, getVertexBufferObjectManager());

    DelayModifier delay = new DelayModifier(.3f); // delay in seconds, can
                                                    // take float numbers .5
                                                    // seconds
    delay.addModifierListener(new IModifierListener<IEntity>() {

        @Override
        public void onModifierStarted(IModifier<IEntity> pModifier,
                IEntity pItem) {
            ((AnimatedSprite) pItem).animate(new long[] { 100, 100, 100 }, // durations/frame
                    new int[] { 1, 2, 3 }, // which frames
                    true); // loop
        }

        @Override
        public void onModifierFinished(IModifier<IEntity> pModifier,
                final IEntity pItem) {
            ((AnimatedSprite) pItem).detachSelf();
        }
    });

    boomSprite.registerEntityModifier(delay); // register action
    return boomSprite;
}

我该如何解决这个问题?循环逻辑并不完全是我的强项。我也对如何实现这一点持开放态度。

更新:刚刚意识到,即使碰撞的结果是啪啪声或爆炸,也没关系,如果玩家不断投掷垃圾炸弹(比如 4-5 次),整个游戏力量就会关闭。

-似乎每次创建焊料/罐的实例时都有允许的轰炸次数。每当炸弹击中士兵时,我都会首先关闭创建爆炸(因此血溅只会留在原位而不是两者都保留)。它工作正常,但超过 4-6 炸弹和游戏关闭。当一个新的士兵实例产生时(意味着当旧的士兵离开屏幕并分离时)玩家会在游戏部队关闭之前获得 4-6 颗炸弹。

【问题讨论】:

    标签: android collision-detection andengine game-engine collision


    【解决方案1】:

    好的,在 JohnEye 先生的帮助下,我已经能够解决我的代码中的问题。考虑以下 sn-p:

        protected void checkSoldierCollision() {
        // TODO Auto-generated method stub
    
        int numBombs = bombGroup.getChildCount();
        int numTroops = troopsGroup.getChildCount();
        final ArrayList<Sprite> toBeDetached = new ArrayList<Sprite>();
        for (int i = 0; i < numBombs; i++) {
            Sprite s = (Sprite) bombGroup.getChildByIndex(i);
            for (int j = 0; j < numTroops; j++) {
                Sprite s2 = (Sprite) troopsGroup.getChildByIndex(j);
    
                if (s.collidesWith(s2)) {
    
                    Sprite splat = createSplat();
                    splat.setPosition(s.getX(), s.getY());
                    getEngine().getScene().attachChild(splat);
    
                    Sprite splode = createExplosion();
                    splode.setPosition(s.getX(), s.getY());
                    explodeGroup.attachChild(splode);
                    // getEngine().getScene().attachChild(splode);
    
                    // WARNING: cannot detach from the list
                    // while looping through the list
                    toBeDetached.add(s);
                    toBeDetached.add(s2);
    
                }
            }
        }
        runOnUpdateThread(new Runnable() {
            @Override
            public void run() {
                for (Sprite s : toBeDetached) {
                    s.detachSelf();
                    Sprite splat = createSplat();
                    splat.setPosition(s.getX(), s.getY());
                    getEngine().getScene().attachChild(splat);
    
                    Sprite splode = createExplosion();
                    splode.setPosition(s.getX(), s.getY());
                    explodeGroup.attachChild(splode);
                    // getEngine().getScene().attachChild(splode);
    
                }
                toBeDetached.clear();
            }
        });
    
    }
    

    正如我所提到的,由于 2 个碰撞对象之间的计数差异,游戏崩溃了。正如 JohnEye 先生指出的那样,当在 UI 线程中生成精灵,但在更新线程中也没有生成精灵时,就会发生这种情况。此外,考虑一下我的 createExplosion、createBomb 和 createSplat 函数:

    制造炸弹

        public Sprite createBomb(float x, float y) {
        Sprite bombSprite = new Sprite(x, y, this.mBombTextureRegion,
                getVertexBufferObjectManager());
    
        MoveYModifier downModBomb = new MoveYModifier(1, 60, FLOOR);
    
        downModBomb.addModifierListener(new IModifierListener<IEntity>() {
    
            @Override
            public void onModifierStarted(IModifier<IEntity> pModifier,
                    IEntity pItem) {
    
            }
    
            @Override
            public void onModifierFinished(IModifier<IEntity> pModifier,
                    final IEntity pItem) {
                runOnUpdateThread(new Runnable() {
                    @Override
                    public void run() {
                        pItem.detachSelf();
                    }
                });
                AnimatedSprite explodeSprite = createExplosion();
                explodeSprite.setPosition(pItem.getX(), FLOOR);
                explodeGroup.attachChild(explodeSprite);
                // getEngine().getScene().attachChild(explodeGroup);
            }
        });
        bombSprite.registerEntityModifier(downModBomb);
        return bombSprite;
    }
    

    创建爆炸

    public AnimatedSprite createExplosion() {
        AnimatedSprite boomSprite = new AnimatedSprite(0, 0,
                this.mExplodeTextureRegion, getVertexBufferObjectManager());
    
        DelayModifier delay = new DelayModifier(.3f); // delay in seconds, can
                                                        // take float numbers .5
                                                        // seconds
        delay.addModifierListener(new IModifierListener<IEntity>() {
    
            @Override
            public void onModifierStarted(IModifier<IEntity> pModifier,
                    IEntity pItem) {
                ((AnimatedSprite) pItem).animate(new long[] { 100, 100, 100 }, // durations/frame
                        new int[] { 1, 2, 3 }, // which frames
                        true); // loop
            }
    
            @Override
            public void onModifierFinished(IModifier<IEntity> pModifier,
                    final IEntity pItem) {
                runOnUpdateThread(new Runnable() {
                    @Override
                    public void run() {
                        ((AnimatedSprite) pItem).detachSelf();
                    }
                });
            }
        });
    
        boomSprite.registerEntityModifier(delay); // register action
        return boomSprite;
    }
    

    创建喷溅

    public Sprite createSplat() {
        Sprite splatSprite = new Sprite(0, 0, this.mSplatTextureRegion,
                getVertexBufferObjectManager());
    
        DelayModifier delay = new DelayModifier(.3f); // delay in seconds, can
                                                        // take float numbers .5
                                                        // seconds
        delay.addModifierListener(new IModifierListener<IEntity>() {
    
            @Override
            public void onModifierStarted(IModifier<IEntity> pModifier,
                    IEntity pItem) {
    
            }
    
            @Override
            public void onModifierFinished(IModifier<IEntity> pModifier,
                    final IEntity pItem) {
                runOnUpdateThread(new Runnable() {
                    @Override
                    public void run() {
                        pItem.detachSelf();
                    }
                });
            }
        });
    
        splatSprite.registerEntityModifier(delay); // register action
        return splatSprite;
    }
    

    请注意,它们都在 runOnUpdate 线程中分离/生成其他精灵。游戏在我之前的运行中崩溃,因为我没有在 runOnUpdate 类上分离/生成所有这些精灵。

    【讨论】:

    • 你这么说a sprite is spawned in the UI thread, but not in the Update thread as well. It needs to be generated on both。这并不正确,只能在更新线程中完成。您可能想要更改它,以免将来的访问者对这个问题感到困惑。顺便说一句,你对赏金改变主意了吗?
    • 糟糕,完全忘记了,JohnEye 来了!
    【解决方案2】:

    问题可能是由于您通过调用 runOnUiThread 将炸弹附加到 UI 线程上,但您在更新线程上检查了冲突。尝试在 runOnUpdateThread 中添加炸弹。

    一般来说,你要保持一致的线程来操作事物,否则会出现奇怪的错误,调试起来很痛苦。

    附带说明:UI 线程非常适合显示 Toast,请参见以下示例:

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(GameActivity.this, "Hello!", Toast.LENGTH_SHORT).show();
        }
    });
    

    【讨论】:

    • 我已经对我的代码进行了 Toast 测试(在鼠标单击事件期间放置了 toast)。看来我每次都有 1 个炸弹组的孩子,因为它一落地就会分离。我也有1个部队实例。另外,让我澄清一下您的建议;我在我的 runOnUpdate 线程上产生了炸弹?但是,分离不也应该发生吗?
    • 是的,您也希望生成代码在更新线程上运行。 UI 线程可能调用会破坏更新线程的方法。考虑以下情况 - 更新线程在场景中所有孩子的列表上执行某些操作。此操作需要大量时间,并且通过孩子的方法以与您相同的方式获取他们的号码并将其存储在内存中。假设有七个孩子。当循环完成它的工作时,来自 UI 线程的一个方法删除了五个主体并砰的一声,更新线程的循环方法超出了范围。
    • 我更新了我的代码,以便能够在 UI 线程期间(当 s 与 s2 碰撞时)生成炸弹,以及在 runOnUpdate 中(在 for-each 循环内)生成炸弹。它仍然没有;但似乎不起作用。正如我所提到的,bombsGroup 和 teamsGroup 的大小都保持为 1
    • 我刚刚注意到你在分离Sprites时使用了postRunnable而不是runOnUpdateThread,如果你把getEngine().getScene().postRunnable(new Runnable() {这一行改成runOnUpdateThread(new Runnable() {怎么样
    • 将其更新为 runOnUpdateThread。当玩家超过一定数量的炸弹(垃圾邮件)时,它仍然会出现索引越界错误。
    【解决方案3】:

    为了调试这个,你需要通过输出(也许通过日志)监控bombGrouptroopsGroup实际添加了多少,这应该与应该添加多少一致。还尝试输出getChildCount() 的值以及您的 i 和 j 参数。

    如果想大胆猜测,试试这个:

    for (int i = 0; i < toBeDetached.size(); i++) {
    
         toBeDetached.get(i).detachSelf();
    
      }
    

    【讨论】:

    • 嗯,澄清一下,这个 sn-p 被放入更新线程是吗?如果是这样,我必须将循环转换为 for 循环而不是 for-each
    • 但是你没有使用 for-each 循环,for-each 就像 for(Sprite s : bombGroup)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多