作るゲームの内容とか、全コード曝すことはしない。
あくまで 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 とか実装忘れちゃやーよ。