ビールが飲みたい。

ゲーム開発の備忘録などを書きます。

Excelでマスターデータを作り、JsonでScriptableObjectに読み込ませる備忘録

はじめに

この記事は、Excelでマスターデータを作り、jsonとして出力し、UnityのJsonUtilityを利用してScriptableObjectに読み込ませるまでの一連の流れを列挙した備忘録です。

暇ができたから記事を書いたものの、ExcelをUnityで直接読み込んだりした方が楽なので、特に理由が無ければこちらの記事等を参考にされた方が良いかと思います。

robamemo.hatenablog.com

ExcelからJSONを出力したかったり、JSONを読み込ませたかったり、サーバーからJSONAPIで受け取る予定だけどまだサーバー準備中だから......みたいなちょっと変わった事情がある方が参考になるかなぁという感じの記事です。

そういうやり方もできるンダナーくらいの流し読み推奨。

※10/4 12:00 追記

ScriptableObjectの何が良いのかってスライドをUnity様が丁度同じ日にアップしていました。

www.slideshare.net

Excel等からScriptableObjectを生成する方法も少し記述されていて、こちらの手法が使えればよりスマートに事が進むと思います。

バージョン情報

  • Excel 2016
  • Unity 2017.1.0f3

ExcelからJSONで出力する

Excelを開き、item.xlsmなど適当な名前で保存します。
.xlsx形式だと保存後に書いたマクロが消えてしまうのでご注意下さい。

まずはマスターデータを作ります。 f:id:eggame:20171003165153p:plain 今回はこんな感じ。

次にテーブルをjsonで出力出来るよう、vbaを書きます。
f:id:eggame:20171003171618g:plain

  • メニューの[開発] > [Visual Basic]をクリック
  • [挿入] > [標準モジュール] を選択肢し、Module1を作成
  • マクロを記述(今回動かしたコードは下記に記載しました。)
  • utf-8で保存したいため、とあるライブラリを使用します。
     [ツール] > [参照設定] から
    Microsoft ActiveX Data Objects
    の適当なバージョンにチェック
  • 実行ボタンよりマクロを実行

targetFilePath変数に出力時のファイル名が設定されています。 コードは下記の通り。

Option Explicit

Sub CreateJson()

  Dim stringData As String
  Dim targetFilePath As String
  
  Dim sheetName As String
  Dim key As String
  Dim value As String
  
  Dim row As Integer
  Dim col As Integer
    
'****
' Init
'****
  targetFilePath = ThisWorkbook.Path & "\item.json"
  
  'ファイルを一旦削除
  If Dir(targetFilePath) <> "" Then
    Kill targetFilePath
  End If


'****
' 処理
'****
  Dim stm As ADODB.Stream
  Set stm = New ADODB.Stream
  
  stm.Charset = "UTF-8"
  stm.LineSeparator = adLF
  stm.Open

  
  sheetName = "Sheet1"
  'ループを開始する行番号を入れて下さい。
  row = 3
  
  stringData = stringData + "{"
  stringData = stringData + """" + "item" + """" + ":["
    
  Do
    stringData = stringData + "{"
    
    col = 1
    Do
      
      key = """" + CStr(Worksheets(sheetName).Cells(2, col).value) + """"
      value = """" + CStr(Worksheets(sheetName).Cells(row, col).value) + """"
      stringData = stringData + key + ":" + value
      
      col = col + 1
      
      If IsEmpty(Worksheets(sheetName).Cells(row, col).value) = False Then
        stringData = stringData + ","
      End If
      
    Loop Until IsEmpty(Worksheets(sheetName).Cells(row, col).value) = True

    row = row + 1
    
    If IsEmpty(Worksheets(sheetName).Cells(row, 1).value) = True Then
      stringData = stringData + "}"
    Else
      stringData = stringData + "},"
    End If
    
  Loop Until IsEmpty(Worksheets(sheetName).Cells(row, 1).value) = True
  
  stringData = stringData + "]}"
  
  stm.WriteText stringData, adWriteLine
  stm.SaveToFile targetFilePath, adSaveCreateOverWrite
  stm.Close
  
  MsgBox ("出力完了")
  
  End Sub

詳しくは解説しませんが、stringData変数にテーブルから読み取ったデータをどんどん繋げていき、最後にtargetFilePathで設定したファイルへまとめて書き込みます。
この設定の場合、jsonファイルはxlsmファイルと同じディレクトリに出力されます。

以下のようなjsonファイルが作成されます。(出力後に整形しました。)

item.json

{
  "item": [
    {
      "idx": "item1",
      "name": "木の棒",
      "discription": "故郷の木の枝を拾いました。",
      "hp": "0",
      "attack": "1",
      "diffence": "1",
      "speed": "1",
      "assetbundle": "item_wood"
    },
    {
      "idx": "item2",
      "name": "竹やり",
      "discription": "一般的な武器です。",
      "hp": "0",
      "attack": "2",
      "diffence": "2",
      "speed": "2",
      "assetbundle": "item_bamboo_spear"
    },
    {
      "idx": "item3",
      "name": "錆びたナイフ",
      "discription": "この竜の紋章は何でしょう。",
      "hp": "0",
      "attack": "3",
      "diffence": "1",
      "speed": "3",
      "assetbundle": "item_rusted_knife"
    },
    {
      "idx": "item4",
      "name": "なべのふた",
      "discription": "投げたい。",
      "hp": "5",
      "attack": "0",
      "diffence": "5",
      "speed": "0",
      "assetbundle": "item_pot_lid"
    }
  ]
}

最後に、シートにボタンを用意し、押すと出力されるようにしておくと少し楽になります。 f:id:eggame:20171003171816g:plain

  • [開発] > [挿入] > [ボタン]をクリック
  • クリック長押ししながら適当なサイズへマウスを移動
  • マウスを離すと機能を登録するウィンドウが出現するので、処理を書いたプロシージャ名を選択

うまく設定出来れば、クリックするとjsonが出力されるようになります。

JSONを読み込むための準備

ここからUnityでの作業に移ります。

読み込むjsonの形と一致するよう、マスターデータクラスを作ります。
フィールド名とjsonのkeyが一致していないと無視されてしまうので、ご注意下さい。
JsonUtilityでシリアライズするため、各クラスへ[Serializable]をつけます。

ItemMasterData.cs

using System;
using System.Collections.Generic;

[Serializable]
public class ItemMasterData{

    public List<ItemData> item = new List<ItemData>();

    [Serializable]
    public class ItemData
    {
        public string idx;
        public string name;
        public string discription;
        public int hp;
        public int attack;
        public int defense;
        public int speed;
        public string assetbundle;
    }
}

マスターデータを入れるScriptableObjectを作ります。

ItemSO.cs

using UnityEngine;

[CreateAssetMenu]
public class ItemSO : ScriptableObject {
    public ItemMasterData itemMasterData;
}

ScriptableObjectの作り方は色々あると思いますが(マスターデータ用クラスと統合しちゃったり)、今回はわけて作りました。

jsonファイルをStringで読み込むスクリプトを用意します。

JsonHelper.cs

using UnityEngine;
using System;
using System.IO;
using System.Text;

public class JsonHelper
{
    /// <summary>
    /// JSONファイルをStringで読み込みます。
    /// </summary>
    /// <param name="filePath">streamingAssetsフォルダからのパス</param>
    /// <param name="fileName">ファイル名</param>
    /// <returns>jsonのstringデータ</returns>
    public static String GetJsonFile(String filePath, String fileName)
    {
        string fileText = "";

        // Jsonファイルを読み込む
        FileInfo fi = new FileInfo(Application.streamingAssetsPath + filePath + fileName);
        try
        {
            // 一行毎読み込み
            using (StreamReader sr = new StreamReader(fi.OpenRead(), Encoding.UTF8))
            {
                fileText = sr.ReadToEnd();
            }
        }
        catch (Exception e)
        {
            // 改行コード
            fileText += e + "\n";
        }

        return fileText;
    }
}

ボタンをクリックするとScriptbleObjectへjsonデータが読み込めるスクリプトを作ります。

LoadMasterDataFromJson.cs

using UnityEngine;

public class LoadMasterDataFromJson : MonoBehaviour {

    public ItemSO itemSO;

    private void Awake()
    {
        if (!itemSO)
        {
            itemSO = Resources.Load<ItemSO>("MasterData/ItemMasterData");
        }
    }

    private void LoadFromJson()
    {
        itemSO.itemMasterData = new ItemMasterData();
        itemSO.itemMasterData = JsonUtility.FromJson<ItemMasterData>(JsonHelper.GetJsonFile("/","item.json"));
    }

    private void OnGUI()
    {
        if(GUI.Button(new Rect(20, 20, 100, 50), "読み込み"))
        {
            LoadFromJson();
        }
    }
}

フォルダを作っていきます。

今回はScriptableObjectをResources.Loadで読み込むことを想定し、
Resources/MasterDataの中に格納します。

jsonファイルをStreamingAssetsフォルダから読み込むため、StreamingAssetsフォルダを作成し、item.jsonを格納します。

これで下準備完了です。

JSONをScriptableObjectへ読み込む

データを入れるScriptableObjectを作ります。
MasterDataフォルダを右クリックし、[Create] > [Item SO]を選択すると、MasterDataフォルダ内に新規オブジェクトが生成されます。
名前をItemMasterDataにしておきます。

ファイル名は、LoadMasterDataFromJson.csでの読み込み処理に影響が出ますので、別の名前にする際はLoadMasterDataFromJson.csを適宜編集して下さい。

f:id:eggame:20171003174704g:plain

LoadMasterDataFromJson.csを適当なGameObjectに追加し、シーンを再生するとGame画面にGUIボタンが出現します。
GUIボタンをクリックするとScriptableObjectにjsonファイルからデータが読み込まれます。

f:id:eggame:20171003175524g:plain

使用後、LoadMasterDataFromJson.csを使わないときはInstectorのチェックを外したり、削除したりすれば動かなくなります。

おわりに

VBAなどハードコーディングしちゃってる部分もあり、ちょっとコードがわかりづらいかもしれません。すいません。
何かあればコメント欄にお願いします。