読者です 読者をやめる 読者になる 読者になる

WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

「LiquidFun」を利用して船を海に浮かべてみる

こんにちは。今回ブログを担当します長屋です。

さて今回は「LiquidFun」という「Box2D」をベースに作られたお手軽に流体計算ができるライブラリをcocos2d-xに組み込んで海を作りその上に船を浮かべてみたいと思います。
今回はcocos2d-x 3.2 rc0を利用しますが、他のバージョンでも同じような手順で組み込めると思います。

☆cocos2d-x 3.2 rc0☆
http://www.cocos2d-x.org/download

☆LiquidFun☆
https://github.com/google/liquidfun/releases

☆LiquidFunをcocos2d-xに組み込もう☆
①cocos2d-x 3.0以上の場合、cocosコマンドを使用するなどしてプロジェクトを作成しましょう。
作成すると以下のディレクトリにBox2D関連のソースがコピーされていると思います。

・プロジェクト名/cocos2d/external/Box2D

Xcode上からディレクトリ内の以下のファイルを削除しておきましょう。
・Box2d.h
・Collision
・Common
・Dynamics
・Rope

image01

ファインダー上で確認すると空のフォルダが残っているので削除しておきましょう。

②LiquidFunをダウンロードした後、解凍します。
以下のパスにある6つのファイルを先ほど削除したディレクトリにコピーしておきます。

・liquidfun-1.0.0/liquidfun/Box2D/Box2D/

これらのファイルをコピー
・Box2d.h
・Collision
・Common
・Dynamics
・Rope
・Particle

その後Xcode上からプロジェクトに再度追加し直しておきましょう。
image02

☆水の流体パーティクルを生成する☆
Box2D上を使用するためにワールドと生成したパーティクルが落ちていかないように箱を生成します。

//32ピクセルで1mに設定  
#define PTM_RATIO 32.0f  
  
void HelloWorld::initPhysics(){  
    //ワールド生成  
    b2World* world = new b2World(b2Vec2(0, -10.0));  
    // 箱の設定    b2BodyDef groundBodyDef;  
    groundBodyDef.position.Set(0, 0);  
  
    //箱のボディ作成  
    b2Body * groundBody = world->CreateBody(&groundBodyDef);  
  
    //判定箇所作成  
    b2EdgeShape edge;  
    b2FixtureDef groundFixtureDef;  
    groundFixtureDef.shape = &edge;  
  
    //左の壁  
    edge.Set(b2Vec2(0, size.height/PTM_RATIO), b2Vec2(0, 0));  
    groundBody->CreateFixture(&groundFixtureDef);  
    //底の壁  
    edge.Set(b2Vec2(0, 0), b2Vec2(size.width/PTM_RATIO, 0));  
    groundBody->CreateFixture(&groundFixtureDef);  
    //右の壁  
    edge.Set(b2Vec2(size.width/PTM_RATIO,0),b2Vec2(size.width/PTM_RATIO, size.height/PTM_RATIO));  
    groundBody->CreateFixture(&groundFixtureDef);  
}  

次に流体パーティクルを生成します

void HelloWorld::createSeaParticleGroup(){  
    //パーティクルシステムのデータ設定  
    b2ParticleSystemDef particleSystemDef;  
  
    //各パーティクルの半径を設定  
    particleSystemDef.radius = 4.0/PTM_RATIO;  
  
    //パーティクルシステムの生成  
    b2ParticleSystem * particleSystem = world->CreateParticleSystem(&particleSystemDef);  
  
    //画面サイズ取得  
    Size size = Director::getInstance()->getWinSize();  
  
    //生成位置設定  
    Point pos = {size.width/2/PTM_RATIO,size.height * 0.2/PTM_RATIO};  
  
    //グループで生成する際はパーティクルグループの形を設定する必要がある  
    b2PolygonShape* shape = new b2PolygonShape();  
    shape->SetAsBox(size.width*0.8/PTM_RATIO, size.height*0.05/PTM_RATIO);  
  
    //パーティクルグループのデータ設定  
    b2ParticleGroupDef groupDef;  
  
    //パーティクルのグループを設定  
    groupDef.shape = shape;  
  
    //flagsを設定する事でパーティクルの挙動を設定する事ができます  
    //今回は海を作るので水になっていますが  
    //バネや砂など色々な挙動を設定する事ができます。  
    groupDef.flags = b2_waterParticle;  
    groupDef.color = b2ParticleColor(100,150,255,255);  
    groupDef.position.Set(pos.x / PTM_RATIO, pos.y / PTM_RATIO);  
    seaGroup = particleSystem->CreateParticleGroup(groupDef);  
  
    //各パーティクルのユーザーデータに「Sprite*」を持たせる  
    //ユーザーデータは「b2ParticleSystem」がバッファとして保持している。  
    //各グループはデータの開始地点を保持しており  
    //自分のグループが保持しているパーティクルの分だけループして取得する事ができる  
    void ** userData = particleSystem->GetUserDataBuffer() + seaGroup->GetBufferIndex();  
    for(int i = 0; i != seaGroup->GetParticleCount();i++,userData++){  
        Sprite * tex = Sprite::create("tex.png");  
        (*userData) = tex;  
        tex->setScale(0.5f);  
        this->addChild(tex);  
    }  
}  

Layerクラスの「void update(float dt)」メソッドをオーバーライドして毎フレームワールドの更新処理と
パーティクルスプライトの座標更新を行います

void HelloWorld::update(float update){  
  
    //box2d更新  
    world->Step(1.0/60.0f, 5, 2, 1);  
  
    //グループのパーティクルを更新  
    void ** userData = particleSystem->GetUserDataBuffer() + seaGroup->GetBufferIndex();  
  
    //各スプライトの座標と色を更新します  
  
    //色バッファ  
    b2ParticleColor * colorList = particleSystem->GetColorBuffer() + seaGroup->GetBufferIndex();  
  
    //座標バッファ  
    b2Vec2* vecList = particleSystem->GetPositionBuffer() + seaGroup->GetBufferIndex();  
  
    //ユーザーデータ取得  
    void ** userData = particleSystem->GetUserDataBuffer() + seaGroup->GetBufferIndex();  
  
    //バッファループ  
    for(int i = 0; i < seaGroup->GetParticleCount();i++,colorList++,vecList++,userData++){  
        ((Sprite*)(*userData))->setPosition((*vecList).x*PTM_RATIO,(*vecList).y*PTM_RATIO);  
        ((Sprite*)(*userData))->setColor(Color3B((*colorList).r, (*colorList).g, (*colorList).b));  
    }  
}

実行結果

image03

海らしき物が完成したので「PhysicsEditor」を使用して船にあたり判定をつけたオブジェクトを追加して・・・

image04

海に船を浮かべてみました。
が・・・このままではこの船は絶体絶命の状態なので、次回は助けるための回になるかもしれません。

LiquidFunの詳しい仕様や動作は、リファレンスを参照してください。

☆LiquidFun Programmer's Guide☆
http://google.github.io/liquidfun/Programmers-Guide/html/index.html