ゲームの設定データをスプレッドシートで管理して、Jsonを介してUnityに取り込む

2018-12-28 #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


来年こそはリリースしたい…

Profile

石原 悠 / Yu Ishihara

東京都杉並区在住。デザインとプログラミングとヨーグルト作りが好きです。