Updated at: 2013-12-16

ゲーム実装するときに考えることリスト

はじめに

  • 密結合だがコーディングは楽、オーバヘッド大きいけど疎結合、など世界は何かとトレードオフ
    • 費用対効果ふまえつつ、ある程度の割り切りを持って、ちょうどよいレベルでやろう

スマフォのゲーム開発で気にすること

  • Android と iOS がある
    • iOS は申請が必要で、アップデートも世に出せるまで 1 週間程度のラグがある

  • Client のバージョンアップがある
    • Server があるゲームの場合、Server は古い Client と新しい Client がいることを考慮しなければならない
    • どうしようもない場合は古い Client では遊べなくなる仕組みが必要(アップデートを強制する)

  • 端末スペックと OS のバージョンがバラバラ
    • 低スペックでも動かせるようにするならば、メモリ / cpu / 見栄え などの間でトレードオフになるパフォーマンスチューニングが必要

  • ストレージは足りない場合がある
    • 容量を食いまくるアプリは敬遠される
    • ファイル扱う系は容量足りなくて失敗しうるケースを常に考慮する

  • 電池と発熱
    • 高負荷で電池消費が激しく、端末が熱くなるアプリは敬遠される

  • 1 人でも複数端末で同じゲームをプレイする
    • 外ではスマフォでプレイして、家ではタブレットで遊ぶとか、割と普通にやられる
    • ユーザデータの Sync をどこまで自動で厳密に行うかは決め所

  • 端末の時刻は変更できる
    • 固くやりたい場合は起動やレジュームのタイミングで Server から時刻を取得する必要がある
    • 時刻は同じでもタイムゾーンが違うとかもある

  • UI がタッチスクリーンである
    • ボタンの同時押しなどをケアしておく必要がある

強制的に Client をアップデートする

  • Server があるゲームの話
    • 古い Version の Client でプレイされたら困るような更新のとき
    • (後方互換を保てないようなケースが出てきたとき)

  • Server のレスポンスに、必須バージョンの情報などをつけておく
  • シンプルにやるなら、アップデート要請の情報がレスポンスについていたら Client は進行不能にしてアップデート要請通知、とかでよい

  • Client が通信をキャッシュしたり特殊なことをやっている場合は、 ハンドリングするタイミングを Client 任せにした方がいい
    • Client には「このタイミングでプレイ不可になったらプレイヤーの損失が大きい」場面がある
    • 例えばレアアイテムを使ってステージをクリアするときに、クリアを確定する通信でプレイ不可になったら そこまでの労力とアイテム消費が無駄になる
  • Client はレスポンスについてる情報を見て、必須バージョンを保持しておく
    • 強制的なアップデートに促す表示を出してよい場面でそれを出してブロッキングする

Server / Client のゲーム作るなら覚えておこう

  • Client がリクエスト投げて Server で受理・処理されたけどレスポンスだけ受け取れないケース
    • ちゃんと考慮しとかないと、たまにしか起きないので根深いバグになる
    • 固くやると課金のトランザクション的なアレになる

設計時に考えておくとよい系

拡張性

  • 先を見越して修正・拡張しやすいように作っておく余力が、エンジニアの腕の見せどころ
    • よくあるのは同時に 1 個しかやらないと思っていたものがやっぱ複数やりたくなった、とか
    • データがどんどん増えていくようなもので 1 年後にスケールしてるのか、とか
    • このフラグ管理、32 bit あれば足りるよね → 大抵足りなくなる
    • ゲーム全体に影響を及ぼすようなレイヤー、 大きくてスキーマ変更が大変になるようなデータベースほど慎重になろう

ユーザデータ

  • Server にユーザがたくさん居るようなゲームで、開発時にユーザのリセットはできるか?
    • ケースによるが、新しいユーザに乗り換えて、前のユーザデータは残しておくのが無難か
    • データをリセットするのは、他のユーザと関係性を持つようなゲームの場合に厄介になることが多い
    • 「進捗を巻き戻す」みたいな、本番環境では起こらないような処理はデバッグコマンドとしても用意したくない

ユーザが任意で保存するセーブデータ

  • お気に入りのキャラ編成を保存しておく、など
    • 長く運用するゲームの場合、リリース後に保存すべきデータが増えたりする
    • 新しい Ver. で古い Ver. のデータをロードしてもちゃんと動くようにしなきゃならない
    • アップデート時にデータの変換とかスキーマの変更みたいなことをしたくないなら、 ランタイムで Ver. を最新までマイグレーションするような機構を入れるとか

みんながアクセスできちゃうグローバルなデータ領域

  • よく使われるものは割り切って static にアクセスできるようにした方が楽ではある
    • (もしくはフレームワークレベルで全 actor に引き渡すようにしとく)
    • (みんなが気楽にさわれるべきものは、みんなが気楽にさわれるインタフェースを用意しておく)
    • 関係者だけでイベント投げ合うのでも実現可能だが、面倒だしオーバヘッド大きい

  • ただし static な変数群に直接アクセスするのではなく、何らかのモデル化は行った方がよいだろう
    • こいつを通してれば、データの持ってくる元をローカルからサーバに移し替えたくなった、とかの場合でも対応が楽

リソースマネジメント

  • シーンスコープのリソース管理はわかりやすい
  • シーン単位でリローダブルになるのもよい
  • 本当によく使う小さめアセットはグローバルに読み込んでおいてもいい

  • 厄介なのは、「シーン中に、場合に応じて動的に読み込みたい」ようなもの
    • 画像のサムネイルとか、いっぱいあるメニューのウインドウとか
  • あとは「まずデータファイルを読み込まないと、次に読み込むべきアセットがわからない」ケース
  • どう書けたら嬉しい?

  • 基本はシーンスコープで、追加読み込みもできるようにしておいて、 「もう読み込まれてたら何もしない」ようにフレームワークレベルでなっているとまあまあ使いやすい
  • 部分的に破棄もできるようにしたいなら、リソースも階層構造つくりたくなる

メモリ管理

  • チューニング頑張りたいなら、結局 C++ とかで自分で制御したくなる
  • 楽をするなら GC のある言語。でもゲームだと GC タイミングコントロールできるものじゃないとプレイ中のカクつき辛い

  • スマフォの世界だと低スペック端末もあるのでチューニングは無視できない
    • データの軽量化によるメモリ削減はまずやるべき
    • ゲームの Scene ごとに都度解放すればピーク消費は減らせるが、処理のオーバヘッドは上がる (CPU とメモリのトレードオフ)
    • キャッシュ機構を作ればリソースのスコープをまたぐとき(シーン遷移時など)のプレイ体感は上げられるが メモリの少ない端末だとアプリが落ちるリスクが上がる
    • 端末ごとにキャッシュサイズを設定できるような機構にすれば、低スペック端末で落ちるのは救える

  • 複数のファイル固めたり、バイナリそのまま保存して扱うようなものは効果出やすい
    • SD カードもランダムアクセスはシーケンシャルよりもずっと遅くなる

ローカライズ

  • TextMaster 的なモデルを作ってそいつから getText するようにする
    • どの言語を返すべきかは TextMaster が初期化段階でロケール見て判断する
    • 引数をプレースフォルダで指定できるようにしておくと吉

// 以下のデータあったとして
{
    string_key_1: {
        jp: "お前は {1} を {2} 個持ってるよね",
        en: "You have {2} {1}s."
    },
    item_name_1: {
        jp: "りんご",
        en: "Apple"
    }
}

// こう取得するかな
itemNum  = 10;
itemName = getText("item_name_1");
message  = getText("string_key_1", itemNum, itemName);  // 可変長引数もしくは配列で

// jp -> お前は りんご を 10 個持ってるよね
// en -> You have 10 Apples.

  • プログラムに食わせるデータは json か XML が妥当
    • 吐き出す元ファイルは csv を Excel で編集するのが無難か?
    • Google Docs 使う派もいるけどデータ壊しやすいしバージョン管理が悩ましい

UI どう作って組み込む

  • プログラマが無茶せずに作るなら Feathers みたいなのでコードで書けばいい
    • ただこれだとアーティストとの連携がコーディングありきになるので開発のオーバヘッドはある

  • ペルソナ 3, 4 みたいなメニュー画面作りたいなら Flash とかでオーサリングしたい
    • まあこれはこれでイベントハンドリングやラベルの取り決めとかもろもろ発生するけどね

  • プログラマとアーティスト、どちら先行で作っていくかによるかな
  • iPhone のリスト UI みたいなのやろうと思ったらまあ Feathers でコーディングになるよね

スタックにのる感じのウインドウ表示

  • 経験的に、ウインドウをシーンで表現しない方がよいと思う
    • まあシーンの定義によるが。自分は「シーン = リソースのスコープ」くらいに考えたい
    • つまり「シーン遷移 = 後片付けが行われて新たな Loading が走るタイミング」
    • シーンを階層構造にできるようにするシステムを作ってもよいが、ちょっと複雑
    • 描画の重なりをシーンのスタックに委ねるのは融通ききづらい
      • かといってシーン遷移時に {bg-hide: true} みたいなの渡すのも面倒
    • 一番よくないのは、「背景にウインドウ 3 つ重なった状態から、1 つめまで戻る」みたいな処理の時に シーンが「じゃあ 2 つ pop してくれ」とか言うこと
    • 子のシーンはそんなこと知っている状態であってはいけない。何より 2 つ pop しないような場面で 呼び出すときに使えなくなる

  • 見た目と状態遷移を一緒に扱っているのがいけない
    • 階層形のステートマシンを別に持つべき
    • 状態遷移はステートマシンを使ってイベント駆動で定義する
    • ステートマシンを見て「この状態だからこういう見た目にする」という処理の書き方にする
    • 見た目と内部状態で分離できて読みやすく・変えやすくなる

どこからでも呼び出したい、割と凝ったメニューとかは?

  • リソースの扱いによる
  • 背景のシーンを保持しておきたいのか、完全に切り替えちゃってよいのか
  • メモリ少ない環境だとバックのシーンのメモリ退避とか考えないといけない

ステートマシン

  • 単純な FSM でなく階層型くらいまでやれば結構使い道ある
  • 自分は json でステート定義できる階層型ステートマシンを作った
    • 自前フレームワーク内のコンポーネントとして
    • KrewStateMachine.as
    • ToDo: ドキュメンテーション

参考リンク

Object Pooling

  • 物量が出る系のゲームではまあ必須
  • ライブラリ化したいよね

参考リンク

・匿名関数のメモリ束縛について

衝突判定

四分木で空間分割

Spatial Hashing

参考リンク

タイルマップ

Tiled Map Editor

  • ツールとしては Tiled が鉄板か
  • ソースとなる画像をタイル状に敷き詰めるのってよいツールあるのかな
    • 自分は PhotoShop で配置しちゃってるけど
    • ソース画像の配置とか解像度とか変えたくなったときめんどい?


  • シンプルで使いやすいが、たまに物足りないところもあり
    • タイル画像のローカル ID とか自分で計算するしかない。みんな望んでそうなものだが
    • あと画像のサイズとか変えようとすると ID 全部狂うので大変
      • メタデータとかをタイルで重ねて、プログラムで ID を紐づけているような場合には注意

六角形のタイルマップ

ゼルダみたいなマップ

壁との衝突判定

  • 物理エンジン使っちゃうのが楽

めっちゃ広いのをシームレスに表示

Isometric なマップ

マリオみたいな platformer なマップ

タイルベースじゃない感じのマップのエディタ

  • プロでも、最終的に自前でなんか用意することが多い
  • Unity とかも Unity 内にエディタ作ってデータ吐いたりとかね
  • 僕は 2D なら Flash で絵を置いて jsfl でデータ書き出すとかが手軽でよいかと
  • 3D なら Maya とか

ナナメの地面とかあるときの衝突判定

  • これも物理エンジン使っちゃうのが楽
  • パラメータの調整とかうまくやらんとつるんと滑ったりしちゃう

サーバからゲームデータとってくる

通信タイミングと同期

  • いわゆるブラウザゲーみたいに毎回とってくるのは無難でセキュアにしやすいが、まあうっとおしい
    • ローカルにモデルを構築してそいつからとるようにするのは基本だろう
    • あとはそのモデルがいつ通信したり、クライアントでの進捗を同期しにいったりするかという問題になる

サーバ / クライアントのつなぎこみ

  • リクエスト投げる部分は一枚かます、パラメータ類はコンテキストオブジェクトにする、あたりは基本だろう

参考リンク

タッチイベント

  • タッチのたびに全オブジェクトなめるとかは重いので避けたい(Starling デフォだとそうなっちゃうと思う)
  • タッチマージンとかはつけられるようにしておく
    • スプライトのサイズと単純に一致するタッチ領域とかはイケてない

  • オブジェクトをまたぐスワイプとか絡んでくると面倒
    • タッチフィルター的オブジェクト1枚上にのせてそいつでどうにかするやり方もある
    • ただし画面がスケーリングしたときにちゃんとタッチ領域もついてこれるかは考慮しておくこと