【cocos2dx】画面上にあるオブジェクトを削除する

ここ数日、ある問題で凄く悩んでいました。

それは、メインのゲーム画面に配置したオブジェクト(敵や、アイテム、主人公)を削除することです。 これは、もしかしたら私のゲーム上特異のモノかもしれないのですが、上手く削除できない時があります。

今回は、そのオブジェクトの削除についてブログに書いていきたいと思います。

オブジェクトの削除

まずは基本です。 親クラスから、オブジェクトを削除するremoveFromParentAndCleanup(bool cleanup)を利用します。

[c]

CCSprite * sprite;

sprite->removeFromParentAndCleanup(true);

[/c]

これを削除したいオブジェクトで使用すると、オブジェクトを削除出来ます。

親クラス側から削除する

先ほどは、オブジェクト側からの削除の方法でしたが、親クラス(CCLayerクラス)で削除する方法もあります。 その場合は、削除するオブジェクトを識別するためにタグを付ける必要があります。

[c]

CCSprite *sprite = CCSprite::create();

//addChildの第三引数がタグです。 layer->addChild(sprite,2,101);

[/c]

削除する際にはremoveChildByTag()を利用します。

[c]

  //オブジェクトのタグを指定します。 layer->removeChildByTag(101);

[/c]

同じクラスのオブジェクトを全て削除する

ここからが凄く悩んだ所です。 上記のオブジェクトを削除する方法から、以下のようにすれば、同じクラスのオブジェクトを削除できると考えました。

[c]

//layerがCCLayerクラスの親クラス
//削除するのはEnemyクラスのオブジェクト

CCObject *mapChild = NULL;

CCARRAY_FOREACH(layer->getChildren(), layerChild){

    bool isRemoveObject= dynamic_cast<Enemy*>(layerChild);
    if (isRemove) {

        CCNode *node = (CCNode*)layerChild;

        node->removeFromParentAndCleanup(true);


    }

}

[/c]

cocos2dx2.1.3を利用をしています。

オブジェクトが削除できない

結論として、上記のコードはでは、オブジェクトを削除しきれませんでした。 同じようなコードをcocos2dで利用していたのですが、その時はこのようなことはありませんでした。

この方法では、クラスを全て把握できないのではないかと考えて、以下のように変えてみると全てのオブジェクトに適応されました。

[c]

  if (isRemove) {

        CCNode *node = (CCNode*)layerChild;

        //削除するのではなく、消すこと
        node->setVisible(false);


    }

[/c]

for文と、クラスの識別が上手く行っているのではなく、どうもオブジェクトにremoveFromParentAndCleanup()が上手く適応されていないみたいです。

考えられる原因

removeFromParentAndCleanupの処理が重い? 私は、同様な処理で、衝突判定も同時に行なっているので、処理が複数になり、removeFromParentAndCleanup()が上手く適応されない。 もしくは、removeFromParentAndCleanup()自体、処理が重いので、適用されないオブジェクトがある。

cocos2dxの問題

cocos2dxは、完璧なゲームエンジンではないので、もしかしたら今使っているバージョンではこのような処理に関してバグがある?

方法が悪い

上記で行った方法では、オブジェクトの削除ができない。別の方法がある。 【追記 8/1有り 下記参考】

代替案

問題はあるにせよ、この処理を行うために何かしないといけません。 ということで、私は以下のようにしています。この方法を使うことにしています。

直接、削除をするのではなく、オブジェクトのループ内でremoveFromParentAndCleanup()の処理を行います。

[c]

    if (isRemove) {

        Enemy *_enemy = (Enemy*)layerChild;


        //EnemyクラスにあるremoveFlag(bool型)をtrueにする。
        _enemy->removeFlag = true;


    }

//EnemyクラスのUpdate(ループ内)

     if(removeFlag){

         this->removeFromParentAndCleanup(true);

     }

[/c]

Enemyクラスに、フラグを用意してそれを利用してupdate(ループ内)で削除の処理をします。

タグを利用する方法

この方法は、削除したいクラスのオブジェクトのタグを統一して、for分を利用する方法です。 これで上手く削除はできましたが、個人的にあまりスマートな感じがしません。

[c]

  CCARRAY_FOREACH(layer->getChildren(), layerChild){

     this->removeChildByTag(101);

  }

    

[/c]

追記 8/1

for文を利用して、オブジェクトを削除する際には、 CCARRAY_FOREACH()を使うのではなく、CCARRAY_FOREACH_REVERSE()を使えばうまくいきます。 [c]

CCARRAY_FOREACH_REVERSE(layer->getChildren(), layerChild){

}

[/c]

理由は、配列の中身を削除した時点で、オブジェクトを格納している配列順が一つ繰り上がってしまうために、for文で、取り出せなくなるためです。 よく考えると、凄く当然なことなのです。 とても勉強になりました。