ゲームの設定データをスプレッドシートで管理して、Jsonを介してUnityに取り込む
ここ2年ほど空き時間でちょっとづつ開発している個人開発のゲームがあるのですが、ステージ作りが一向に捗らないのでステージをつくるための仕組みを作るのに力を入れてみてます。
取り急ぎゲーム内で使用している固定の値をせっせと入力していたら時間がかかって仕方ないし、構造を変更したりとか、何かのタイミングでいなくなってしまったりする事もあるので、 スプレッドシート経由で更新できるように 手を加えてみたのですが、忘れたころにまた使う事になりそうなのでメモ。
スプレッドシートでの作業
スプレッドシートでデータを作成
まずスプレッドシートで元となるデータを作成します。 例えばモンスターの情報をこんな感じでズラッと列挙します。
Google Apps Script でJsonに変換
ツール -> スクリプトエディタ
でスクリプトエディタを開きます。
Google Apps Scriptは下記のような感じで書いてみました。
function getData(sheetName) {
var sheet = SpreadsheetApp.getActive().getSheetByName(sheetName);
var rows = sheet.getDataRange().getValues();
var keys = rows.splice(0, 1)[0];
return rows.map(function(row) {
var obj = {};
row.map(function(item, index) {
obj[String(keys[index])] = String(item);
});
return obj;
});
}
function toJson() {
var output = {};
output['monsters'] = getData('Monsters');
Browser.msgBox(JSON.stringify(output));
return output;
}
function doGet(request) {
return ContentService.createTextOutput(JSON.stringify(toJson()))
.setMimeType(ContentService.MimeType.JSON);
}
ざっくり説明を入れると、
Monsters
となっているところは上で作っていたシートの名前になります。(複数シートを使用したい場合に増やせるようにこの構成にしています)toJson
関数はブラウザでデバッグしたい時に実行 -> 関数を実行 -> toJson
で実行すればスプレッドシート上で結果が確認できます(API化が面倒ならこの関数で表示されるメッセージからjsonを作ってもOK)doGet
関数はJSONを返すAPIが叩く用のものになります
次に 公開 -> ウェブアプリケーションとして導入...
を選択。
適当な名前をつけて、最後に 導入
をクリック。
最後に OK
ボタンで保存されます。
あとはブラウザで上記URLにアクセスすると下記のデータが取得できます。
{
"monsters": [
{ "id": "0", "name": "スライム", "description": "もぞもぞ動くやつ。綺麗好き。", "hp": "10", "gold": "5" },
{ "id": "1", "name": "ゴブリン", "description": "知性は低め。単純作業に向いている。", "hp": "20", "gold": "20" },
{ "id": "2", "name": "動く鎧", "description": "ダンジョンの警備員。メンテ不要。", "hp": "30", "gold": "30" }
]
}
Unityでの作業
JsonデータをUnityの ScriptableObject
に読み込ませる方法はこちらを参考にさせていただきました。
Excelでマスターデータを作り、JsonでScriptableObjectに読み込ませる備忘録 | ビールが飲みたい。
StreamingAssetsディレクトリに書き出したデータをJsonを格納
先ほどのデータをUnityプロジェクトに取り込みます。
/Assets/StreamingAssets/data.json
みたいな感じで入れておきます。
ScriptableObjectを作成する
Jsonからロードしたデータを入れておく実体となる ScriptableObject
のクラスを作成します。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//ScriptableObject作成
[CreateAssetMenu]
public class GameData : ScriptableObject
{
public GameMasterData master;
}
データ構造に応じたクラスを作成する
先ほどの ScriptableObject
で使用していた、スプレッドシート上で作ったデータ構造をUnity上で扱いやすくするためのクラス GameMasterData
を作成します。
//シート毎のデータ形式
[Serializable]
public class GameMasterData
{
public List<MonsterItem> monsters = new List<MonsterItem>();
}
//行毎のデータ形式
[Serializable]
public class MonsterItem
{
public int id;
public string name;
public string description;
public int hp;
public int gold;
}
スプレッドシートを複数使用するような元データを作っている場合には GameMasterData
クラスに追加でシート名の List
を作成して、そのシートのデータの構造に合わせて MonsterItem
みたいなクラスを作成します(同じ構造ならそれっぽい名前に変更して使いまわしてもよさそうだけど)
Resoucesディレクトリにマスターデータ用のオブジェクトを作成する
前述の ScriptableObject
クラスを作成すると、Project -> Create
からオブジェクトを作成できるようになっているので、下記の要領で Resouces/GameMasterData
を作成します。
データは空ですが、ここにjsonからロードされた情報が上書きされます。
Jsonから上記ScriptableObjectにデータをロードする
参考元のサイトでは、LoadMasterDataFromJson
というクラスを作成した上でゲームを再生して、デバッグ用のボタンを表示して実行されていますが、僕はインスペクタ上にボタンを表示させられるようにEditorスクリプトを書きました。
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
public class GameDataLoader : MonoBehaviour
{
public string fileName = "data.json";//StreamingAssets内のファイル
public const string DATA_NAME = "GameMasterData";//Resouces内のファイル
//jsonからResourcesにあるデータを上書きする
public void Load()
{
GameData gameData = Resources.Load<GameData>(DATA_NAME);
gameData.master = new GameMasterData();
gameData.master = JsonUtility.FromJson<GameMasterData>(JsonHelper.GetJsonFile("/", fileName, true));
Debug.Log(fileName + "をロードしました");
//MEMO: そのままではオブジェクトが保存されないので、一度配列を追加するとか編集する事
}
}
上記の Load
関数をインスペクタ上に表示できるよう下記みたいな感じでスクリプトを書きます。
using UnityEditor;
using UnityEngine;
#if UNITY_EDITOR
[CustomEditor(typeof(GameDataLoader))]
public class GameDataLoaderEditor : Editor
{
public override void OnInspectorGUI()
{
ase.OnInspectorGUI();
if (GUILayout.Button("Load"))
{
GameDataLoader t = target as GameDataLoader;
t.Load();
}
}
}
#endif
JsonHelper
クラスは参考元のサイトのものをほぼそのまま使用させていただいています。
GameDataLoaderを実行
あとはこれをゲームオブジェクトにアタッチしてLoadボタンをクリックします。
Jsonの情報がマスターデータに反映されました。
データを利用する
ゲームのプログラムでは先ほどのマスターデータから値を読み込んで利用するだけです。
下記みたいに Resources
からロードすれば簡単に利用できます。
GameData gameData = Resources.Load<GameData>("GameDataMaster");
print(gameData.master.monsters.Count);
先ほどの GameDataLoader.cs
とかに static
な関数を利用して簡単に呼び出せるようにしておく事も可能です。
private static GameData entity;
public static GameMasterData Entity
{
get
{
if (entity == null)
{
entity = Resources.Load<GameData>(DATA_NAME);
if (entity == null)
{
Debug.LogError(DATA_NAME + " not found");
}
}
return entity.master;
}
}
こうしておけば下記みたいな感じで利用する事が可能になります。 初回にキャッシュしたオブジェクトを利用できるのでこっちの方が良さそう。
print(GameDataLoader.Entity.monsters.Count);
サンプルプロジェクトを下記にアップロードしておきました。
https://github.com/is8r/example-JsonImport
来年こそはリリースしたい…
Comment
comments powered by Disqus