(:3[kanのメモ帳]

個人ゲーム開発者kan.kikuchiのメモ的技術ブログ。月木更新でUnity関連がメイン。

(:3[kanのメモ帳]


本ブログの運営者kan.kikuchiが個人で開発したゲームです!

    

エクセルやスプレッドシートを自動でScriptableObjectに変換するUnity-Excel-ImporterとUnity-QuickSheetの使い方と比較【Unity】【ScriptableObject】【エディタ拡張】


このエントリーをはてなブックマークに追加




この記事でのバージョン
Unity 2018.3.4f1


はじめに

エクセルやスプレッドシートでデータをまとめて、

ScriptableObjectに変換して使うというのはUnityではよくあります。


実はその変換部分を自動でやってくれるアセット(?)の有名所に

Unity-Excel-ImporterとUnity-QuickSheetという2つがあります。




2つあるとなると、やはりどちらを使うか悩むので、

今回はその2つを実際に使い、比較をしてみた感じの記事です!


両者で出来ることと出来ない事、それぞれの良い所

最初に分かりやすいように

両者で共通して出来る事出来ない事それぞれの良い所をを箇条書きにしてみました。

なお、詳細は次項以降の使い方で確認出来ると思います。


共通して出来る事

  • xls(xlsxも?)ファイルからScriptableObjectのクラスを自動作成
  • 変換処理を行うクラスも自動作成
  • xlsファイルを更新するとScriptableObjectも自動更新
  • シート毎に異なる形式のデータでも変換可能


共通して出来ない事

  • ScriptableObjectファイルの生成場所の指定


Unity-Excel-Importerの良い所

  • 日本語の解説がある
  • クラス名や変数名をファイルとは別に設定可能
  • 変換時に型を自動判定
  • 複数のシートを1つのScriptableObjectにまとめる事が可能


Unity-QuickSheetの良い所

  • 設定項目にshortやlong、Enumも使える
  • 配列の設定が1列で出来る
  • 生成されたScriptableObjectのInspectorがちょっと見やすい
  • スプレッドシートのダウンロードがエディタ上で出来る


基本的には簡単に使えるUnity-Excel-Importerがオススメで、

shortやlong、Enumが使いたい場合や、

スプレッドシートのダウンロードを行いたい場合はUnity-QuickSheetがオススメになります。


ただし、Unity-QuickSheetはクラス名と変数名がファイルでの設定と必ず同じになるため、

例えば「ScriptableObjectではisBossだけど、エクセルでは"ボスならTRUE、それ以外はFALSE"」

のように説明用の名前を設定することが出来ず、

自分以外の人(特に非プログラマー)と一緒にやる場合は結構使いにくいと思われます。


Unity-Excel-Importerの使い方


まずはUnity-Excel-Importerの使い方からです。





なお、エクセルの内容はモンスターのパラメータのような2つのシートを作成し、

intやstring、floatにboolといった複数の型の項目を作ってあります。

また、配列を指定したい場合は項目名に[]を付けて列を分けて設定します。


f:id:kan_kikuchi:20190418072542j:plain
f:id:kan_kikuchi:20190418072532j:plain


GitHubから取得してくると、unitypackageが入ってるので、これで簡単にインポート出来ます。


f:id:kan_kikuchi:20190415055539j:plain


インポートが完了したら、対象のxlsファイルを右クリックし、Create XLS Settingsを実行します。


f:id:kan_kikuchi:20190415055639j:plain


すると、ウィンドウが表示されるので、ここで各種設定をします。


f:id:kan_kikuchi:20190415061432j:plain

  • class name : 変換後のScriptableObjectのクラス名(デフォルトはxlsファイルの名前)
  • sepalate sheet : 有効にするとシートごとに別のScriptableObjectに分割します
  • sheet settings : 変換するシートの設定
  • parameter setting : 変換する項目とその型と変数名(自動で設定されるが、変更する事も可能)


なお、型にはbool, string, int, float, doubleとその配列が使えます。


f:id:kan_kikuchi:20190415061442j:plain


設定後、xlsのファイルをReimportするか、保存を行うと


f:id:kan_kikuchi:20190415061914j:plain


xlsファイルと同じ場所にScriptableObjectが生成されます。

もちろん、xlsファイルを更新すれば、ScriptableObjectも更新されます。


f:id:kan_kikuchi:20190415061922j:plain


また、ScriptableObjectや変換処理のプログラムはTerasurware/Class以下に生成されます。


f:id:kan_kikuchi:20190415061455j:plain


今回の例で生成されたScriptableObjectのプログラムは以下の通りで、

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Entity_Monster1 : ScriptableObject
{	
	public List<Sheet> sheets = new List<Sheet> ();

	[System.SerializableAttribute]
	public class Sheet
	{
		public string name = string.Empty;
		public List<Param> list = new List<Param>();
	}

	[System.SerializableAttribute]
	public class Param
	{
		
		public double No;
		public string Key;
		public string Name_JP;
		public string Name_En;
		public float HP;
		public int ATK;
		public int DEF;
		public int[] ItemNoArray;
		public bool BOSS;
	}
}


これを実際に使う場合にはこんな感じになると思います。

//生成されたScriptableObjectをInspectorで設定
public Entity_Monster1 MonsterData;
    
private void Start(){

  //各シートのデータを順に取り出す
  foreach (Entity_Monster1.Sheet monsterSheet in MonsterData.sheets) {
    //各項目のデータを順に取り出す
    foreach (Entity_Monster1.Param monsterParam in monsterSheet.list) {
      //モンスターの日本語名をログで表示
      Debug.Log(monsterParam.Name_JP);
    }
  }

}

f:id:kan_kikuchi:20190415062412j:plain


今回は同じデータ形式のシートが複数あるタイプでしたが、

別のデータ形式のシートが複数あるタイプを変換するUnity-Excel-Importerもあります。

例えばモンスターデータとアイテムデータを1つのエクセルで管理したい場合などに使えます。

なお、最初に紹介したリポジトリとは別のリポジトリになるので注意が必要です。






Unity-QuickSheetの使い方(xlsファイルがプロジェクト内にある場合)

次にUnity-QuickSheetのxlsファイルがプロジェクト内にある場合の使い方です。




なお、エクセルの内容はUnity-Excel-Importer時と基本的に同じですが、

ImprintTypeというenumが追加されています。

また、配列は1つの列にカンマ区切りで設定します。


f:id:kan_kikuchi:20190418074746j:plain
f:id:kan_kikuchi:20190418074755j:plain


GitHubから取得したら、Assets以下にあるQuickSheetをプロジェクトにそのまま追加します。


f:id:kan_kikuchi:20190415063151j:plain


その後、QuickSheet -> Tools -> Excelからファイルを作成


f:id:kan_kikuchi:20190415063844j:plain


作成したファイルを選択し、Inspector上でxlsファイルを指定し、Importを実行

f:id:kan_kikuchi:20190415064101j:plain


すると、各項目が表示されるので型を自分で指定します。(配列の場合はArrayにチェック)

なお、クラス名や変数名は変更できません。(エクセルでの設定がそのまま反映される)


f:id:kan_kikuchi:20190415064115j:plain


型の種類にはstring, short, int, long, float, double, enum, boolが使えます。


f:id:kan_kikuchi:20190415064212j:plain


ただし、enumを使いたい場合は先にenumを宣言しておく必要があります。

(今回の例だとImprintTypeというenumを先に宣言)

public enum ImprintType {
    Cute, Smart, Cool 
}


項目の設定が済んだら、パスの設定をし、Generateボタンを押します。

f:id:kan_kikuchi:20190415065354j:plain


すると指定したパスにスクリプトが生成されます。


f:id:kan_kikuchi:20190415065440j:plain


生成されるのは指定した1つのシート分だけなので、

複数のシートがある場合はWorksheetを切り替えて、再度Generateする必要があります。

(Monster1(Data)とMonster2(Data)はクラス名以外同じ内容になる)


f:id:kan_kikuchi:20190415065459j:plain
f:id:kan_kikuchi:20190415065514j:plain


また、シートのデータ形式を変える場合は設定ファイルも別にする必要があります。

(Monster1とMonster2で項目の種類が違う場合など)


f:id:kan_kikuchi:20190418081716j:plain


設定後、xlsのファイルをReimportするか、保存を行うと


f:id:kan_kikuchi:20190415065542j:plain


xlsファイルと同じ場所にScriptableObjectが生成されます。

もちろん、xlsファイルを更新すれば、ScriptableObjectも更新されます。

(ScriptableObjectのUpdateボタンで更新する事も可能)


f:id:kan_kikuchi:20190415065555j:plain


ちなみに、今回生成されたクラスのプログラムは以下のような感じで、

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;

///
/// !!! Machine generated code !!!
///
/// A class which deriveds ScritableObject class so all its data 
/// can be serialized onto an asset data file.
/// 
[System.Serializable]
public class Monster1 : ScriptableObject 
{	
    [HideInInspector] [SerializeField] 
    public string SheetName = "";
    
    [HideInInspector] [SerializeField] 
    public string WorksheetName = "";
    
    // Note: initialize in OnEnable() not here.
    public Monster1Data[] dataArray;
    
    void OnEnable()
    {		
//#if UNITY_EDITOR
        //hideFlags = HideFlags.DontSave;
//#endif
        // Important:
        //    It should be checked an initialization of any collection data before it is initialized.
        //    Without this check, the array collection which already has its data get to be null 
        //    because OnEnable is called whenever Unity builds.
        // 		
        if (dataArray == null)
            dataArray = new Monster1Data[0];

    }
    
    //
    // Highly recommand to use LINQ to query the data sources.
    //

}
using UnityEngine;
using System.Collections;

///
/// !!! Machine generated code !!!
/// !!! DO NOT CHANGE Tabs to Spaces !!!
/// 
[System.Serializable]
public class Monster1Data
{
  [SerializeField]
  int no;
  public int No { get {return no; } set { no = value;} }
  
  [SerializeField]
  string key;
  public string Key { get {return key; } set { key = value;} }
  
  [SerializeField]
  string name_jp;
  public string Name_JP { get {return name_jp; } set { name_jp = value;} }
  
  [SerializeField]
  string name_en;
  public string Name_En { get {return name_en; } set { name_en = value;} }
  
  [SerializeField]
  float hp;
  public float HP { get {return hp; } set { hp = value;} }
  
  [SerializeField]
  int atk;
  public int ATK { get {return atk; } set { atk = value;} }
  
  [SerializeField]
  int def;
  public int DEF { get {return def; } set { def = value;} }
  
  [SerializeField]
  int[] itemnoarray = new int[0];
  public int[] Itemnoarray { get {return itemnoarray; } set { itemnoarray = value;} }
  
  [SerializeField]
  ImprintType imprinttype;
  public ImprintType IMPRINTTYPE { get {return imprinttype; } set { imprinttype = value;} }
  
  [SerializeField]
  bool boss;
  public bool BOSS { get {return boss; } set { boss = value;} }
  
}


実際に使う場合はこんな感じになると思います。

//生成されたScriptableObjectをInspectorで設定
public Monster1 Monster1;
    
private void Start(){
  //各項目のデータを順に取り出す
  foreach (Monster1Data monster1Data in Monster1.dataArray) {
    //モンスターの日本語名をログで表示
    Debug.Log(monster1Data.Name_JP);
  }
}

f:id:kan_kikuchi:20190415070101j:plain


Unity-QuickSheetの使い方(スプレッドシートからダウンロードしてくる場合)

最後にUnity-QuickSheetのスプレッドシートからダウンロードしてくる場合の使い方です。

(xlsファイルがプロジェクト内にある場合とリポジトリは同じ)




なお、xlsファイルがプロジェクト内にある場合と同じデータをスプレッドシートに作成しています。


f:id:kan_kikuchi:20190418083135j:plain


まずはスプレッドシートを作ったのと同じアカウントで、

Google Developer Console(Google Cloud Platformと同じ?)にアクセスします。



ここでダッシュボードの左上の項目から、新しいプロジェクトを作成し、


f:id:kan_kikuchi:20190417061102j:plain


認証情報の同意画面を作成します。

(とりあえずアプリケーション名だけ設定して、保存でOK)


f:id:kan_kikuchi:20190417061545j:plain


その後、認証情報からOAuth クライアント IDの作成を行います。


f:id:kan_kikuchi:20190417061556j:plain
f:id:kan_kikuchi:20190417061607j:plain


ここで、クライアントID&シークレットが表示されますが、これを無視して、


f:id:kan_kikuchi:20190417061915j:plain


jsonをダウンロードし、プロジェクトに追加します。


f:id:kan_kikuchi:20190417063041j:plain


ここからプロジェクトに戻り、QuickSheet/GDataPlugin/Google Data Settingsを選択し、


f:id:kan_kikuchi:20190417063118j:plain


Json設定をした後(IDとSecretは自動で設定)、Start Authenticationを実行


f:id:kan_kikuchi:20190417063323j:plain


ブラウザで権限の許可を求められるので、これを許可。

f:id:kan_kikuchi:20190417063410j:plain


すると、アクセスコードが発行されるので、これをAccessCodeにコピペし、


f:id:kan_kikuchi:20190417063519j:plain
f:id:kan_kikuchi:20190417063421j:plain


Finish Authenticationを実行します。


f:id:kan_kikuchi:20190417064846j:plain


ついでにパスも設定しておきます。


f:id:kan_kikuchi:20190417063637j:plain


今度はQuickSheet/Tools/Googleを選択し、


f:id:kan_kikuchi:20190417064924j:plain


作成されたImport Settingsに

変換したいスプレッドシートとワークシートの名前を設定し、Importを実行します。


f:id:kan_kikuchi:20190417065119j:plain
f:id:kan_kikuchi:20190417065129j:plain


あとはxlsファイルがプロジェクト内にある場合と同様に項目の設定をし、Generateを実行し、

f:id:kan_kikuchi:20190417065140j:plain


プログラムが生成されます。


f:id:kan_kikuchi:20190417065203j:plain


すると、Google/Monster1という項目が追加されるので、これで、ScriptableObjectを作成。

(Mosnter1はクラス名)


f:id:kan_kikuchi:20190417065917j:plain


このScriptableObjectにはDownloadボタンがあるので、ここを押せばデータがロード&変換されます。

(スプレッドシート自体はプロジェクトに保存されない)


f:id:kan_kikuchi:20190417065400j:plain
f:id:kan_kikuchi:20190417065410j:plain


なお、ScriptableObjectの使い方は前項と同じなので割愛します。


おわりに

記事の通り、どちらも便利なのは間違いないですが、

場合によってはちょっと痒い所に手が届かないという事にもなりそうな感じでした。


もし、どうしてもこの2つでは満足できないという場合はおそらくこれらを改良するより、

普通に専用の変換処理とScriptableObjectのクラスを作った方が楽かと思います。


今回紹介した2つのように汎用的なやつを作るのは大変ですが、

専用のやつを作るのはそんなに大変じゃなかったりするので。

(もしかしたらこのへんも記事にするかも)