発表資料
自己紹介
- 西田綾佑(にしだ りょうすけ)
- グリー株式会社 Wright Flyer Studio部
- クライアントリード(エンジニア)
- @hosi_mo
- 塔コード:4M9EVCBK
- 経歴
- 東京大学大学院情報理工学系研究科 修了
- グリー株式会社(2014年~)
ゲーム紹介
https://itunes.apple.com/jp/app/line-tawaraijingu/id961565339?mt=8&uo=4&at=10l8JW&ct=hatenablog
- 配信日:2015/6/23
- ジャンル:RPG、アドベンチャー
- プラットフォーム:iOS/Android
- おかげさまで事前登録43万人
- 塔はつんでも、人生つむな
- 頭脳系ダンジョンRPG
- 方向音痴の作者が作った、新しいスタイルのダンジョン探検
実はDungeon Flickerがベースとなった
https://itunes.apple.com/jp/app/dungeon-flicker-danjonfurikka/id847236039?mt=8&uo=4&at=10l8JW&ct=hatenablog
"タワーライジング"リリースまでの開発体制
職種 | 人数 |
---|---|
プロデューサー | 1名 |
ディレクター | 1名 |
クライアントプログラマ | 4名 |
アート | 2名 |
ゲームデザイナー | 2名 |
サーバプログラマ | 3名 |
PM | 3名 |
全体スケジュール(14ヶ月)
年月 | スケジュール |
---|---|
2014/3~ | ダンジョンフリッカー公開 |
2014/4~ | α開発 |
2014/10~2015/1 | β開発 |
2014/11~2015/4 | ベンダーテスト |
2015/1~ | LINE連携 |
2015/4~2015/6 | チュートリアル改善 |
2015/6 | GA |
開発環境
- Cocos2d-x 3.2
- コーディング環境
- C++ 11
- Max + Xcode
- 主にiOSの実機開発(手触りを重視)
- Android
- 動作確認 Android 2.3~(動作保証は現状4.0)
- なにか起きたらlog catを眺めてスレッドを感じる
要素技術
- libdispatch
- https://github.com/nickhutchinson/libdispatch
- 並列化したい処理だけキューに投げられて便利
- Cricket Audio
- http://www.crickettechnology.com/
- サウンドの再生。安定してる
- 他プロジェクト(Cocos2d-x)で実績あり
- Jenkins
- https://jenkins-ci.org/
- 毎日自動でビルドを作って社内チャットに共有
- Hockey App
- http://hockeyapp.net/features/
- ビルド配布
- クラッシュログ収集
タワーライジングの開発
開発初期(2014年春~夏)
2014年3月にダンジョンフリッカーをリリース
- Cocos2d-xで作り直したい
- ダンジョンフリッカーはAdobe Air
- 画面数が少ない
- 複雑な画面が少ない
- 社内ツール:レイアウトローダ(psd->Cocos2d-x)を使うほどでもない
- 簡素な演出が好まれた
- LWFを使うほどではない
- 当時:(送りバントと呼ばれたプロジェクト)
- そもそものアサインが少ない(さらにFlashアニメータ自体が社内に少ない)
結果
oh...
- UI配置:エンジニアが頑張る
- UIアニメーション:エンジニアが頑張る
- 演出:エンジニアが頑張る
アサインされたエンジニアが面食らう
タワーライジングは町工場だから
開発初期
エンジニアから職人へのジョブチェンジ
- UI
- コードに座標打ってもなんとかなる...かも?
- 演出
- runAction()も、Easingもあるじゃないか
- パペットアニメーションの実現は...
アニメーション
安心してください、書いてますよ
- Avatar.cpp
- 1200行くらい
なんとかなった理由 ①画面がシンプル
画面の仕様がシンプル
- すべての画面を同一のコンポーネントで作る
- アニメーションが共通化できる
- SEが共通化できる
- 基本的に9sliceで作り切れる範囲の装飾
なんとかなった理由 ②仕様書もシンプル
一枚絵とにらめっこして作り上げられる
- 仕様書が画像一枚(+注釈)
- 位置もこれがマスター
- 究極的にはエンジニアがこの一枚絵を完コピすれば良い
なんとかなった理由 ③Flashでmockを作成
新規のこまかい演出の実装
- Flashでmockを作ってくれた
- ソース見てC++に書き換える
- SのBetweenライクなWrapperをC++で実装
なんとかなった理由 ④消滅都市先輩がいた
消滅都市から基本機能を切り出して改良した
- クライアントサイド
- 通信
- データベース
- サーバーサイド
- 概ね消滅都市と同等の構成(Silex + MagicSpice)
- その他プロジェクトの偵察を怠らなかった
- エンジニアリングにおける挑戦をしなかった
- 安全な方向に倒す開発に専念した
設計のはなし
全体の設計
シーン一覧<CCScene.cpp>
- TitleScene
- ログイン処理、データベース構築など
- HomeScene
- ホーム画面(塔を俯瞰したあの画面)
- DungeonScene
- ダンジョン内
- DecorationScene
- デコレーションする画面
各シーンにLayerを重ねてUIを表示していった
- HomeScene.cpp<ホーム画面>
- HomeState<ホーム画面のidle状態>
- ShopState<Shop画面>
- WarpzoneState<ワープゾーン画面>
- HomeMenuState<メニュー画面>
- 強化画面(以下略
- HomeState<ホーム画面のidle状態>
- DungeonScene
- BattleState
- EquipmentState
ステートの分割
UIの動きに見合うステートマシンもどきを設計した
- Layerの管理
- Stack型の状態管理(ゲームロジック)
- Viewのトランジションも自動化
LayerのPush()とPop()で階層的なUIを管理
StateManager::getInstance()->pushState(レイヤー);
StateManager::getInstance()->popState();
(例)ダイアログの表示と消去
auto dlg1 = DialogOne::create(); StateManager::getInstance()->pushState(dlg1);
StateManager::getInstance()->popState();
(例)ダイアログ1のコールバックを受けてダイアログ2を表示
auto dlg1 = DialogOne::create(); auto dlg2 = DialogTwo::create(); StateManager::getInstance()->pushState(dlg1, [dlg2](StateEvent se){ if (se.Name == DialogOne::YES){ StateManager::getInstance()->pushState(dlg2); } });
階層的なUIの実現
そのおかげで
- 階層的な画面が容易に作れた
- ウィンドウの表示・非表示のアニメーションが楽に統一化できた
- UIベタ書きでもなんとか持ちこたえた
特徴的な機能の実装例
オートセーブ機能
ダンジョン内にいる場合いつでもアプリをkillできる
- 地道に1フリックごとに状態を保存
- バトル中は特定のタイミングでスナップショットをとっている
- ※暗号化しています
デコレーション
乗算でそれっぽく仕上げる
- グレースケールで書かれたレンガに画像を乗算してalpha指定
- 横の壁はSpriteを3軸で歪ませて表現
- レンガのパースが厳密でないので鬼の位置調整
BlendFunc blend; blend.src = GL_ZERO; blend.dst = GL_SRC_COLOR;
顔はめ込み機能
両OSでNative Bridge先で端末内の画像をcropする
- iOS
- Obj-C
- UIImageで頑張る
- Android
- Java
- Bitmap.createBitmapで頑張る
画像シェア機能
Utils::captureScreen
- GPUによってはRenderTextureで描画が崩れる(ClippingNode...)
- スクショを撮る一瞬だけ不必要なnodeを隠した
- 本当はcropしてシェアしたかった
- 全画面スクショで我慢した
パフォーマンス
- アクションゲームではない
- 30FPSで問題ない
- 演出が激しくない
- 要求されるものが少ない
- がんばらなくてよかった
あえて言うならば
コンパクトなアートリソース
- アイテム画像全部で500KBくらい
- アートさんがモンスターも1枚1枚減色してくれた
- アセットダウンロード無しで初期リリースを決意
- 結果的に初回起動が早い
反省点
- やっぱりロジックは切り離したい
- 手書きは慣れると早いけど
- オススメはしない
- 大規模な画面仕様の変更がはいると
- QAを厚くしないと不安で寝れない
- エンジニアが全員職人なわけがない
- アニメーションは書かないと感覚が身につかない
苦労したところ
- Cocos2d-x 3.2に積んでいるlibcurlが古い
- HTTP Status:200なのにresponseが空っぽ
- オプションによってはタイムアウト時にクラッシュ
- libcurlの乗り換え検討中
- SQLiteのWriteが遅い
- Write前にReadしてハッシュ比較
- 探検中データの下位互換
- Nヶ月前のダンジョン内のデータをちゃんと救えるかどうか
- どうしてもダメになるまでは諦めないけどやはり辛い