
私は現在、HTML / JavaScript / CSSでゲームを開発しています。
当初考えていたのは、かなり単純なものでした。
このゲームをiPhoneで遊べるようにして、できれば販売したい。
ところが調べ始めると、「iPhoneで動かす」だけならそれほど難しくない一方で、App Storeで配布するとなるとMac、Xcode、Apple Developer Programなどが関わってきます。
さらに考えていくうちに、
今作っている1本だけではなく、今後もゲームを作るなら、今回をきっかけに開発基盤そのものを作った方がいいのでは?
と思うようになりました。
最終的には、当面の方針を次のように決めました。
開発環境
Windows
ゲームエンジン
Unity / C#
最初の配布先
Android
ゲームデータ管理
Spreadsheet
↓
Importer
↓
Validator
↓
ScriptableObject
オンライン機能
Firebase
多言語化
Spreadsheet + Unity Localization
将来
同じUnityプロジェクトからiOS版も展開この記事では、なぜこの構成にしたのか、検討過程も含めて整理します。
そもそもUnityに移行する必要があるのか?
最初に考えたのは、現在のHTML / JavaScript / CSSゲームをそのままスマートフォン向けにする方法です。
方法はいくつかあります。
PWAとしてSafariなどから遊べるようにすることもできますし、Capacitorなどを使って既存Webアプリをネイティブアプリとして包む方法もあります。
「現在のゲームを最短でスマートフォンに持っていく」だけなら、こちらの方が速い可能性があります。
しかし、私の場合は今後もゲームやインタラクティブなコンテンツを作る予定があります。
そこで、
今回だけ最短で終わらせるより、Unityを覚えるための1本目にした方が長期的なメリットが大きいのではないか?
と考えました。
Unityなら、
Windows
Android
iOSへ同じプロジェクトから展開できます。
そこで現在のゲームをUnityで作り直すことにしました。
最初はWindows版を作る
私にはすでに比較的高性能なWindows PCがあります。
そのためUnity開発そのものはWindowsで行います。
まず、
Unity
↓
Windows Build
↓
.exeとしてゲームを完成させる。
その後、
同じUnity Project
↓
Android Build
↓
Androidアプリにします。
重要なのは、Windows版をAndroid版へ「変換」するわけではないということです。
同じUnityプロジェクトから、それぞれのプラットフォーム向けにビルドするという考え方です。
最初はiPhoneを考えていた。しかしMacが必要になる
最初のターゲットはiPhoneを考えていました。
UnityからiOS向けプロジェクトを生成し、
Unity
↓
iOS Build
↓
Xcode
↓
TestFlight
↓
App Storeという流れです。
問題はXcodeです。
iOSアプリを通常のUnity→Xcode経路でビルド・署名・公開するにはMacが必要になります。
つまり、Windows PCをすでに持っているにもかかわらず、iPhone版を公開するためにMacを購入する必要が出てきます。
Mac miniも検討しました。
16GBメモリ、512GB SSD程度あれば、Windowsをメイン開発機としてMacをiOSビルド用に使うには十分です。
MacBook Airも候補になりました。
しかし考えてみると、
まだゲームが1本も完成していない段階で、iOSビルドのために十数万円以上のMacを購入する必要があるのか?
という疑問が出てきました。
そこで「まずAndroid」に変更した
ここで方針を変えました。
Windows
↓
Unity
↓
Windows版完成
↓
Android版
↓
Google Playまずここまでやります。
AndroidならWindows環境だけでUnity開発からビルドまで進められます。
そしてゲームに手応えが出た段階でMacを購入し、
同じUnity Project
↓
iOS
↓
TestFlight
↓
App Storeへ進めばいい。
つまりMac購入そのものを、
iOS展開する価値があると判断したときの投資
にしました。
これは個人開発では結構重要な考え方だと思います。
「いつか必要になるから」という理由ですべての環境を最初に揃える必要はありません。
UnityではPrefabに全部設定するのか?
Unityのグラフィック周りについては多少経験がありました。
例えばゴブリンなら、
Goblin
↓
Material設定
↓
Animator
↓
Collider
↓
Script追加
↓
Prefab化という作業は理解しています。
小規模なゲームならPrefabのInspectorに、
HP 100
Attack 20
Defense 10
EXP 30と設定してもゲームは作れます。
しかしRPGなどを考えると、敵、アイテム、スキル、ステージが大量に増えます。
敵が200種類になったときに、200個のPrefabを開いてHPを調整するのは大変です。
そこで見た目・実体とゲームデータを分離することにしました。
PrefabとScriptableObjectを分ける
例えばゴブリンなら、
Goblin.prefab
────────────────
モデル
Material
Animator
Collider
EnemyController.csこちらは「実際にゲーム世界に登場するもの」です。
一方、
GoblinData
────────────────
ID goblin_001
HP 100
Attack 20
Defense 10
EXP 30
Gold 5こちらをScriptableObjectとして管理します。
ScriptableObjectは、簡単にいえば、
Unityプロジェクト内に保存できるゲーム設定データ
です。
PrefabをScriptableObjectが置き換えるわけではありません。
Prefab
=どう見えるか、どう動くか
ScriptableObject
=そのキャラクターがどんな設定なのかという分担です。
例えば同じGoblin Prefabを使いながら、
GoblinData
HP 100
StrongGoblinData
HP 250
GoblinKingData
HP 1000とデータだけ変えることもできます。
見た目や挙動が同じなら、Prefabを何個も作る必要がありません。
マスターデータはSpreadsheetで管理する
さらに考えたのが、
ScriptableObjectをUnity Editorから一個ずつ編集する必要があるのか?
という問題です。
RPGなら、
アイテム
敵
スキル
ステージ
経験値テーブル
ドロップテーブルなど大量の数値があります。
これは明らかに表計算ソフトの方が見やすい。
そこでSpreadsheetをゲームデータの原本にすることにしました。
例えばEnemiesシートなら、
| enemy_id | name_key | HP | ATK | DEF | EXP |
|---|---|---|---|---|---|
| goblin_001 | enemy_goblin | 100 | 20 | 10 | 30 |
| slime_001 | enemy_slime | 30 | 5 | 2 | 10 |
| orc_001 | enemy_orc | 250 | 35 | 20 | 80 |
このように管理します。
これなら、
「敵全体のHPを見ながらバランス調整する」
といった作業が圧倒的に楽になります。
Importerとは何か?
SpreadsheetとUnityをつなぐためにImporterを作ります。
Importerとは単純に、
SpreadsheetのデータをUnityへ取り込むC#プログラム
です。
Spreadsheet
↓
CSV / JSON
↓
Importer
↓
Unity例えば、
goblin_001 | HP 100 | ATK 20という行を読み、
GoblinData
ID goblin_001
HP 100
Attack 20というUnity側のデータへ変換します。
Importer自体もUnity内で動くC#です。
最終的にはUnity Editorに、
Tools
└ Master Data
├ Import All
├ Import Items
├ Import Enemies
└ Validate Allのような自分用ツールを作れます。
Validatorも作る
Spreadsheetの弱点は、人間が簡単に間違ったデータを入力できることです。
例えば、
goblin_001 HP = -100と入力してしまったり、
goblin_001
goblin_001とIDを重複させてしまったりします。
そこでImporterと一緒にValidatorを用意します。
Validatorは、
データに間違いがないか自動検査するC#プログラム
です。
例えば、
・IDが空ではないか
・IDが重複していないか
・HPがマイナスではないか
・存在しない敵IDをStageが参照していないか
・ドロップ率が正しい範囲かなどをチェックします。
その結果、
Spreadsheet
↓
Importer
↓
Validator
↓
ScriptableObject
↓
ゲームというパイプラインになります。
これは今回のゲームだけではなく、次にUnityでゲームを作るときにも再利用できる開発資産になります。
ID管理を徹底する
この設計でもう一つ重要なのがIDです。
例えば「鉄の剣」を、
鉄の剣という名前で参照しません。
weapon_iron_001のような変更しないIDを持たせます。
表示名が、
鉄の剣
↓
鋼鉄の剣に変わっても、
weapon_iron_001は変えません。
これによってセーブデータやステージデータとの参照関係が壊れにくくなります。
そして、このID方式は多言語対応にもつながります。
多言語対応もSpreadsheetで管理する
最初から日本語をコードに直接書かないことにしました。
例えば、
message.text = "ゴブリンを倒した!";とは書きません。
代わりに、
battle_enemy_defeatedという文章IDを使います。
SpreadsheetにはLocalizationシートを作ります。
| key | ja | en | ko |
|---|---|---|---|
| enemy_goblin | ゴブリン | Goblin | 고블린 |
| item_potion | ポーション | Potion | 포션 |
| menu_start | ゲーム開始 | Start Game | 게임 시작 |
ゲーム側は、
menu_startというKeyだけを知っています。
日本語設定なら、
ゲーム開始英語設定なら、
Start Gameを表示します。
UnityにはLocalization用の仕組みがあるので、
Spreadsheet
↓
Importer
↓
Unity Localization
↓
TextMeshPro / UIという構成にできます。
これなら後からAIに翻訳を手伝わせることも容易です。
重要なのは、多言語対応を完成直前に追加するのではなく、最初から文字列をKey化しておくことです。
ランキングはどうするか?
AndroidゲームなのでGoogle Play GamesのLeaderboardを使う方法もあります。
これは非常に簡単です。
ただし将来的にiPhone版を出した場合、
Android
↓
Google Play Games
iPhone
↓
Game Centerとなり、AndroidユーザーとiPhoneユーザーのランキングが分かれてしまいます。
そこで候補になったのがFirebaseです。
Firebaseとは?
FirebaseはGoogleが提供しているバックエンドサービスです。
簡単にいえば、
自分でサーバーを一から構築しなくても、オンラインゲームに必要な裏側の機能を作れる仕組み
です。
例えば、
Android ──┐
│
↓
Firebase
↑
│
iPhone ───┘とすれば、AndroidとiPhoneで同じデータを使えます。
Firebaseには、
Authentication
ユーザー認証
Firestore
データベース
Cloud Functions
サーバー側プログラム
Analytics
利用状況分析
Crashlytics
クラッシュ解析
Remote Config
ゲーム設定の遠隔変更などがあります。
最初から全部使う必要はありません。
今回ならまず、
Unity
↓
Firebase Authentication
↓
ユーザー識別
↓
Firestore
↓
ランキング程度から始めれば十分です。
Cloud Functionsは何のためにあるのか?
ランキングをUnityから直接データベースへ書き込ませると、不正が問題になります。
改造されたゲームから、
私のスコアは
999,999,999点ですと送信される可能性があるからです。
そこで重要な処理はスマートフォンではなくサーバー側に置きます。
Unity
↓
ゲーム結果送信
↓
Cloud Functions
↓
不正な値ではないか検査
↓
FirestoreCloud Functionsは、
Google側のコンピュータで実行されるプログラム
と考えると分かりやすいです。
Unity側はC#。
Cloud Functions側ではJavaScript/TypeScriptやPythonなどを使えます。
例えば将来的には、
ランキング登録
報酬付与
課金アイテム付与
イベント処理
不正チェックなど、ユーザー端末を信用してはいけない処理をサーバー側へ移せます。
最終的に決めたアーキテクチャ
ここまで検討して、現時点では次の構成で進めることにしました。
<code> Spreadsheet
│
┌─────────────┼─────────────┐
│ │ │
Items Enemies Localization
│ │ │
└─────────────┼─────────────┘
↓
Importer
↓
Validator
↓
ScriptableObject
│
↓
Unity / C# ゲーム
│
┌───────┴────────┐
↓ ↓
Prefab等 Firebase
│
┌────────┼────────┐
↓ ↓ ↓
認証 Ranking Save等
↓
Android版
│
まずここで公開
│
↓
手応えがあれば
│
↓
Mac購入
│
↓
iOS Build
│
↓
TestFlight
│
↓
App Store</code>AI時代なので「作業」そのものも自動化する
今回もう一つ意識しているのが、AIを単なるコード生成に使うだけではなく、開発ツールそのものを作らせることです。
例えば敵の画像を用意してSpreadsheetに、
goblin_001という敵を追加したとします。
将来的には、
敵画像をAssetsに追加
↓
Spreadsheetに敵を追加
↓
Unity
「Sync Master Data」
↓
CSV取得
↓
Importer
↓
Validator
↓
EnemyData生成
↓
Prefab生成・設定
↓
EnemyController設定
↓
Playくらいまで自動化できます。
つまり、
AIに毎回Goblin Prefabを作らせる
のではなく、
Goblin Prefabを自動生成するシステムをAIと一緒に作る
という考え方です。
こちらの方が、一度作った仕組みが次のゲームにも残ります。
今回考えてみて一番大きかったこと
当初は、
「HTMLゲームをどうやってiPhoneアプリにするか?」
という話でした。
しかし検討していくと、本当に考えるべきだったのは、
「今後ゲームを何本も作るための開発基盤をどう作るか?」
でした。
その視点に変わったことで、判断も変わりました。
最短でiPhoneへ持っていくことを優先するなら、Web技術をそのまま活用する方法もあります。
しかし今回は、
Unity
C#
Spreadsheet
Importer
Validator
ScriptableObject
Localization
Firebaseという技術を一度経験すること自体を、次のゲームへの投資と考えることにしました。
同時に、必要のない投資は後回しにしました。
Macもその一つです。
「iPhoneアプリをいつか出すからMacを買う」ではなく、
Android版を完成させ、iOSへ展開する段階になったら買う。
この順番なら、今持っているWindows PCを最大限利用できます。
同じような個人開発をする人へ
今回の検討を通じて感じたのは、最初から完璧なシステムを作る必要はないが、後から変更するのが大変な部分だけは最初に方向を決めておいた方がいいということです。
今回なら特に、
① データとPrefabを分離する
② Spreadsheetをマスターデータの原本にする
③ すべてIDで参照する
④ 表示文字列をコードに直接書かない
⑤ Android/iOS固有処理とゲーム本体を分離する
⑥ オンライン機能も将来クロスプラットフォーム化できる構造にする
このあたりです。
一方で、
高度なチート対策
巨大なバックエンド
完全自動CI/CD
iOSビルド環境
何百種類ものデータに対応したImporterなどは、本当に必要になってから追加すればいい。
最初は、
敵1種類
アイテム1種類
ステージ1つ
↓
Spreadsheet
↓
Importer
↓
Validator
↓
ScriptableObject
↓
Prefab
↓
実際にAndroidで動くという一本の流れを完成させる。
それが動いたら敵を10種類にする。
ランキングを追加する。
多言語化する。
そして公開する。
この順番で進める予定です。
おわりに
今回のスタート地点は、
「iPhoneでゲームを販売するにはどうすればいい?」
でした。
そこからMac、Xcode、TestFlight、Android、Unity、ScriptableObject、Spreadsheet、Firebase、多言語化まで話が広がりました。
結果として得られた答えは、単なる「iPhoneアプリの作り方」ではありませんでした。
まずWindows + Unityでゲームを作る。
Spreadsheetを中心にゲームデータを管理する。
Androidで最初の製品を公開する。
オンライン部分はAndroid/iOS双方から使える構造にする。
そしてiOS展開する価値が出たところでMacを導入する。
という、今後何本もゲームを作ることを前提にした開発方針になりました。
この1本を完成させることはもちろんですが、それと同じくらい、
2本目を1本目より速く作れる仕組みを残す
ことを今回の開発目標にしています。
それが、今回Unityへ移行する一番大きな意味になるのではないかと思っています。











