jarinosuke blog

about software engineering, mostly about iOS

cocos2dで簡単なボタンを作ろうぜチュートリアル

f:id:jarinosuke0808:20100818222359p:image:right

Thanks to Ray!!!

今回の記事も大まかな流れや、画像などはRayの記事「How To Create Buttons in Cocos2D: Simple, Radio, and Toggle」を参考に書かせてもらいました。

はじめに

cocos2dでゲームを作ってる最中、「これボタンみたいなの必要じゃん」って時ありますよね?
このチュートリアルは手取り足取りcocos2dでのボタンの作り方を説明していきます。
はじめは単純なボタンの作成からはじまって、トグルボタンであったり最後はラジオボタンも作っていきます。
もしあなたがcocos2dを用いてiPhone上で至極シンプルなゲーム作成チュートリアルをこなしていないのであれば、そちらを先にやるのをお薦めします。

始める前に、もしかしたらあなたが疑問に持っている一つの事についてお答えしたいと思います。
ずばり、それは「別にcocos2dでやらなくても、UIButtonで良いんじゃねーの?」です。答えは「NO!」です。
具体的なメリットを以下に挙げていきます。

  • cocos2dにはMenuクラスというものがあって、それが各Buttonクラスのラッパーのようなまとめ役になってくれる。
  • cocos2dにはUIButtonというただ一つのButtonクラスだけではなく、Toggleクラスであったりがある。
  • Layerを大量に貼り付けないで済むのでメモリに優しい。

まぁ簡単に言うとクラスがたくさん整備されてるということですね!
言うは易し、行うは難し。とにかくコードを書いていきましょう〜。

まずは単純なボタンから。

Xcodeを開いて新しいプロジェクトをcocos2dのテンプレートを使って立ち上げましょう、プロジェクトの名前は"CCButtons" にしましょうか。
次にいくつかボタンの画像を用意しなくてはいけません。Rayの作ったzipファイルのリンクがあるので、使わせてもらいましょう。

画像を手に入れたらプロジェクトの中にあるResourcesフォルダにD&Dしましょう、"Copy items into destination group's folder (if needed)"にチェックも忘れずに。

Classesフォルダの中にあるHelloWorldScene.hを開いて、HelloWorldクラスに変数を加えましょう。

CCLabel *_label;

そうしたらHelloWorld.mに移り、忘れてしまう前にdeallocメソッドに以下のリリースを加えましょう。

[_label release];
_label = nil;

完璧です。次に同じファイル(HelloWorldScene.m)の中にある、initメソッドを以下のコードと置き換えて下さい。

-(id) init {
     if( (self = [super init] )) {
          CGSize winSize = [[CCDirector sharedDirector] winSize];
         
          //Create a label for display purpose
          _label = [[CCLabel labelWithString:@"Last button: None"  dimensions:CGSizeMake(320, 50) alignment:UITextAlignmentCenter fontName:@"Arial" fontSize:32.0] retain];
          _label.position = ccp(winSize.width / 2,  winSize.height - (_label.contentSize.height / 2));
          [self addChild:_label];

          //Standard method to create a button
          CCMenuItem *starMenuItem = [CCMenuItemImage itemFromNormalImage:@"ButtonStar.png" selectedImage:@"ButtonStarSel.png" target:self selector:@selector(starButtonTapped:)];
          starMenuItem.position = ccp(60,60);
          CCMenu *starMenu = [CCMenu menuWithItems:starMenuItem, nil];
          starMenu.position = CGPointZero;
          [self addChild:starMenu];

          }
          return self;
          }
}

一番はじめにデバッグ用にラベルを作っています。これはみんな良くやるよね!
次にボタンを作っていきます。まず最初にCCMenuItemImageクラスを使ってイメージや選択された時のイメージを指定してボタンを作成しています。
MenuItemができたら、タップされたあとに行動を起こすためのコールバック関数も作らないといけませんね、それは後でやります。
最後はボタンを含ませるためのメニューを作っています。

コールバック関数は以下のように定義しています、これはボタンがタップされたら呼び出されます。

- (void)starButtonTapped:(id)sender {
     [_label setString:@"Last button: * "];
}

コンパイルして動かしてみよう。こんな風になるはずです。
f:id:jarinosuke0808:20100818222531p:image

次はトグルボタンよ!

他に有名なボタンでiPhone用のゲームで必要になりそうなものっていったらトグルボタンが真っ先に頭に浮かんでくるよね。
どういうボタンかって言うと、さっきと同じようにイメージがあって、君が一回タップするとイメージが変わるんだ。
で、またタップすると前のイメージに戻る。要するに凹むボタンだ!
幸運にもcocos2dではCCMenuItemToggleという素晴らしいクラスがあるおかげで、いとも簡単に実装できる。
じゃあコードを書いていこう。まずはHelloWorldScene.hにクラス変数を二つ加えよう。

CCMenuItem *_plusItem;
CCMenuItem *_minusItem;

で忘れないように、deallocメソッドにも解放するためのコードをね。

[_plusItem release];
_plusItem = nil;
[_minusItem release];
_minusItem = nil;

次に、以下のコードをinitメソッドの中でさっきのstartMenuの後に貼り付けよう。

_plusItem = [[CCMenuItemImage itemFromNormalImage:@"ButtonPlus.png" selectedImage:@"ButtonPlusSel.png" target:nil selector:nil] retain];

_minusItem = [[CCMenuItemImage itemFromNormalImage:@"ButtonMinus.png" selectedImage:@"ButtonMinusSel.png" target:nil selector:nil] retain];

CCMenuItemToggle *toggleItem = [CCMenuItemToggle itemWithTarget:self selector:@selector(plusMinusButtonTapped:) items:_plusItem, _minusItem, nil];

CCMenu *toggleMenu = [CCMenu menuWithItems:toggleItem, nil];
toggleMenu.position = ccp(60, 120);
[self addChild:toggleMenu];

まずはじめに、二つのCCMenuItemImageを作っています。そして、それらをCCMenuItemToggleに貼り付けています。
このクラスはとても優秀なので、内部でトグルを勝手に実装してくれます。
ちなみにCCMenuImageの二つにはコールバック関数にnilを設定しましたが、CCMenuToggleにはコールバック関数をしっかり設定していますね。
このように各ボタン全てにコールバック関数を与えずにラッパークラスに与えることによって、コードを綺麗に保つことができます。

では、そのコールバック関数をみてみましょう!initメソッドの終了後に付け足して下さい。

- (void)plusMinusButtonTapped:(id)sender {
     CCMenuItemToggle *toggleItem = (CCMenuItemToggle *)sender;
     if (toggleItem.selectedItem == _plusItem) {
          [_label setString:@"Visible button: +"];
     }else if (toggleItem.selectedItem == _minusItem) {
          [_label setString:@"Visible button: -"];
     }
}

見ておわかりのように、CCMenuItemToggleは今現在どのアイテムが選択されているかを格納しているselectedItemプロパティを持っています。
選択されているものであって、さっきタップしたものではないので注意しよう!
コンパイルして以下のような画面になれば問題無いです。
f:id:jarinosuke0808:20100818222532p:image

最後はラジオボタン作っちゃおうぜ!!!

3つ目に必要としてるのは恐らくラジオボタンだよね。ゲームを作っている人なら分かってくれるはずだ。
ただ、そうは思ってもcocos2dのソースの中にはラジオボタンのクラスなどは無い。だから自分自身で作ってみよう。
この記事を書いている時点でcocos2dにラジオボタンイシューとして扱われています。もうすぐソースに組み込まれるかもしれませんね。
ただ、現状はまだ無いので書いていきましょう。このチュートリアルの目的のために、ラジオボタンを自分で実装しましょう。
まず初めにCCRadioMenu.hとCCRadioMenu.mをダウンロードして、Classesフォルダに加えましょう。そうしたら、HelloWorldScene.mに以下のimport文を加えましょう。

#import "CCRadioMenu.h"

そしてinitメソッド中のトグルボタンを作った後に以下のコードを加えましょう。

CCMenuItem *menuItem1 = [CCMenuItemImage itemFromNormalImage:@"Button1.png" selectedImage:@"Button1Sel.png" target:self selector:@selector(button1Tapped:)];

CCMenuItem *menuItem2 = [CCMenuItemImage itemFromNormalImage:@"Button2.png" selectedImage:@"Button2Sel.png" target:self selector:@selector(button2Tapped:)];

CCMenuItem *menuItem3 = [CCMenuItemImage itemFromNormalImage:@"Button3.png" selectedImage:@"Button3Sel.png" target:self selector:@selector(button3Tapped:)];
CCRadioMenu *radioMenu = [CCRadioMenu menuWithItems:menuItem1, menuItem2, menuItem3, nil];
radioMenu.position = ccp(120, 180);
[radioMenu alignItemsHorizontally];
radioMenu.selectedItem = menuItem1;
[menuItem1 selected];
[self addChild:radioMenu];

前例と同じようにCCMenuItemImagesを作り、それらをCCMenuの代わりにCCRadioMenuに貼り付けました。
このクラスは常に一つのボタンだけが選択されている状態にしてくれます。
なので、デフォルトでどのアイテムが洗濯されているかを指定しないといけません。

他に新しいところはcocos2dのmenuクラスにある自動レイアウトをしてくれるalignItemsHorizontallyです。
これはmenuの真ん中から自動的にレイアウトを計算してくれます。

もう一つ付け加えるものがありますね、そう、コールバック関数です。

- (void) button1Tapped:(id)sender {
     [_label setString:@"Last  button: 1"];
}

- (void) button2Tapped:(id)sender {
     [_label setString:@"Last  button: 2"];
}

- (void) button3Tapped:(id)sender {
     [_label setString:@"Last  button: 3"];
}

こんな風になったかな?しっかり他のボタンをタップすると今選択されていたボタンはキャンセルされるよね。
f:id:jarinosuke0808:20100818222533p:image

シーンの裏

もしMenu Systemの実装を見ているなら、君はMenu ItemsがCCNodeで作られているけれどMenuはCCLayerで作られていることに気付いたかもしれない。
cocos2d class referenceによれば、Layerによる巨大な階層構造を作らずに、なるべく少なくそれを保つためにそうしているそうだ。
だから、たくさんのMenu Itemを一つのMenuに付けたとしても、それは一つのLayerでしかないので大したことは無い。

これで終わり!

これを何かに活かしてくれることを祈っています。もし、他のクールなcocos2dに関するButtonの使い方を知っていたら教えて下さい!