ゲーム開発とMVC
~コスト削減とクオリティの両立を目指して~
もんぐり
前回のおさらい
• デザインパターンを使いこなせれば、いい感じに開発スピードと品
質が向上する!(かもしれない)
• 以下を紹介しました。
– Singleton
– FactoryMethod
– State
– Strategy
– Flyweight
– Mediator
• 今回はObserverとMVCを紹介します。
– ていうかこの勉強会で一番伝えたかったのはMVCです!
• ちなみに、僕はMVCの専門家でもゲーム開発の専門家でもなく、
どっちも駆け出しなので、暖かくご意見ご指摘ください!
こんな経験ないですか?
• EnemiesManagerからEnemyの非同期処理メソッド呼び出すぞ。結果
通知はEnemiesManagerのメソッド呼び出しで。
• → EnemiesManagerとEnemyが相互に#includeしあう(相互依存)関
係になった。依存性強すぎ。
• EnemyからEnemiesManagerが見えている必要はないんでない?
• 別の例:タッチイベントが起こった時に、他クラスにそれを伝えよ
う。。
• → 伝えるクラスが状況によって違う場合、それら全部を#include
しなきゃいけなかったら依存増えすぎ?
Enemies
Manager Enemy
命令
結果通知
Observer
• イベントを受けるための共通I/Fの抽象クラスを用意し、イベントを受けるクラスはそれを
派生することで、抽象クラスだけ#includeすればいいようにしておこう(タイプ1)
• イベントコールバックとして関数ポインタを登録しておこう(タイプ2)
• Cocos2d-xではCCTouchDelegateでObserverの考え方が使われています。(タイプ1)
• Cocos2d-xではCCNotificationCenterという、イベント通知のための万能クラスが用意されて
います(タイプ2)
• CCNotificationCenterの使い方
• CCNotificationCenterは積極的に使いましょう
• ポーリングで監視する手もあります
イベントリスナ登録
CCNotificationCenter::sharedNotificationCenter()->
addObserver(this, callfuncO_selector(HelloWorld::enemyJumpEndHandler), ”enemyJumpEnd", NULL);
イベント通知
CCNotificationCenter::sharedNotificationCenter()->
postNotification( ”enemyJumpEnd", NULL);
こんな経験ないですか?
• ゲームの演出について、仕様変更が頻繁
すぎてソースがぐちゃぐちゃになってき
たよ。。
• UIを変更したいんだけど、内部のコアなロ
ジックと関連してて修正範囲めっちゃで
かいよ。。。
MVC
• 画面の表示の都合や操作の都合により左右される
部分と、内部のコアなロジックは分離しよう
• GUIアプリケーションやWebサービスの開発でよく
使われるパターンです
• MVCはデザインパターンというよりアーキテク
チャパターンと言われます
– アプリ全体の設計方針を決定します
– いくつかのデザインパターンを利用して実現します
MVC
ModelView
Controller
• Model
– 抽象的な内部ロジック。アプリケーションの核。
• View
– ユーザに見える部分。画面表示や、UIのユーザ操作窓口処理を受け持つ。
• Controller
– ViewとModelを結びつける。ユーザ入力を受け付け、Modelを駆動する。
直接的依存性
Observerへの通知
MVCを使うメリット
• View(ユーザに見える部分)を修正してもModel(コアロジック)に影響が出ません
→ 表示系の仕様変更に対して柔軟
• クラスの独立性が高いです
→ 品質、メンテンナンス性向上
• Model部分を環境やフレームワーク非依存に作れます
→ 移植性向上
• (Model部分は純粋にロジックだけなので自動テストを作成しやすいです)
→ 品質、メンテンナンス性向上
• デメリットは、使いこなすのも処理を追うのもMVCへの理解と慣れがいること
ModelView
Controller
直接的依存性
Observerへの通知
MVCの実装例
• 倉庫番のPlayerクラスをPlayerModelと
PlayerViewクラスに分割してみる
左ボタン押下
Model
View
Controller
左ボタン押下
左ボタン押下
Player
Model
Player
View
PlayerModel
のx座標変更
PlayerViewでの
歩行アニメーショ
ン 座標変更通知
MVCの実装例:ModelとViewの分割
・PlayerModel
class PlayerModel {
public:
int m_cellX;
int m_cellY;
STATE m_state;
・・・
};
Modelの世界では、プレーヤーの大きさだとか画面上の表示位置だとかは扱わない。
セルの座標やプレーヤーの状態値といった、抽象的な情報のみ扱う。
・PlayerView
class PlayerView : public CCSprite {
PlayerModel* model;
・・・
};
Viewの世界では、Modelの抽象データを、画面上の描画として具体化する。
通知にイベントリスナーを使用し
た場合・PlayerModel
void PlayerModel::walkTo(int cellX, int cellY) {
m_cellX = cellX;
m_cellY = cellY;
// イベント通知
CCNotificationCenter::sharedNotificationCenter()->
postNotification( ”playerMoved", NULL);
}
・PlayerView
bool PlayerView::init() {
// 初期化時にイベントリスナ登録をしておく
CCNotificationCenter::sharedNotificationCenter()->
addObserver(this, callfuncO_selector(PlayerView:move), ”playerMoved”, NULL);
}
void PlayerView::move() {
runWalkAimation(); // その場での歩行アニメーション
// 0.5秒で目的地まで移動
CCMoveTo* move = CCMoveTo(0.5f, getRealPosition(model->m_cellX, model->m_cellY));
runAction(move);
}
ゲームループによって描画反映す
る場合・PlayerModel
// ゲームループにより毎フレームupdateメソッドが実行される
void PlayerModel::update() {
switch (Input->getInput()) {
・・・
case KEY_L: // 入力が左キーのとき
m_cellX--;
// 状態を歩行中状態へ変更
m_state = STATE_WALKING;
break;
・・・
}
}
・PlayerView
//ゲームループにより毎フレームdrawメソッドが実行される
void PlayerView::draw() {
switch(m_state) {
・・・
case STATE_WALKING:
// 毎フレームちょっとずつ位置を左にずらしていって0.5秒後に
目的地へ到達する処理。詳細は省略。
break;
・・・
}
}
いわゆるポーリングですが、
これもViewがModelの状態を
監視してることには変わりない!
• ModelとViewの分離方法は、各ゲーム内容によって適した方
法があると思います。
• あくまで、「表示やUIの詳細に左右される部分と、内部のコ
アロジックを切り離すことでメリットを享受する」のが目的
です
– 個人的には「」内が実現できるならどんな作り方でもいいと思
います。
各ゲームジャンルへのMVCの適用
シューティング
※SlideShare版
では絵を外しま
した
アクション
※SlideShare版
では絵を外しま
した
パズル
※SlideShare版
では絵を外しま
した
(MVCって)
本当にゲーム開発の現場で
役に立つんかいな?
知り合いに聞いてみた
※このスライドは、個人情報が入ってくるので、
SlideShare版では記載を外しました。
• 個人的には、経験上、取捨選択すればおおい
に使えると思っています。
まとめ
• MVCをいい感じに使えるようになって開発
スピードと品質を向上させよう!

ゲーム開発とMVC