nirasan's tech blog

趣味や仕事の覚え書きです。Linux, Perl, PHP, Ruby, Javascript, Android, Cocos2d-x, Unity などに興味があります。

cocos2d-x で Box2D を使う 〜 衝突判定

はじめに

cocos2d-x と Box2D のテスト引き続き。
物体同士が衝突したときに削除するような処理を追加します。

やること

衝突イベントのリスナークラスを作成し、衝突したら削除フラグをたてる。
物体の削除処理で、画面外に出たものだけでなく、削除フラグが立っているものも削除する。

地面と物体の判定のためのタグをConfigで持つように

  • タグ名をイベントリスナーからも参照するので GameScene::kTag を Config::kTag にコピー
  • 呼び出しているコードもあわせて変更する
Config.h
class Config
{
public:
    enum kTag {
        kTag_Ground,
        kTag_Sprite
    };
};

物体にデリートフラグを追加する

PhysicsSprite.h
protected:
    bool deleteFlag;
    
public:
    void setDeleteFlag(bool flag);
    bool getDeleteFlag();
PhysicsSprite.cpp
void PhysicsSprite::setDeleteFlag(bool flag)
{
    deleteFlag = flag;
}

bool PhysicsSprite::getDeleteFlag()
{
    return deleteFlag;
}

衝突イベントのリスナークラスを作成する

  • ヘッダーファイルを追加する
GamePhysicsContactListener.h
#ifndef b2test_GamePhysicsContactListener_h
#define b2test_GamePhysicsContactListener_h

#include <math.h>
#include "cocos2d.h"
#include "Box2D.h"
#include "PhysicsSprite.h"
#include "Config.h"

USING_NS_CC;

class GamePhysicsContactListener : public b2ContactListener
{
public:
    // 衝突時の処理
    void BeginContact(b2Contact* contact)
    {
        // 衝突した双方の物体を取得
        b2Body* bodyA = contact->GetFixtureA()->GetBody();
        b2Body* bodyB = contact->GetFixtureB()->GetBody();
        
        // 物体にひもづくSpriteを取得
        PhysicsSprite* spriteA = (PhysicsSprite*)bodyA->GetUserData();
        PhysicsSprite* spriteB = (PhysicsSprite*)bodyB->GetUserData();
        
        // 地面との衝突は無視する
        if (spriteA->getTag() == Config::kTag_Ground ||
            spriteB->getTag() == Config::kTag_Ground)
        {
            return;
        }
        
        // 衝突時の加速度を取得
        b2Vec2 velocityA = bodyA->GetLinearVelocity();
        b2Vec2 velocityB = bodyB->GetLinearVelocity();
        CCLOG("[BeginContact] A(%f, %f) B(%f, %f)", velocityA.x, velocityA.y, velocityB.x, velocityB.y);
        
        // 加速度が一定上の大きさだったら、ぶつかられた方を削除する
        float threshold = 3;
        if (pow(velocityA.x, 2) + pow(velocityA.y, 2) > pow(threshold, 2)) {
            spriteB->setDeleteFlag(true);
        }
        if (pow(velocityB.x, 2) + pow(velocityB.y, 2) > pow(threshold, 2)) {
            spriteA->setDeleteFlag(true);
        }
    }
};

#endif

イベントリスナーを登録する

GameScene.cpp
  • initPhysics() の末尾に追記する
    // 衝突判定の初期化
    GamePhysicsContactListener* contactListener = new GamePhysicsContactListener();
    world->SetContactListener(contactListener);

削除フラグが立ってたら物体を削除

GameScene.cpp
  • deleteSprite() の削除判定に削除フラグを追加
        // 画面外に出たか、削除フラグが立っていたら、削除
        if (x < 0 || size.width < x || y < 0 || size.height < y || pSprite->getDeleteFlag()) {