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

WonderPlanet DEVELOPER BLOG

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

Cocos2d-x と Windows アプリケーションで ParticleDesigner を作ってみる

Cocos2d-x Particle Designer

こんにちは。今回のブログ担当 藤澤です。

先日、ふと思い立ってパーティクルを作ってみようと思ったのですが、ParticleDesigner って 6,000円もするんですね……。
Cocos2d-x はせっかく無料の開発環境ですし、できれば無料で済ませたい。
Cocos2d-x のパーティクルは CCParticleSystem のプロパティをいじってやればいいわけですからコードでパーティクルを作ることは可能です。ということは GUI で値を入力しながらプロパティの値を動的に変更してやれば ParticleDesigner 的なツールを作ることもできそうな気がします。
ということで、使いなれた Windows アプリケーションでツールを作ってみることにしました。

1. 開発環境の準備

Cocos2d-x のライブラリは公式サイトから zip をダウンロードして適当なフォルダに解凍するだけで OK です。
GUI 部分の開発に Visual C# 2010 Express、エンジン部分の開発に Visual C++ 2010 Express を使用します。
その他、プロジェクトの作成に Python が必要ですが、後述するように なくてもプロジェクトを作成することは可能です。

2. プロジェクトの作成

ProjectCreator を使えば Mac のときと同様にプロジェクトを作成可能です(参考)。
あるいは Mac でプロジェクトを作って そのままコピーしてきても OK です。

プロジェクトをイチから作成する場合は Visual C++ 2010 Express で以下の設定をしてあげればいけました。(Box2D などは省略しています)

(1) プロジェクトのプロパティ - 構成プロパティ - 全般

構成の種類 : ダイナミック ライブラリ(.dll)
出力ディレクトリ : $(SolutionDir)$(Configuration).win32¥
中間ディレクトリ : $(Configuration).win32¥
ターゲットの拡張子 : .dll

(2) プロジェクトのプロパティ - 構成プロパティ - C/C++ - 全般

追加のインクルードディレクトリ に以下のフォルダを追加
cocos2dx
cocos2dx¥include
cocos2dx¥kazmath¥include
cocos2dx¥platform¥win32
cocos2dx¥platform¥third_party¥win32
cocos2dx¥platform¥third_party¥win32¥OGLES

(3) プロジェクトのプロパティ - 構成プロパティ - C/C++ - プリコンパイル済みヘッダー

プリコンパイル済みヘッダー : プリコンパイル済みヘッダーを使用しない

(4) プロジェクトのプロパティ - 構成プロパティ - リンカー - 全般

追加のライブラリディレクトリ : $(OutDir)

(5) プロジェクトのプロパティ - 構成プロパティ - リンカー - 入力

追加の依存ファイル に libcocos2d.lib を追加

(6) プロジェクトの追加

cocos2dx¥proj.win32¥cocos2dx.vcproj をソリューションに追加

※ 初回のビルドにはかなり時間がかかります。

3. C# から Cocos2d-x の呼び出し

まずは一般的な Windows アプリケーションから Cocos2d-x の画面を表示させてみます。
C# の exe から C++ の DLL を呼び出し、C++ の DLL が Cocos2d-x のライブラリを操作する、という流れになります。

3-1. C++ 側の実装

Cocos2d-x の画面を表示するメソッドを作成し、それを C# 側からアクセスできるようにします。

(1) ParticleDesigner.h

class ParticleDesigner  
{  
public:  
    static __declspec (dllexport) int __stdcall initParticleDesigner();  
};  

(2) ParticleDesigner.cpp

#include "stdafx.h"  
#include "CCApplication.h"  
#include "CCEGLView.h"  
#include "AppDelegate.h"  
#include "ParticleDesigner.h"  
  
using namespace cocos2d;  
  
int __stdcall ParticleDesigner::initParticleDesigner()  
{  
    AppDelegate app;  
    CCEGLView* eglView = CCEGLView::sharedOpenGLView();  
    eglView->setViewName("HelloCpp");  
    eglView->setFrameSize(480, 320);  
    return CCApplication::sharedApplication()->run();  
}  

(3) ParticleDesigner.def

ファイルの追加メニューに出てこないので、適当にファイルを追加して拡張子を .def に変更します。

LIBRARY "ParticleDesigner.dll"  
EXPORTS  
    initParticleDesigner  

(4) プロジェクトのプロパティ - 構成プロパティ - リンカー - 入力

モジュール定義ファイル に (3) で作成した def ファイルを追加します。

ここまででビルドして DLL を作成しておきます。

3-2. C# 側の実装

(1) DLL およびリソースの配置

3-1.で生成された DLL と必要な画像ファイルを exe と同じフォルダに配置します。
glew32.dll
iconv.dll
libcocos2d.dll
libcurl.dll
libtiff.dll
pthreadVCE2.dll
zlib1.dll
ParticleDesigner.dll

(2) DLL の呼び出し

ボタンクリック時に DLL を呼び出すようにしてみます。(GUI まわりのコードは割愛します)

        private void Button_Click(object sender, RoutedEventArgs e)  
        {  
            initParticleDesigner();  
        }  
  
        [DllImport("ParticleDesigner.dll")]  
        private extern static void initParticleDesigner();  

これで、ボタンをクリックすると Cocos2d-x の画面が表示されます。

4. C# から Cocos2d-x のプロパティの操作

次に C# 側から CCParticleSystem のプロパティを操作できるようにします。

4-1. CCParticleSystem の準備

まずはパーティクルを表示できるようにします。
既存のパーティクルを参考に、適当にプロパティを設定しましょう。
今回はこちらのパーティクルを参考にしました。

(1) HelloWorldScene.cpp

bool HelloWorld::init()  
{  
    if (!CCLayer::init())  
        return false;  
  
    CCParticleSystem* particle = CCParticleSystemQuad::createWithTotalParticles(200);  
    particle->setAngle(90.0f);  
    particle->setAngleVar(20.0f);  
    ccBlendFunc blend = { 770, 1 };  
    particle->setBlendFunc(blend);  
    particle->setDuration(-1.0f);  
    particle->setEmitterMode(0);  
    particle->setEndColor(ccc4f(1.0f, 0.9f, 0.6f, 0.0f));  
    particle->setEndColorVar(ccc4f(0.0f, 0.0f, 0.0f, 0.0f));  
    particle->setEndSize(150.0f);  
    particle->setEndSizeVar(326.7f);  
    particle->setGravity(ccp(0.0f, 50.0f));  
    particle->setLife(1.0f);  
    particle->setLifeVar(0.0f);  
    particle->setRadialAccel(0.0f);  
    particle->setRadialAccelVar(0.0f);  
    particle->setEndSpin(140.0f);  
    particle->setEndSpinVar(0.0f);  
    particle->setStartSpin(330.0f);  
    particle->setStartSpinVar(-230.0f);  
    particle->setSourcePosition(ccp(320.0f, 60.0f));  
    particle->setPosVar(ccp(50.0f, 0.0f));  
    particle->setSpeed(500.0f);  
    particle->setSpeedVar(140.0f);  
    particle->setStartColor(ccc4f(1.0f, 0.2f, 0.0f, 1.0f));  
    particle->setStartColorVar(ccc4f(0.0f, 0.0f, 0.0f, 0.5f));  
    particle->setStartSize(100.0f);  
    particle->setStartSizeVar(50.0f);  
    particle->setTangentialAccel(50.0f);  
    particle->setTangentialAccelVar(320.0f);  
    particle->setEmissionRate(particle->getTotalParticles() / particle->getLife());  
    particle->setTexture(CCTextureCache::sharedTextureCache()->addImage("snow.png"));  
    this->addChild(particle);  
  
    this->particle = particle;  
  
    return true;  
}  

この状態でビルドして先ほどの DLL と差し替えると、ちゃんとパーティクルが表示されています。

4-2. インスタンスの取得

DLL の呼び出しは static メソッドになりますので、CCParticleSystem のインスタンスにアクセスできるようにしてあげる必要があります。
ここでは CCParticleSystem のインスタンスを直接返すのではなく HelloWorldScene 経由でアクセスするようにしました。

(1) HelloWorldScene.h

class HelloWorld : public cocos2d::CCLayer  
{  
private:  
    static HelloWorld* instance;  
  
public:  
  
    // (省略)  
  
    static HelloWorld* getInstance();  
};  

(2) HelloWorldScene.cpp

bool HelloWorld::init()  
{  
    if (!CCLayer::init())  
        return false;  
  
    instance = this;  
  
    // (省略)  
  
    return true;  
}  
  
HelloWorld* HelloWorld::getInstance()  
{  
    return instance;  
}  

4-3. プロパティ変更メソッドの準備

HelloWorldScene にパーティクルのプロパティを変更するメソッドを作り、3-1.と同様に C# 側から呼び出せるようにエントリポイントを用意してあげます。

(1) HelloWorldScene.h

class HelloWorld : public cocos2d::CCLayer  
{  
public:  
  
    // (省略)  
  
    void setAngle(float angle);  
};  

(2) HelloWorldScene.cpp

void HelloWorld::setAngle(float angle)  
{  
    this->particle->setAngle(angle);  
}  

(3) ParticleDesigner.h

class ParticleDesigner  
{  
public:  
  
    // (省略)  
  
    static __declspec (dllexport) void __stdcall setParticleAngle(float angle);  
};  

(4) ParticleDesigner.cpp

void __stdcall ParticleDesigner::setParticleAngle(float angle)  
{  
    HelloWorld::getInstance()->setAngle(angle);  
}  

(5) ParticleDesigner.def

LIBRARY "ParticleDesigner.dll"  
EXPORTS  
    initParticleDesigner  
    setParticleAngle  

4-4. C# 側の実装

C# 側も 3-2.と同様に。ここではスライダの入力に応じてプロパティを変更するようにしてみました。

        private void Button_Click(object sender, RoutedEventArgs e)  
        {  
            setParticleAngle((float)this.angle.Value);  
        }  
  
        [DllImport("ParticleDesigner.dll")]  
        private extern static void setParticleAngle(float angle);  

スライダの値に応じてパーティクルの向きが変化するようになりました。

あとは他のプロパティについても同様に実装してあげれば OK です。
設定したプロパティ値を plist に書き出してあげれば、作ったパーティクルを他のプログラムで使うことも可能です。(plist の編集は CCParticleSystem の initWithDictionary を参考にするとよいでしょう)

余談. 構造体の受け渡し

他のプロパティも上記同様に実装すれば OK ……なのですが、なかには Color や Position のように構造体を引数にとるものがあります。
その場合は C# 側で以下のように指定してあげれば OK です。

        [StructLayout(LayoutKind.Sequential)]  
        private struct CCColor4F  
        {  
            public float r;  
            public float g;  
            public float b;  
            public float a;  
        }  
  
        [DllImport("ParticleDesigner.dll")]  
        private extern static void setParticleEndColor(CCColor4F endColor);  
  
        public void SetColor(Color value)  
        {  
            var ccColor = new CCColor4F()  
            {  
                r = value.R / 255,  
                g = value.G / 255,  
                b = value.B / 255,  
                a = value.A / 255  
            };  
  
            setParticleEndColor(ccColor);  
        }