読者です 読者をやめる 読者になる 読者になる

謎言語使いの徒然

適当に気になった技術や言語を流すブログ。

iPhone でちまちまゲーム作ってみる

iPhone Tips 日記

作るゲームの内容とか、全コード曝すことはしない。
あくまで Tips というか、作る間にハマったこと、ノウハウとか書くだけ。

ゲームは複数の画面があって、当然のごとく切り替える。タイトル、ゲーム画面、ヘルプ、設定画面位はよくある構成か、、、。

もちろん、画像や音声の事も考えるなら、画面を切り替えたらリソースは常に解放するのが正しい。特に音声リソースはかなり容量を食うので、画面遷移の前にリソースを解放する。
では、どうやってリソースを解放するのかという話になる。その前に、ゲームの構造を考えてみる。
まず、どこで画面を切り替えるのか考える。
画面を階層化して管理できる手前、管理用の UIViewController を置く事も考えられるが、UIViewController のネストは、Cocoa の画面管理で、Quartz で言えば、とにかく画面があればよい程度。
リソースも抑えたいので、画面切り替えは AppDelegate にやらせてしまうのがよさげ。

で、このときに画面切り替え用メソッドを、 AppDelegate に突っ込んどく。

enum SceneID {
  SceneIDTitle,
  SceneIDGame,
  SceneIDHelp,
  SceneIDScore,
};

/* 中略 */

- (void)changeScene:(int)sceneId;

で、各 ViewController からは、AppDelegate に対して、画面切り替えせーというメッセージを送る。
このとき、画面4つ全部、AppDelegate への参照を個別に持たすのは面倒なので、親クラスを用意しておく。

@interface ControllerBase : UIViewController {
	XXXXAppDelegate* gameAppDelegate;
}

@property(nonatomic,assign) id gameAppDelegate;
@end

@で消すときとか面倒だし、assign でいいと思った。で、これを各ゲームシーンのコントローラで継承する。継承万歳。

次に画面切り替えのメソッドを実装してみる。

ヘッダはこんな感じで、

@class ControllerBase;

@interface XXXXXXAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    ControllerBase* controller;
    ControllerBase* oldController;
}

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

- (void)changeScene:(int)sceneId;

@end

実装はこんな感じか?

- (ControllerBase*)sceneFactory:(int)sceneId {
	
	ControllerBase* selected = nil;
	
	switch (sceneId) {
		case SceneIDHelp:
			selected = [[HelpController alloc] initWithNibName:@"HelpController" bundle:nil];
			break;
		case SceneIDTitle:
			selected = [[TitleController alloc] initWithNibName:@"TitleController" bundle:nil];
			break;
		case SceneIDGame:
			selected = [[GameController alloc] init];
			break;
		case SceneIDScore:
			selected = [[ScoreRecordController alloc] initWithNibName:@"ScoreRecordController" bundle:nil];
			break;
		default:
			break;
	}
	
	[selected setGameAppDelegate:self];
	
	return selected;
}

- (void)animationEnd {
	[oldController release];
	oldController = nil;
}

- (void)changeScene:(int)sceneId {
	oldController = controller;
	controller = [self sceneFactory:sceneId];
	
	[UIView beginAnimations:nil context:NULL];
	[UIView setAnimationDuration:0.5];
	[UIView setAnimationDidStopSelector:@selector(animationEnd)];
	[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:window cache:YES];
	
	[window addSubview:controller.view];
	[oldController.view removeFromSuperview];
	
	[UIView commitAnimations];
	
	[oldController release];
}
  • ゲームシーンの変更要求が来たら、sceneFactory でインスタンスを作る。Factoryパターン万歳
  • UIView animation で画面切り替える。
  • animationEnd でアニメーションの終了を補足して、リソース解放。

で、ざっくりメモリ解放する。
dealloc とか実装忘れちゃやーよ。