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

コガネブログ

平日更新を目標にUnityやC#などのゲーム開発アレコレを書いていきます

【greegdm01】「Cocos2d-xを用いた "LINE タワーライジング" の開発事例」メモ

発表資料

自己紹介

  • 西田綾佑(にしだ りょうすけ)
    • グリー株式会社 Wright Flyer Studio部
    • クライアントリード(エンジニア)
    • @hosi_mo
    • 塔コード:4M9EVCBK
  • 経歴
    • 東京大学大学院情報理工学系研究科 修了
    • グリー株式会社(2014年~)

ゲーム紹介

LINE タワーライジング

LINE タワーライジング

  • LINE Corporation
  • ゲーム
  • 無料

  • 配信日:2015/6/23
  • ジャンル:RPG、アドベンチャー
  • プラットフォーム:iOS/Android
  • おかげさまで事前登録43万人
    • 塔はつんでも、人生つむな
    • 頭脳系ダンジョンRPG
    • 方向音痴の作者が作った、新しいスタイルのダンジョン探検

実はDungeon Flickerがベースとなった

"タワーライジング"リリースまでの開発体制

職種 人数
プロデューサー 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を眺めてスレッドを感じる

要素技術

タワーライジングの開発

開発初期(2014年春~夏)

2014年3月にダンジョンフリッカーをリリース

  • Cocos2d-xで作り直したい
  • 画面数が少ない
  • 複雑な画面が少ない
  • 簡素な演出が好まれた
    • LWFを使うほどではない
  • 当時:(送りバントと呼ばれたプロジェクト)
    • そもそものアサインが少ない(さらにFlashアニメータ自体が社内に少ない)

結果

oh...

  • UI配置:エンジニアが頑張る
  • UIアニメーション:エンジニアが頑張る
  • 演出:エンジニアが頑張る

アサインされたエンジニアが面食らう

タワーライジングは町工場だから

開発初期

エンジニアから職人へのジョブチェンジ

  • UI
    • コードに座標打ってもなんとかなる...かも?
  • 演出
    • runAction()も、Easingもあるじゃないか
  • パペットアニメーションの実現は...

アニメーション

安心してください、書いてますよ

なんとかなった理由 ①画面がシンプル

画面の仕様がシンプル

  • すべての画面を同一のコンポーネントで作る
    • アニメーションが共通化できる
    • SEが共通化できる
  • 基本的に9sliceで作り切れる範囲の装飾

なんとかなった理由 ②仕様書もシンプル

一枚絵とにらめっこして作り上げられる

  • 仕様書が画像一枚(+注釈)
  • 位置もこれがマスター
  • 究極的にはエンジニアがこの一枚絵を完コピすれば良い

なんとかなった理由 ③Flashでmockを作成

新規のこまかい演出の実装

  • Flashでmockを作ってくれた
  • ソース見てC++に書き換える
  • SのBetweenライクなWrapperをC++で実装

なんとかなった理由 ④消滅都市先輩がいた

消滅都市

消滅都市

  • Wright Flyer Studios
  • ゲーム
  • 無料

消滅都市から基本機能を切り出して改良した

  • クライアントサイド
    • 通信
    • データベース
  • サーバーサイド
    • 概ね消滅都市と同等の構成(Silex + MagicSpice)
  • その他プロジェクトの偵察を怠らなかった
    • エンジニアリングにおける挑戦をしなかった
    • 安全な方向に倒す開発に専念した

設計のはなし

全体の設計

シーン一覧<CCScene.cpp>

  • TitleScene
    • ログイン処理、データベース構築など
  • HomeScene
    • ホーム画面(塔を俯瞰したあの画面)
  • DungeonScene
    • ダンジョン内
  • DecorationScene
    • デコレーションする画面

各シーンにLayerを重ねてUIを表示していった

  • HomeScene.cpp<ホーム画面>
    • HomeState<ホーム画面のidle状態>
      • ShopState<Shop画面>
      • WarpzoneState<ワープゾーン画面>
      • HomeMenuState<メニュー画面>
        • 強化画面(以下略
  • 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ヶ月前のダンジョン内のデータをちゃんと救えるかどうか
    • どうしてもダメになるまでは諦めないけどやはり辛い