普通のiOSからcocos2dを使ってみる

cocos2d Advent Calendarに参加しました。
2011/12/21の担当です。
前の記事:@kanta:cocos3dをさわってみた

■概要
UIKitを使って簡単にcocos2d画面にUIを配置して使ってみる。
しかもXIBで、レイアウトも簡単だったら良いよね、ということでやってみた。
cocos2dからUIKitを使うと、座標云々で面倒である。
なので、普通のiOSからcocos2dを使う方法を取ってみた。
 
■だが、まずはcocos2d_box2dプロジェクト作成
そろそろHelloWorldではつまらないので、box2dでプロジェクトを作成してみる。
タイトル画面に使う適当な背景画像(haikei.jpg)を用意し、横画面対応にする。
次に、禁断main.mの変更を行う。(※main.mは、普通は変更する事は無い)
これは初期状態ではcocos2d制御になるが、それをXIB制御にするためである。

main.m 変更

    int retVal = UIApplicationMain(argc, argv, nil, nil);


■普通のiOS仕様に仕立て上げる
あくまでも普通のiOSぽく、XIBを作ってそこから各画面に遷移する構造にする。(※現時点でStoryBoardの方法は不明)
まずMainWindow.xibを「Window」で作成し、ObjectLibraryから、Objectを追加する。
Objectのclassは、AppDelegateを設定。
File's Ownerのclassは、UIAppricationに設定。
File's OwnerのConnectionsにDelegateが出てくるので、AppDelegateと結ぶ。
そして、AppDelegate.hのwindowのプロパティにIBOutletを付けてやる。

AppDelegate.h 追記

@property (nonatomic, retain) IBOutlet UIWindow *window;

AppDelegateにwindowのConnectionが表示されるので、Windowと結びつけ、NavigationControllerを追加する。
WindowのRootViewControllerを、NavigationControllerに結びつける。
NavigationControllerのclassにTitleを設定し、ついでにNavigationItemのTitleもTitleに変更する。

ひとまず、MainWindow.xibを上記の用に設計し、次に各画面の構築を行う。

■各画面作成とタイトル画面表示
Title.xibを「View」で作成、Title.mと、Title.hをObjective-C classで作成する。SubclassはUIViewControllerのままで良い。
 
今回は横画面になるので、Title.xibのAttributesのOrientationを、PortraitからLandscapeに変更する。
タイトル画面ぽく画像を配置し、InfoViewController.xibへのボタンと、RootViewController.xibへのボタンを配置する。
右下の「i」ボタンは、AttributesのTypeをInfo Lightに変更すると形が変わる。
classは同名のTitleにすると、Connectionsにviewが出るので、Viewと結びつける。
 
InfoViewController.xibInfoViewController.mInfoViewController.hRootViewController.xibも同様に作成しておく。
RootViewController.mは既に有るものを使うので作成の必要は無いが、C++でコンパイルする必要が有るためRootViewController.mmリネームする。
Title.hを下記のように記載し、各画面へのボタン定義と、classを読み込んでおく。

Title.h

#import <UIKit/UIKit.h>

#import "RootViewController.h"

#import "InfoViewController.h"


@interface Title : UIViewController {

    RootViewController *_rootViewController;

    InfoViewController *_infoViewController;

}


@property (retain) InfoViewController *infoViewController;

@property (retain) RootViewController *rootViewController;


- (IBAction)infoTapped:(id)sender;

- (IBAction)viewTapped:(id)sender;


@end

InfoViewController.xibは下記のように作成してみた。
Title.xibでも行ったが、File's Ownerのclass設定と、Viewとの結びつけを忘れずに。
RootViewController.xibは下記のように背景を黒にし、Titleボタンだけ作成し、FullScreenに変更した。
Status BarもNoneにしておく。

■各画面への遷移組み立て
さて、ここまでやったが、まだ動くアプリケーションは出来ていない。
各画面に遷移するプログラムを行う必要がある。
まずはタイトル画面だけ出したい。
MainWindow.xibをMain Interfaceに設定する。
MainWindow.xibのViewControllerのNIB NameをTitleにし、NavigationControllerのAttributesのPresentation>Defines Contextをオフにする。
AppDelegate.mの一部を下記のようにコメントアウトする。

AppDelegate.m コメントアウト

/* 

 #import "HelloWorldLayer.h"

 #import "RootViewController.h"

 */


// window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];


/* 

 // Init the View Controller

 viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];

 viewController.wantsFullScreenLayout = YES;

 

 //

 // Create the EAGLView manually

 //  1. Create a RGB565 format. Alternative: RGBA8

 // 2. depth format of 0 bit. Use 16 or 24 bit for 3d effects, like CCPageTurnTransition

 //

 //

 EAGLView *glView = [EAGLView viewWithFrame:[window bounds]

 pixelFormat:kEAGLColorFormatRGB565 // kEAGLColorFormatRGBA8

 depthFormat:0 // GL_DEPTH_COMPONENT16_OES

 ];

 

 // attach the openglView to the director

 [director setOpenGLView:glView];

 */


/* 

 // make the OpenGLView a child of the view controller

 [viewController setView:glView];

 

 // make the View Controller a child of the main window

 [window addSubview: viewController.view];

 */


// [[CCDirector sharedDirector] runWithScene: [HelloWorldLayer sceneWithLastCalendar:0]]; 

Title.mを、とりあえず下記のように設定しておく。(※後程、更に更新する)

Title.m 暫定コード

@implementation Title

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation

{

    return UIInterfaceOrientationIsLandscape(interfaceOrientation);

}


- (void) viewWillAppear:(BOOL)animated

{

    [self.navigationController setNavigationBarHidden:YES animated:animated];

    [super viewWillAppear:animated];

}

@end

ここまでで、ボタンは機能しないが、タイトル画面は出るはずだ。

■InfoViewController画面への遷移
ここまで出来たら、各画面への遷移をプログラミングする。
Title.hについてはここまでの経緯で完成しているので、Title.mに各ボタンの遷移プログラムを記載する。

Title.m

#import "Title.h"


@implementation Title

@synthesize rootViewController = _rootViewController;

@synthesize infoViewController = _infoViewController;


- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation

{

    return UIInterfaceOrientationIsLandscape(interfaceOrientation);

}


- (void) viewWillAppear:(BOOL)animated

{

    [self.navigationController setNavigationBarHidden:YES animated:animated];

    [super viewWillAppear:animated];

}


- (IBAction)infoTapped:(id)sender {

    if (_infoViewController == nil) {

        self.infoViewController = [[[InfoViewController alloc] initWithNibName:nil bundle:nil] autorelease];

    }

    [self.navigationController pushViewController:_infoViewController animated:YES];

}


- (void)viewWallpapers:(id)arg {

    if (_rootViewController == nil) {

        self.rootViewController = [[[RootViewController alloc] initWithNibName:nil bundle:nil] autorelease];

    }

    [self.navigationController pushViewController:_rootViewController animated:YES];

}


- (IBAction)viewTapped:(id)sender {

    [self viewWallpapers:nil];

}


- (void)dealloc

{

    [_rootViewController release];

    _rootViewController = nil;

    [super dealloc];

}


@end

これで、ボタンとの紐付けが可能になるので、InfoViewController、RootViewControllerへのIBActionとボタンのTouch Up Insideを結びつける。
なんとなく、タイトルラベルも貼ってみた。
まずは、InfoViewControllerへの遷移をプログラムし、確認してみよう。
ボタンの紐付けは終わっているが、受け側にもプログラムを記載する必要がある。

InfoViewController.h

#import <UIKit/UIKit.h>


@interface InfoViewController : UIViewController {

    InfoViewController *_infoViewController;

}


@property (retain) InfoViewController *infoViewController;


@end

InfoViewController.m

#import "InfoViewController.h"


@implementation InfoViewController


@synthesize infoViewController = _infoViewController;


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

{

    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

    if (self) {

        // Custom initialization

    }

    return self;

}


- (void) viewWillAppear:(BOOL)animated

{

    [self.navigationController setNavigationBarHidden:NO animated:animated];

    [super viewWillAppear:animated];

}


- (void)dealloc

{

    [_infoViewController release];

    _infoViewController = nil;

    [super dealloc];

}


@end

InfoViewController.xibの作成は、ここまでで完了しているはずなので、タイトル画面右下の「i」ボタンを押してみよう。
下記のように、ナビゲージョンバーに「Title」ボタンが出て、戻る事が出来れば成功である。

■RootViewController画面への遷移
基本的にはInfoViewControllerと似ている。
遷移ボタンは出来ているはずなので、タイトル画面中央の「Start cocos2d」ボタンを押せば黒い画面にTitleボタンだけ出る。
まずは、Titleボタンの実装と、ViewDidUnLoadだけ実装し、タイトル画面とInfoViewController画面の行き来を確認しよう。

RootViewController.m 追記

- (IBAction)homeTapped:(id)sender {

    [self.navigationController popViewControllerAnimated:YES];

}

-(void)ViewDidUnLoad の最後に追記

    [[CCDirector sharedDirector] end];

とりあえず、下の画面が出て、Titleボタンでタイトル画面に戻れればOK。
さて、まだcocos2dが登場していない。
次はcocos2dのbox2dをRootViewControllerにハメ込む事にする。

■cocos2dをロードし、画面を重ねる
当記事の最初でcocos2dを切り離しているので、ここからセットアップしてやる必要がある。
RootViewController.mmの先頭のほうに下記を追記する。
Viewが呼ばれたときに、setupCocos2Dを実行しセットアップした後、HelloWorldに振っている。

RootViewController.mm 先頭に追記

#import "cocos2d.h"

#import "HelloWorldLayer.h"    //追記

#import "RootViewController.h"

#import "GameConfig.h"


@implementation RootViewController


- (void)setupCocos2D {

    EAGLView *glView = [EAGLView viewWithFrame:self.view.bounds

                                   pixelFormat:kEAGLColorFormatRGB565 // kEAGLColorFormatRGBA8

                                   depthFormat:0                        // GL_DEPTH_COMPONENT16_OES

                        ];

    glView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

    [self.view insertSubview:glView atIndex:0];

    [[CCDirector sharedDirector] setOpenGLView:glView];

    CCScene *scene = [HelloWorldLayer node];

    [[CCDirector sharedDirector] runWithScene:scene];

}


- (void) viewWillAppear:(BOOL)animated

{

    [self.navigationController setNavigationBarHidden:YES animated:NO];

    [super viewWillAppear:animated];

}


- (void)viewDidLoad {

    [super viewDidLoad];    

    [self setupCocos2D];

}

これで、RootViewControllerの下でHelloWorld(box2d)が実行され、タイトル画面にも戻る事が出来る。

■セグメントコントロールを取り入れる
ここまでは、単に普通のiOSからcocos2dを起動しただけだが、UIKitからcocos2dに変化をもたらせてやることにする。
RootViewController.xibに、Segmented Controlを追加し、箱の種類を選ばせてみる。
ButtonChangedメソッドを作り、Segmented ControlのValue Changedと結ぶ。

RootViewController にメソッド追加

//RootViewController.h に追記

- (IBAction)buttonChanged:(id)sender;


//RootViewController.mm に追記

- (IBAction)buttonChanged:(id)sender {

    NSUserDefaults* def = [NSUserDefaults standardUserDefaults];

    [def setInteger:[sender selectedSegmentIndex] forKey:@"boxType"];

}

HelloWorldLayer.mmも変更する必要がある。
初期値は「ランダム」ということで、セグメントでいう4をinitで設定しておくのと、
セグメントで選ばれた箱を指定する処理を入れる。

HelloWorldLayer.mm のinitに追加

        NSUserDefaults* def = [NSUserDefaults standardUserDefaults];

        [def setInteger:4 forKey:@"boxType"];

HelloWorldLayer.mm 変更

//変更前

int idx = (CCRANDOM_0_1() > .5 ? 0:1);

int idy = (CCRANDOM_0_1() > .5 ? 0:1);


//変更後

    int idx, idy;

    NSUserDefaults* def = [NSUserDefaults standardUserDefaults];

    NSInteger boxType = [def integerForKey:@"boxType"];


    switch( boxType ) {

        case 0:

            idx = 0;

            idy = 0;

            break;

        case 1:

            idx = 1;

            idy = 0;

            break;

        case 2:

            idx = 0;

            idy = 1;

            break;

        case 3:

            idx = 1;

            idy = 1;

            break;

        case 4:

        default:

            idx = (CCRANDOM_0_1() > .5 ? 0:1);

            idy = (CCRANDOM_0_1() > .5 ? 0:1);

            break;

    }

これで、セグメントで選んだ箱が追加出来るようになる。

■ポイント
最初が少々面倒ではあるが、これで後々cocos2dとUIKitの組み合わせがかなり楽になる。
ただし、2つの画面を重ねているため、重い処理には向かないと思われる。

■その他
RootViewController.mmの、setupCocos2Dの中で下記の記述をすると半透明で重ねる事も出来る。
alphaの値は0.0〜1.0。

alpha値の設定例

    self.view.alpha = 0.5;

添削アドバイス:@ajinotataki氏

後の記事:@ajinotataki:Tiledの使い方講座
Č
ċ
ď
xib5.zip
(2663k)
Aoi Alice,
2011/12/21 2:51