トップページへ戻る
つくれる?iPhoneアプリ
cocos2dを使ったiPhoneアプリ制作を解説してみる

cocos2d デフォルトのサンプルの解説


どのページでも
ご自由にリンクしてください。

感想ご要望はお気軽に!
info@taskinteractive.com


■順番に解説
・導入
その1
導入


その2
HelloWorld


その3
わからない単語でも安心


その4
いじるファイルと画面構成


その5
ソースにある単語を手探る


その6
実行の順を追って手探る


その7
クラスの名前と素性を手探る


・サンプル1
その8 ボール遊び1
Scene/Layer/Sprite


その9 ボール遊び2
ファイル名を変えてみる


その10 ボール遊び3
ソースをいじってみる



■項目ごとに解説
クラスの解説
その1 クラスって何?


クラスの解説
その2 インスタンスって何?


クラスの解説
その3 オブジェクト指向って何?



クラスの解説
キリッと クラスの書式1




cocos2d HelloWorldScene
cocos2dデフォルトのサンプルを解説


企画・販売元タスクインタラクティブ


cocos2dデフォルトのサンプルHelloWorldを解説してみる


cocos2d のサンプルで、HelloWorld が表示されることと、その表示位置や内容を変えられることは その2 HelloWorld で解説しました。



では、その表示がどのような手順で行われているでしょうか。

それを全部最初から理解しようとすると混乱する恐れがありますが、ある程度Objective-Cの学習が進んだ時点までくると、今度はその流れを理解することが、次のステップにつながります。


ですので、この項目は、自分で「もう少し理解を進めるために必要」と思ったタイミングで読んでください。





■最初に躓かないために



まず、最初につまづきそうなのが、クラスメソッドとして定義されている +(id) scene です。

これは HelloWorld クラスの本来の役割とはあまり関係がないのですが、最初に出てくるのですごく重要な気がしてしまいますが、実はそうでもありません。

そのことをちょっと頭の片隅に置いて読んでみてください。





■cocos2dAppDelegate.m
※プロジェクトを cocos2d という名前で新規作成した場合


cocos2dAppDelegate.m では、scene とともに Director を起動している。

[[Director sharedDirector] runWithScene: [HelloWorld scene]];

HelloWorld クラスは、scene ではなく、 Layer のクラスですね。




■HelloWorldScene.h/.m


HelloWorldScene.h を見ると、こう書いてあったはずです。


@interface HelloWorld : Layer



なんで”Scene”クラスのインスタンスオブジェクトとともに起動と書いてあるのに”Layer”クラスのクラスメソッドにメッセージを送ってるの?と思うかもしれません。

それにはこんなからくりがあります。


HelloWorld のクラスメソッド +(id) scene で

 1)scene クラスのインスタンスをscene というインスタンス名で生成(インスタンス化)して

 2)HelloWorldクラスのインスタンスをlayerというインスタンス名でスタンス化して

 3)インスタンスオブジェクト scene に、addChiled で layer というインスタンスオブジェクトを設置

 4)最後に return scene; で、”Sceneクラスのインスタンスオブジェクト”である scene を返している

ということで、HelloWorld というクラスは、Layer クラスを継承しているのですが、クラスメソッドである +(id) scene; は、Layer クラスのインスタンスオブジェクトを返すためではなく、 Scene クラスのインスタンスオブジェクトを返すためにある、ということです。


Scene は、画面を構成するオブジェクトの階層でいうと、 Scene → Layer → Sprite とLayerの上位にあたるため、矛盾というか、内包関係がおかしくなる気がしますが、それはありません。

というか、クラスメソッドはどこのクラスに定義しておいても、そのクラスとは全く切り離して動作させられるため、書き方によっては、単に小さなルーチンがたまたまどこかのクラスにクラスメソッドとして定義されていた、という書き方も出来ます。

それは、例えば次のようにすればわかります。







■試しに別のクラス testScene を作ってみる



試しに、「testScene」というクラスを作りましょう。

ファイルの追加で、「testScene.m/testScene.h」というファイルを作ります。


.hにクラスメソッドを宣言て、こうします。


#import <Foundation/Foundation.h>

@interface testScene : NSObject {


}

+(id) scene;

@end



.m には、そのクラスメソッドの処理を定義します。


#import "testScene.h"


@implementation testScene

+(id) scene

{

Scene *scene = [Scene node];

HelloWorld *layer = [HelloWorld node];

[scene addChild: layer];

return scene;

}

@end



最後に、cocos2dAppDelegate.m に以下を追加します。


#import "testScene.h"


と、

[[Director sharedDirector] runWithScene: [HelloWorld scene]];

この行をコメントアウトして、

[[Director sharedDirector] runWithScene: [testScene scene]];

直後にこの行を追加


こんな感じに並びます。

//[[Director sharedDirector] runWithScene: [HelloWorld scene]];

[[Director sharedDirector] runWithScene: [testScene scene]];




■実行してみる


ビルドすると、4つのエラーが出ると思います。

これは、メッセージを送ったクラス testScene に記述のあるcocos2d 関係のクラス(Scene と Layer)が読み込まれていないからですね。

なので、 testScene.h に、以下を追記します。


#import "cocos2d.h"


もう一度ビルドしてみてください。

今度は、エラーが2つに減ったと思います。


ここで出ているエラーは、HelloWorld というクラスが認識できないよ、という感じです。


なので、それを宣言している HelloWorldScene.h をインポートします。


testScene.m に、下記を追加してください。


#import "HelloWorldScene.h"



これで、問題なく実行されたと思います。



今行ったのは、

a)NSobject を継承して、testScene というクラスを作る。

b)そのクラスに cocos2d のクラスと、HelloWorld のクラスを読み込ませる。

c)testScene クラスに次の処理をするクラスメソッドを定義する。

 1)scene クラスのインスタンスをscene というインスタンス名で生成(インスタンス化)して

 2)HelloWorldクラスのインスタンスをlayerというインスタンス名でスタンス化して

 3)インスタンスオブジェクト scene に、addChiled で layer というインスタンスオブジェクトを設置

 4)最後に return scene; で、”Sceneクラスのインスタンスオブジェクト”である scene を返している

d)cocos2dAppDelegate.m から testScene のクラスメソッド +(id) scene にメッセージを送る。


ということです。

上記の1)?4)は、HelloWorld のクラスメソッド +(id) scene の解説と全く同じですし、事実、クラスメソッド内の記述も全く同じです。

+(id) scene

{

Scene *scene = [Scene node];

HelloWorld *layer = [HelloWorld node];

[scene addChild: layer];

return scene;

}










クラスメソッドは、あくまでクラスに渡されたメッセージに対して応答しますが、インスタンス化されたら意味がありません。

無いものに出来ます。


なのでここでは、そのクラスに関係するちょっとした処理を、クラスメソッドとしてとして定義されていた、ぐらいに思っておいてください。

つまり、+(id) scene 別に HelloWorldScene.h/.m に HelloWorld クラスのクラスオブジェクトとして定義されている必要は無く、ほかのどこかにあってもよかったけれど、たまたまここに定義されていた。

という感じです。 


大事なのは、単に

cocos2dAppDelegate.m の

[[Director sharedDirector] runWithScene: [HelloWorld scene]];

ここで、Scene のインスタンスオブジェクトが必要だったということです。







■ +(id) scene の中身は?



次に、そのクラスメソッド内でどのように処理をしているかは、上記の1)〜4)で書いた通りですが、それを具体的に見てみましょう。



Scene *scene = [Scene node];

 Scene クラスのインスタンスオブジェクトを scene という名前でつくります。

 これをSceneクラスをインスタンス化する、と言います。


HelloWorld *layer = [HelloWorld node];

 HelloWorld クラスを layer という名前でインスタンス化します。


[scene addChild: layer];

 インスタンスオブジェクトの scene に インスタンスオブジェクトの layer を関連づけます。

 この関連づける、というのはObjective-Cの機能ではなく、cocos2dの画面操作のための機能です。

 (それも含めて後の解説を読んでください。)


return scene;

 最後に、Scene クラスのインスタンスオブジェクトである scene を返しています。





■ [***** node] っていうメッセージの意味は?




上記で出てきた、Scene とLayer をインスタンス化する

[+++++ node];

というメッセージですが、本来、インスタンス化してから初期化する必要があるはずです。

つまり、


Scene *scene = [Scene alloc];

[scene init];


こんな感じで書いて、インスタンス化 → 初期化 しなければならないところを、なぜ [++++ node] となっているか、疑問が出てきます。

この疑問を解決するために、 「その7 クラスの名前と素性を手探る」の手順に従って、Scene とLayer のクラスの継承を逆にたどっていくと、 CocosNode というクラスにたどり着きます。

そこに、

+(id) node

{

return [[[self alloc] init] autorelease];

}

こういうメソッドが定義されているのが見つかるはずです。


このメソッドはクラスメソッドがインスタンスメソッドかは、もうお分かりですよね。

+で始まっているので、クラスメソッドです。

つまり、インスタンス化する前にメッセージを送ることが出来るメソッドです。


ここで、 self つまり、cocosNode インスタンス化をして、さらに init で初期化をしています。


つまり、

Scene *scene = [Scene alloc];

[scene init];

を一度に出来るようにしています。




なぜわざわざそうしているのかは、おそらくなのですが、cocos2d の扱うノード(オブジェクト)については、必ず autorelease を適用して、確実にインスタンスの解放を管理したいからではないかと思います。

別のところで説明しますが、インスタンスオブジェクトを作るたびに、必要なメモリが確保されます。

それが、多数の画像を扱うcocos2dのような仕組みを使用していると、すぐに画像を扱うSpriteの様なオブジェクトがが増えてメモリを圧迫してしまいます。

なので、使わなくなったインスタンスオブジェクトには、  release のメッセージを送ってそのインスタンスオブジェクトを削除し、メモリの解放をしなければならないのですが、もう一つ、autoreleaseで解放の管理をする宣言が出来るのです。

ここでは、その宣言をしています。



あと、何となくお気づきかもしれませんが、先ほどから何度も出てきている、クラスオブジェクトとインスタンスオブジェクトに関連することで、

+(id) node と同様

+(id) alloc というのもクラスメソッドですね。

インスタンス化する前に送るメッセージですので、当然受け取るのはインスタンスメソッドではなく、クラスメソッドです。


それは、すべての Objective-C の親オブジェクトのルーツである、 NSObject のヘッダーファイルを見れば書いてあります。

NSObject のヘッダーファイルは、グループとファイルの、Frameworks → Foundation.frameworks → Headers の中にあります。

ちょっとのぞいてみてください。



ということで、cocos2dデフォルトのサンプルで何が行われているかを解説しました。

理解の一助になればと思います。