cocos2d裏面如何實現MVC(二)

模型類

就像之前所討論的,GameModel類存儲了遊戲世界裏面的一些屬性,比如當前的重力。但是,它同時也負責創建和聯接遊戲裏面的對象,比如Player和Platforms。它們之間的關係如下圖所示:(譯者:這裏採用了針對接口編程的方法,所有的遊戲對象都實例updateable接口,這樣就可以在game loop裏面更新自己了。同時GameModel類提供了一個工廠方法createGameObjects,用來創建遊戲裏面的對象。)


你可能已經注意到了,所有的model類都實現了updateable protocol,並實現了update方法。這樣它們就可以在game loop裏面更新自己的狀態了。比如,在Player類裏面,我們需要根據當前x軸和y軸的速度來更新player的位置信息。在我的遊戲裏面,我把它委託給Physics組件,它是我實現的一個簡單的物理引擎。但是,假如你的遊戲很簡單的話,你可以不用分開你的物理代碼,然後可以直接在update方法裏面來做碰撞檢測等物理操作。

 

@implementation Player
- (void)update:(ccTime)dt
{
[_physics updateModel:self dt:dt];
// detect collisions with game objects, etc.
}
 

GameModel實現的update方法,不僅僅用來更新自己的狀態,同時,它還調用player的update方法和所有platform的update方法。這個update方法,之後會被game loop所調用。

@implementation GameModel
- (void)update:(ccTime)dt
{
// modify game model properties here
// update player
[self.player update:dt];
// update platforms
for (Platform *platform in _platforms) {
[platform update:dt];
}
// ...
}
 

視圖和控制器類

對於我的遊戲裏面的每一個場景,都關聯了一個Controller類,它負責處理用戶交互、創建視圖和管理場景的跳轉。控制器會schedule一個遊戲主循環,在這個loop裏面,所有的model和view的update方法都會被調用。

@implementation GameplayController
- (id)init
{
if((self=[super init])) {
GameplayView *view = [[GameplayView alloc] initWithDelegate:self];
// retain view in controller
self.view = view;
// release view
[view release];

// init model
GameModel *model = [GameModel sharedModel];
[model createGameObjects];
[model.player run];

[self scheduleUpdate];
}
}

- (void)update:(ccTime) dt
{
GameModel *model = [GameModel sharedModel];

if (model.isGameOver) {
[[CCDirector sharedDirector] replaceScene:[GameOverController node]];
}

// process model
[model update:dt];

// update view
[self.view update:dt];
}
 

View主要負責根據model的狀態來渲染遊戲畫面。但是,同時,因爲cococs2d的實現方式,我們還需要把touch事件傳遞給controller類。你應該注意到了,view不併直接依賴controller。view類調用controller的方法是通過GameViewDelegate協議來實現的。這也是爲什麼我們要在init方法裏面傳遞一個delegate的原因。

@implementation GameplayView
- (id)initWithDelegate:(id)theDelegate
{
if ((self = [super init])) {
self.delegate = theDelegate;

// initialize layers
_backgroundLayer = [GameplayBackgroundLayer node];
[self.delegate addChild: _backgroundLayer];

_platformLayer = [GameplayPlatformLayer node];
[self.delegate addChild:_platformLayer];

_playerLayer = [GameplayPlayerLayer node];
_playerLayer.delegate = theDelegate;
[self.delegate addChild: _playerLayer];

_hudLayer = [GameplayHudLayer node];
_hudLayer.delegate = theDelegate;
[self.delegate addChild:_hudLayer];
}

return self;
}
 

// 更新:我忘了告訴大家layer本身是怎麼實現的了。其實很簡單,就是創建一些sprite、action和animation等。

@implementation GameplayPlayerLayer
- (id)init
{
if ((self = [super init])) {
self.isTouchEnabled = YES;
self.isAccelerometerEnabled = YES;
ResourceManager *resources = [ResourceManager sharedResourceManager];

[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:PLAYER_SPRITE_SHEET_PLIST];

CCSpriteBatchNode *spriteSheet = [resources playerSpriteSheet];
[self addChild:spriteSheet];
// ...
// initialize sprites
// initialize animations
}
 

層裏面的精靈都會在layer的update方法裏面被更新,如下所示:

 

- (void)update:(ccTime)dt 
{
// update player sprite based on model
GameModel *model = [GameModel sharedModel];

_playerSprite.position = ccp((model.player.position.x - model.viewPort.rect.origin.x) * PPM_RATIO, (model.player.position.y - model.viewPort.rect.origin.y) * PPM_RATIO);
}
 

注意,在渲染player的位置的時候,我們使用了PPM_RATIO,用來把米轉換成point。(爲什麼是point而不是pixel,因爲cocos2d使用的是point而不是pixel,不明白的可以看看源代碼和官方文檔)

touch事件被傳遞給了controller類,如下所示:

@implementation GameplayPlayerLayer
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.delegate playerBeginJump];
}
 

然後下圖就是view和controller交互的完整的UML圖:

處理模型事件

上一篇博文中,我留下了一個問題,就是怎麼處理model和controller之間的交互。其它很簡單,就是使用觀察者模式,controller只要訂閱model的事件,然後定義相應的處理方法即可。當model更新的時候,會觸發事件,然後所以偵聽了該事件的controller都能被通知到。下面給出實現:(譯者:很多童靯不知道對象之間該怎麼交互,其實使用NSNotification可以大大地解耦對象的交互,使代碼更容易維護。)

@implementation Player
- (void)beginJump
{
if ([_gameModel isOnGround:self]) {
[[NSNotificationCenter defaultCenter] postNotificationName:EVENT_PLAYER_BEGIN_JUMP object:nil];
...
}

controller訂閱事件,當事件發生的時候會得到通知,同時相應的事件處理函數將會被調用。

@implementation GameplayController
- (id)init
{
if ((self = [super init])) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPlayerBeginJumpNotification:) name:EVENT_PLAYER_BEGIN_JUMP object:nil];
...
}
}

- (void)onPlayerBeginJumpNotification:(NSNotification *)notification
{
[[SimpleAudioEngine sharedEngine] playEffect:PLAYER_JUMP_SOUND];
}
 

轉自:http://www.cnblogs.com/andyque/archive/2012/03/11/2390082.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章