Webゲームをスマホアプリ化したい。検討の末「Unity + Android + Spreadsheet + Firebase」にたどり着いた話

私は現在、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_idname_keyHPATKDEFEXP
goblin_001enemy_goblin100201030
slime_001enemy_slime305210
orc_001enemy_orc250352080

このように管理します。

これなら、

「敵全体の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シートを作ります。

keyjaenko
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
 ↓
不正な値ではないか検査
 ↓
Firestore

Cloud 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へ移行する一番大きな意味になるのではないかと思っています。

おすすめの記事