Cocos2d-xのゲームの構造がどのようになっているか紐解いてみる
Cocos2d-xでゲームの開発を始めてそろそろ5ヶ月たちます。とにかく作ってみるといった感じで基礎知識があるかもも怪しいまま(Scene と Layer の違いがわからんとか)開発を続けていました。最近になってiOS/Androidとのネイティブ連携ではまったり、iOSだと問題なく動くのにAndroidだとクラッシュしたり、BGMの制御うまくいかなかったり、まあいろいろと問題が出てきました。というわけでここらで一度Cocos2d-xとちゃんと向き合ってみるかというわけで過去に書いたこちらの記事「iOS アプリの構造がどのようになっているか紐解いてみる」と同じノリでCocos2d-xで作ったゲームの構造を紐解いてみたいと思います。
前提とする Cocos2d-x のバージョンとかもろもろ
Cocos2d-xはiOS/Androidはもちろんのこと、その他多数のプラットフォームに対応したゲームエンジンです。この記事ではスマホゲームを前提にしますのでiOSとAndroid上でゲームを開発することを前提に書きます。
この記事で対象とするCocos2d-xやiOS、Androidのバージョンは以下の通りです。
Cocos2d-x プロジェクトのフォルダ構成
クラス構成について説明をする前に Cocos2d-x で作成したプロジェクトのフォルダ構成について説明します。ゲーム自体に直接関係するクラスとプラットフォーム依存のクラスの置き場が違って少し複雑なので理解しておいたほうがなにかと良いです。以下は Cocos2d-x で作成したプロジェクトのフォルダ構成一覧になります。
シーンとレイヤーが1つの単純なゲームの構造
シーンとレイヤーが一つでスプライトもなにもなく、ただ真っ黒な画面を表示するだけのゲームがどのような構造になっているのか見てみましょう。以下クラスの関係を図にしたものです。
iOSとAndroidの違いを吸収しているのが Application(Application-Android) クラスと GLViewImpl(GLViewImpl-Android) クラスです*1。図の中の赤色のクラスです。この2つのクラスがプラットフォームごとに用意されています。プラットフォーム固有のクラスが多いだけでCocos2d-x自体は割とシンプルなつくりだというのがわかると思います。
図の中の各クラスは以下のように4つに分類できます。
- ゲームエンジンクラス(プラットフォームに依存しないクラス)
- 各プラットフォームから提供されているクラス
- Cocos2d-x が各プラットフォームのために用意している拡張クラス
- Cocos2d-x がプロジェクト作成時に自動生成するクラス
それでは分類ごとにクラスの説明をしていきます。
ゲームエンジンクラス
Cocos2d-x のコアとなるゲームエンジン部分のクラスです。この分類のクラスはプラットフォームに依存していません。iOS 向けゲームでも Android 向けゲームでも同じです。実際にゲームを開発するとなるとここで紹介するクラス以外に Sprite クラスや Action クラスとその派生クラスなんかも必要になります。
GLView クラス
Cocos2d-x のゲームは OpenGL を使って描画をしています。その OpenGL を使ったビューを管理しているクラス。抽象クラスになっていて、プラットフォーム依存しない機能は実装があり、プラットフォームに依存する部分はメソッドの定義のみされています。プラットフォームごとに依存する部分は後述の GLViewImpl クラスに実装されています。
Director クラス
シーンとイベントを管理するためのクラス。Cocos2d-x の心臓部。シングルトンクラスなのでどこからでもオブジェクトを取得できます。シーンの遷移(画面遷移みたいなもの)だったりカスタムイベントの追加だったりするときに使います。
DisplayLinkDirector クラス
Director クラスから派生したクラス。タイマーと同期するための機能が提供されています。Director::getInstance メソッドで取得できるオブジェクトはこのクラスのオブジェクトです。
Node クラス
画面上のオブジェクトのツリー構造を管理するためのクラス。後述の Scene クラスや Layer クラスの他、画面表示に必要な機能を提供するあらゆるクラス(Sprite クラスや UI 系クラス)の親クラスになります。
Scene クラス
ゲームの場面を表すクラス。シーン単位でゲームの画面を管理するのが普通で、マップ、町の中、バトル、ステージ選択、ローディングなどゲームの見せ方が変わる単位でシーンを作成します。1画面で表示されるシーンは1つになります。なので画面上のノードオブジェクトのツリー構造をたどっていくと一番根っこにシーンオブジェクトが存在することになります。
Layer クラス
レイヤーを表すクラス。レイヤーというのはノードをグループ化するための概念だと考えると良いと思います。特定のZオーダーをまとめて管理したい時(モーダルでノードを表示するみたいな)やタッチイベントを制御するときなんかに使います。
各プラットフォームから提供されているクラス
iOS や Android 向けのアプリ開発をしている方には馴染みのあるクラスばかりだと思います。iOS/Android に関わらず画面を1枚表示するための必要最低限の機能だけ使っている印象です。Android は OpenGL を使った描画に色々とお作法があり、その辺りが少し複雑な印象です。
UIApplicationDelegate インターフェース(iOS)
アプリから通知されるライフサイクルに関する(アプリを立ち上げたとか、バックグラウンド状態になったとか、そこから復帰したとか)メソッドが定義されているインターフェース。
UIViewController クラス(iOS)
画面に表示しているビューオブジェクト(UIview オブジェクト)を監視する機能と他のビューコントローラに遷移させる機能を提供するクラス。Cocos2d-x でゲームを作る場合、後者の機能(画面遷移)は基本的には使いません。
UIView クラス(iOS)
画面に矩形領域を表示するための機能を提供するクラス。
FrameLayout クラス(Android)
画面レイアウトを表すクラス。Android では幾つかのレイアウト方法がありその方法ごとにレイアウトクラスが存在します。FrameLayout クラスは画面に1つのビューを表示する場合に使用するレイアウトです。
SurfaceView クラス(Android)
アニメーションとかゲームのような描画のパフォーマンスが必要なビューを作成するためのクラス。このクラスを使うと描画用のスレッドを持った View を作ることができます。
Cocos2d-x が各プラットフォームのために用意している拡張クラス
各プラットフォーム上で OpenGL の描画を行うための機能を提供してくれるクラス群(CCEAGLView、Cocos2dxGLSurfaceView、Cocos2dxRenderer、Cocos2dxActivity クラス)と iOS/Android の違いを吸収しているクラス群(Application、GLViewImpl)があります。
CCEAGLView クラス(iOS)
UIView クラスを継承した OpenGL の描画を行うための機能を提供してくれるクラス。ゲームをマルチタッチに対応させるときはこのクラスのオブジェクトの setMultipleTouchEnabled メソッドを使います。
Cocos2dxRenderer クラス(Android)
GLSurfaceView.Renderer インターフェースを実装したクラス。onSurfaceCreated メソッドを実装して Cocos2d-x 側のゲーム初期化処理を呼び出したり、onDrawFrame メソッドを実装して Cocos2d-x のゲームループを呼び出したり、画面のタッチを Cocos2d-x 側に通知したり、アプリのライフサイクルイベント(アプリが起動した、サスペンドした、サスペンドから復帰したなど)を Cocos2d-x 側に通知したり、など JNI(Java Native Interface) を利用して Cocos2d-x とやりとりするための機能を提供してくれています。
Cocos2dxRenderer_nativeInit 関数(Android)
あまりにも長いので省略していますが、正式な関数名は Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit です。JNI 呼び出しで実行される関数です。クラス図では便宜上クラスとして独立させていますが実際は Cocos2dxRenderer クラスの nativeInit メソッド(static メソッド)の実態です。
Cocos2dxActivity クラス(Android)
Activity クラスを継承したクラス。Cocos2d-x のライブラリを読み込んだり、ビューをセットしたり、OpenGL のせってをしたり、Cocos2d-x のゲームに必要な下準備をするための機能を提供してくれます。
Application クラス(クラス名共通、実装別)
クラス図が複雑になるので省略しましたが iOS 版 Android 版ともに ApplicationProtocol クラスというアプリのライフサイクルに関わるイベントメソッドが定義されている抽象クラス(C++なんでクラスとしていますが実質インターフェースです)を継承しています。このクラスの run メソッドを使うと applicationDidFinishLaunching メソッドが呼ばれアプリの起動が Cocos2d-x 側に伝達される仕組みになっています。iOS 版と Android 版の違いは run メソッドの中でゲームループを開始させるかどうかです。iOS 版は run メソッドの処理の中でゲームループを開始させているのに対し、Android 版は run メソッドの中でゲームループに関する処理を一切していません(Android のゲームループは GLSurfaceView が行うので必要ないのです)。
GLViewImpl クラス(クラス名共通、実装別)
GLView クラスを継承したクラス。バージョン3.3から導入されたようです。実装はだいたい同じなのですが、IME キーボードの実装だったり、ビューの初期化方法だったり、が違っています。
Cocos2d-x がプロジェクト作成時に自動生成するクラス
プロジェクト作成時に Cocos2d-x が自動生成してくれるクラスです。AppDelegate と HelloWorldScene クラスに実際のゲームのコードを書いていきます。それ以外のクラスはネイティブ連携(SNS 連携、広告表示、課金など)をするときの拡張ポイントとして使います。
AppController クラス(iOS)
UIApplicationDelegate プロトコル(インターフェース)を実装したクラス。アプリのライフサイクル(アプリが起動した、サスペンドした、サスペンドから復帰したなど)に関するイベントを受け取る機能を持っています。このクラスの application:didFinishLaunchingWithOptions: メソッドの中でウインドウやビューコントローラー、ビューの生成をしています。ネイティブ連携(SNS 連携、広告表示、課金など)の機能を追加するときに使います。
RootViewController クラス(iOS)
UIViewController クラスを継承したクラス。自動生成されるだけで特別なことはほとんど何もしていません。拡張ポイントとして用意されていると考えると良いと思います。ネイティブ連携は基本 AppController クラスに実装していくのですがたまにこのクラスを拡張するときがあります(Game Center を使うときなど)。
AppActivity クラス(Android)
Activity クラスを継承したクラス。自動生成されるだけで特別なことはほとんど何もしていません。拡張ポイントとして用意されていると考えると良いと思います。ネイティブ連携(SNS 連携、広告表示、課金など)の機能を追加するときに使います。
AppDelegate クラス(共通)
アプリのライフサイクルに関するイベントを受け取る機能を持ったクラス。ライフサイクルイベントは iOS の場合 AppController オブジェクト、Android の場合 Cocos2dxRenderer オブジェクトから通知されます。
HelloWorldScene クラス(共通)
Scene とついていますが実際は Layer クラスを継承したクラスです。Scene オブジェクトの生成機能を持っています。クラス名がわかりにくいので変更することをお勧めします。自分の場合、このややこしいクラス名のおかげで Scene と Layer の違いがわからず苦労しました。