![]() |
つくれる?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 クラスの本来の役割とはあまり関係がないのですが、最初に出てくるのですごく重要な気がしてしまいますが、実はそうでもありません。 そのことをちょっと頭の片隅に置いて読んでみてください。 ※プロジェクトを cocos2d という名前で新規作成した場合 cocos2dAppDelegate.m では、scene とともに Director を起動している。 [[Director sharedDirector] runWithScene: [HelloWorld scene]]; HelloWorld クラスは、scene ではなく、 Layer のクラスですね。 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.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 のインスタンスオブジェクトが必要だったということです。 次に、そのクラスメソッド内でどのように処理をしているかは、上記の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 を返しています。 上記で出てきた、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デフォルトのサンプルで何が行われているかを解説しました。 理解の一助になればと思います。 |