五回目の投稿です。よろしくお願いします。
目次
概要
以下のような、指定したスプレッドシートを読み込み、それをCSVファイルとして出力するEditorWindowの作成方法を解説します :
(GSSReaderTool : EditorWindow)
効用
- Unity外でデータ管理をするので、非プログラマでも編集しやすく、共同編集も可能
- アプリ配信後でもデータの編集が可能
- 1列目を"id"にしておくことで、idからその行のデータセットを指定するといった運用が可能 (詳しくは"最後に"を参照)
事前準備
事前にスプレッドシート(GSS)を作成しておきます。
- GSSをGoogleドライブなどの共有可能な場所に作成
- セルの値やページ名を上記のようにしておく。
- GSSの画面右上の共有ボタンから、アクセス制限を以下のようにしておく :
また、エディタ上でコルーチンを実行するために"Editor Coroutines"パッケージを導入しています。パッケージマネージャーからインストールしておいてください。
使用方法
ここでは使用方法を説明します。作成方法に興味のない方は、ここを読むことで使用できるようになっています。
1. 下記スクリプトを作成する :
GssReaderTool.cs (tap)
#if UNITY_EDITOR
using System.Collections;
using UnityEngine.Networking;
using UnityEngine;
using System.IO;
using UnityEditor;
using Unity.EditorCoroutines.Editor;
namespace App.ReadCsv
{
public class GssReaderTool : EditorWindow
{
private const string CSV_FOLDER_PATH = "Assets/MasterData/CSV/";
private const string GSS_FORMAT = "tqx=out:csv";
private string gssSheetId;
private string gssSheetName;
private string csvfileName;
private string log;
private bool isLoadingGss;
[MenuItem("Tools/GSS Reader Tool")]
public static void OpenWindow()
{
GetWindow<GssReaderTool>("GSS読み込みツール");
}
private void OnGUI() // 描画処理
{
GUILayout.Label("[GSSの読み込み, CSVファイルの作成]");
gssSheetId = EditorGUILayout.TextField("・ GSSのシートID :", gssSheetId);
gssSheetName = EditorGUILayout.TextField("・ GSSのシート名 :", gssSheetName);
csvfileName = EditorGUILayout.TextField("・ 作成するファイル名 :", csvfileName);
if (GUILayout.Button("実行"))
{
log = string.Empty;
StartLoadGss(gssSheetId, gssSheetName);
}
DrawLogGUI();
}
/// <summary>
/// GSSの読み込み
/// </summary>
public void StartLoadGss(string id, string name)
{
if (isLoadingGss) // GSS読み込み中は実行しない
{
AddLog("Error : GSSを読み込み中...");
return;
}
else if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(name) || string.IsNullOrEmpty(csvfileName))
{
AddLog("Error : 設定項目をすべて記入してください。");
return;
}
var url = "https://docs.google.com/spreadsheets/d/" + id + "/gviz/tq?" + GSS_FORMAT + "&sheet=" + name;
EditorCoroutineUtility.StartCoroutine(LoadGss(url), this);
}
private void AddLog(string message)
{
log += $" - {message}\n";
}
private IEnumerator LoadGss(string url)
{
using (var request = UnityWebRequest.Get(url)) // GSSの読み込み
{
AddLog("GSSの読み込み開始");
isLoadingGss = true;
yield return request.SendWebRequest();
isLoadingGss = false;
if (request.result == UnityWebRequest.Result.ProtocolError)
{
AddLog($"Error Load GSS : {request.error} (GSSの存在性と公開設定を確認して下さい)");
yield break;
}
else if (request.result == UnityWebRequest.Result.ConnectionError)
{
AddLog($"Error Load GSS : {request.error} (ネットワークに接続されているかを確認して下さい)");
yield break;
}
AddLog("GSSの読み込み完了");
CreateCsv(csvfileName, request.downloadHandler.text);
}
}
/// <summary>
/// CSVファイルの作成
/// </summary>
private void CreateCsv(string fileName, string data)
{
if (string.IsNullOrEmpty(data))
{
AddLog("Error Create CSV : データが空であるため、ファイルを作成できませんでした。");
return;
}
AddLog("CSVファイルの作成開始");
if (!Directory.Exists(CSV_FOLDER_PATH)) // フォルダがなければ、
{
Directory.CreateDirectory(CSV_FOLDER_PATH); // 新規作成
}
var path = CSV_FOLDER_PATH + fileName + ".csv";
using (var sw = new StreamWriter(path, false)) // 新規作成 or 上書き
{
try
{
sw.Write(data);
AssetDatabase.Refresh(); // エディタ上のアセットの更新
AddLog($"CSVファイルの作成完了 : {path}");
}
catch (System.Exception e)
{
AddLog(e.ToString());
}
}
}
/// <summary>
/// ログを出力
/// </summary>
private void DrawLogGUI()
{
if (string.IsNullOrEmpty(log)) { return; }
GUILayout.FlexibleSpace();
GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(3));
GUILayout.Label(" [Log] ");
GUILayout.Label(log, new GUIStyle() { wordWrap = true, normal = new GUIStyleState() { textColor = new Color(1, 1, 1, 1) } });
if (GUILayout.Button("Clear Log"))
{
log = string.Empty;
}
GUILayout.Space(7);
}
}
}
#endif
2. Unityエディタ上で"Tools/GSS Reader Tool"を選択する :
3. すると、下記EditorWindowが出現するので、"GSSのシートID"と"GSSのシート名"と"作成するCSVファイル名"を指定し、実行ボタンを押すと、GSSを読み込み、そのCSVファイルが出力される :
- GSSのシートIDの場所は、GSSのリンクの下記の部分 :
https://docs.google.com/spreadsheets/d/{GSSのシートID}/edit#gid=0 - CSVファイルの出力先フォルダ (なければ自動で作成) :
Assets/MasterData/CSV
作業手順
1. Unityエディタ上からEditorWindowを開けるように実装
2. 動作に必要な最低限の描画処理の実装
3. GSSの読み込み処理の実装
4. 読み込んだデータをCSVファイルとして出力する処理の実装
5. 実行中の処理に関するログ出力の実装
実装方法
1. Unityエディタ上からEditorWindowを開けるように実装
以下のように、GssReaderTool.csを作成する :
GssReaderTool.cs (tap)
#if UNITY_EDITOR
using UnityEditor;
namespace App.ReadCsv
{
public class GssReaderTool : EditorWindow
{
[MenuItem("Tools/GSS Reader Tool")]
public static void OpenWindow()
{
GetWindow<GssReaderTool>("GSS読み込みツール");
}
private void OnGUI() // 描画処理
{
}
}
}
#endif
ポイント:
- #if UNITY_EDITOR : Unityエディタ上でしか動作しないようにしている。
- MenuItem属性 : Unityエディタ上のToolsに項目を追加している。
- GetWindow() : EditorWindowを開けるようにしている。
2. 動作に必要な最低限の描画処理の実装
以下のように、GssReaderTool.csに追記する :
GssReaderTool.cs (tap)
#if UNITY_EDITOR
+ using UnityEngine;
using UnityEditor;
namespace App.ReadCsv
{
public class GssReaderTool : EditorWindow
{
+ private string gssSheetId;
+ private string gssSheetName;
+ private string csvfileName;
[MenuItem("Tools/GSS Reader Tool")]
public static void OpenWindow()
{
GetWindow<GssReaderTool>("GSS読み込みツール");
}
private void OnGUI() // 描画処理
{
+ GUILayout.Label("[GSSの読み込み, CSVファイルの作成]");
+ gssSheetId = EditorGUILayout.TextField("・ GSSのシートID :", gssSheetId);
+ gssSheetName = EditorGUILayout.TextField("・ GSSのシート名 :", gssSheetName);
+ csvfileName = EditorGUILayout.TextField("・ 作成するファイル名 :", csvfileName);
if (GUILayout.Button("実行"))
{
// GSSの読み込み処理
}
}
}
}
#endif
ポイント:
- EditorGUILayout.TextField() : インプットフィールドを用いて、データを渡せるようにしている。
3. GSSの読み込み処理の実装
以下のように、GssReaderTool.csに追記する :
GssReaderTool.cs (tap)
#if UNITY_EDITOR
+ using System.Collections;
using UnityEngine;
+ using UnityEngine.Networking;
using UnityEditor;
+ using Unity.EditorCoroutines.Editor;
namespace App.ReadCsv
{
public class GssReaderTool : EditorWindow
{
+ private const string GSS_FORMAT = "tqx=out:csv";
private string gssSheetId;
private string gssSheetName;
private string csvfileName;
+ private bool isLoadingGss;
[MenuItem("Tools/GSS Reader Tool")]
public static void OpenWindow()
{
GetWindow<GssReaderTool>("GSS読み込みツール");
}
private void OnGUI() // 描画処理
{
GUILayout.Label("[GSSの読み込み, CSVファイルの作成]");
gssSheetId = EditorGUILayout.TextField("・ GSSのシートID :", gssSheetId);
gssSheetName = EditorGUILayout.TextField("・ GSSのシート名 :", gssSheetName);
csvfileName = EditorGUILayout.TextField("・ 作成するファイル名 :", csvfileName);
if (GUILayout.Button("実行"))
{
+ StartLoadGss(gssSheetId, gssSheetName);
}
}
+ /// <summary>
+ /// GSSの読み込み
+ /// </summary>
+ public void StartLoadGss(string id, string name)
+ {
+ if (isLoadingGss) // GSS読み込み中は実行しない
+ {
+ return;
+ }
+ else if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(name) || string.IsNullOrEmpty(csvfileName))
+ {
+ return;
+ }
+
+ var url = "https://docs.google.com/spreadsheets/d/" + id + "/gviz/tq?" + GSS_FORMAT + "&sheet=" + name;
+ EditorCoroutineUtility.StartCoroutine(LoadGss(url), this);
+ }
+ private IEnumerator LoadGss(string url)
+ {
+ using (var request = UnityWebRequest.Get(url)) // GSSの読み込み
+ {
+ isLoadingGss = true;
+ yield return request.SendWebRequest();
+ isLoadingGss = false;
+
+ if (request.result == UnityWebRequest.Result.ProtocolError)
+ {
+ yield break;
+ }
+ else if (request.result == UnityWebRequest.Result.ConnectionError)
+ {
+ yield break;
+ }
+ }
+ }
}
}
#endif
ポイント:
- isLoadingGss : GSS読み込み中は、読み込みを開始できないようにするフラグ
- UnityWebRequest.Get() : URLを指定し、GSSを取得
- usingステートメントを用いて、外部リソースを解放
4. 読み込んだデータをCSVファイルとして出力する処理の実装
以下のように、GssReaderTool.csに追記する :
GssReaderTool.cs (tap)
#if UNITY_EDITOR
using System.Collections;
+ using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using UnityEditor;
using Unity.EditorCoroutines.Editor;
namespace App.ReadCsv
{
public class GssReaderTool : EditorWindow
{
+ private const string CSV_FOLDER_PATH = "Assets/MasterData/CSV/";
private const string GSS_FORMAT = "tqx=out:csv";
private string gssSheetId;
private string gssSheetName;
private string csvfileName;
private bool isLoadingGss;
[MenuItem("Tools/GSS Reader Tool")]
public static void OpenWindow()
{
GetWindow<GssReaderTool>("GSS読み込みツール");
}
private void OnGUI() // 描画処理
{
GUILayout.Label("[GSSの読み込み, CSVファイルの作成]");
gssSheetId = EditorGUILayout.TextField("・ GSSのシートID :", gssSheetId);
gssSheetName = EditorGUILayout.TextField("・ GSSのシート名 :", gssSheetName);
csvfileName = EditorGUILayout.TextField("・ 作成するファイル名 :", csvfileName);
if (GUILayout.Button("実行"))
{
StartLoadGss(gssSheetId, gssSheetName);
}
}
/// <summary>
/// GSSの読み込み
/// </summary>
public void StartLoadGss(string id, string name)
{
if (isLoadingGss) // GSS読み込み中は実行しない
{
return;
}
else if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(name) || string.IsNullOrEmpty(csvfileName))
{
return;
}
var url = "https://docs.google.com/spreadsheets/d/" + id + "/gviz/tq?" + GSS_FORMAT + "&sheet=" + name;
EditorCoroutineUtility.StartCoroutine(LoadGss(url), this);
}
private IEnumerator LoadGss(string url)
{
using(var request = UnityWebRequest.Get(url)) // GSSの読み込み
{
isLoadingGss = true;
yield return request.SendWebRequest();
isLoadingGss = false;
if (request.result == UnityWebRequest.Result.ProtocolError)
{
yield break;
}
else if (request.result == UnityWebRequest.Result.ConnectionError)
{
yield break;
}
+ CreateCsv(csvfileName, request.downloadHandler.text);
}
}
+ /// <summary>
+ /// CSVファイルの作成
+ /// </summary>
+ private void CreateCsv(string fileName, string data)
+ {
+ if (string.IsNullOrEmpty(data))
+ {
+ return;
+ }
+
+ if (!Directory.Exists(CSV_FOLDER_PATH)) // フォルダがなければ、
+ {
+ Directory.CreateDirectory(CSV_FOLDER_PATH); // 新規作成
+ }
+
+ var path = CSV_FOLDER_PATH + fileName + ".csv";
+ using (var sw = new StreamWriter(path, false)) // 新規作成 or 上書き
+ {
+ try
+ {
+ sw.Write(data);
+ AssetDatabase.Refresh(); // エディタ上のアセットの更新
+ }
+ catch (System.Exception e)
+ {
+
+ }
+ }
+ }
}
}
#endif
ポイント:
- CSV_FOLDER_PATH : CSVファイルを保存するフォルダのパス
- Directory.CreateDirectory() : フォルダの新規作成処理
- StreamWriter() : ファイルの作成と上書き処理を実行するクラス
- AssetDatabase.Refresh() : 作成されたファイルがすぐにエディタに表示されるようにする。
この時点で、GSSの読み込みと、CSVファイルの作成の実装は完了しているが、利便性向上のため、実行中の処理に関するログを表示する描画処理を、以下で実装する。
5. 実行中の処理に関するログ出力の実装
以下のように、GssReaderTool.csに追記する :
GssReaderTool.cs (tap)
#if UNITY_EDITOR
using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using UnityEditor;
using Unity.EditorCoroutines.Editor;
namespace App.ReadCsv
{
public class GssReaderTool : EditorWindow
{
private const string CSV_FOLDER_PATH = "Assets/MasterData/CSV/";
private const string GSS_FORMAT = "tqx=out:csv";
private string gssSheetId;
private string gssSheetName;
private string csvfileName;
+ private string log;
private bool isLoadingGss;
[MenuItem("Tools/GSS Reader Tool")]
public static void OpenWindow()
{
GetWindow<GssReaderTool>("GSS読み込みツール");
}
private void OnGUI() // 描画処理
{
GUILayout.Label("[GSSの読み込み, CSVファイルの作成]");
gssSheetId = EditorGUILayout.TextField("・ GSSのシートID :", gssSheetId);
gssSheetName = EditorGUILayout.TextField("・ GSSのシート名 :", gssSheetName);
csvfileName = EditorGUILayout.TextField("・ 作成するファイル名 :", csvfileName);
if (GUILayout.Button("実行"))
{
+ log = string.Empty;
StartLoadGss(gssSheetId, gssSheetName);
}
+ DrawLogGUI();
}
/// <summary>
/// GSSの読み込み
/// </summary>
public void StartLoadGss(string id, string name)
{
if (isLoadingGss) // GSS読み込み中は実行しない
{
+ AddLog("Error : GSSを読み込み中...");
return;
}
else if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(name) || string.IsNullOrEmpty(csvfileName))
{
+ AddLog("Error : 設定項目をすべて記入してください。");
return;
}
var url = "https://docs.google.com/spreadsheets/d/" + id + "/gviz/tq?" + GSS_FORMAT + "&sheet=" + name;
EditorCoroutineUtility.StartCoroutine(LoadGss(url), this);
}
+ private void AddLog(string message)
+ {
+ log += $" - {message}\n";
+ }
private IEnumerator LoadGss(string url)
{
using(var request = UnityWebRequest.Get(url)) // GSSの読み込み
{
+ AddLog("GSSの読み込み開始");
isLoadingGss = true;
yield return request.SendWebRequest();
isLoadingGss = false;
if (request.result == UnityWebRequest.Result.ProtocolError)
{
+ AddLog($"Error Load GSS : {request.error} (GSSの存在性と公開設定を確認して下さい)");
yield break;
}
else if (request.result == UnityWebRequest.Result.ConnectionError)
{
+ AddLog($"Error Load GSS : {request.error} (ネットワークに接続されているかを確認して下さい)");
yield break;
}
+ AddLog("GSSの読み込み完了");
CreateCsv(csvfileName, request.downloadHandler.text);
}
}
/// <summary>
/// CSVファイルの作成
/// </summary>
private void CreateCsv(string fileName, string data)
{
if (string.IsNullOrEmpty(data))
{
+ AddLog("Error Create CSV : データが空であるため、ファイルを作成できませんでした。");
return;
}
+ AddLog("CSVファイルの作成開始");
if (!Directory.Exists(CSV_FOLDER_PATH)) // フォルダがなければ、
{
Directory.CreateDirectory(CSV_FOLDER_PATH); // 新規作成
}
var path = CSV_FOLDER_PATH + fileName + ".csv";
using (var sw = new StreamWriter(path, false)) // 新規作成 or 上書き
{
try
{
sw.Write(data);
AssetDatabase.Refresh(); // エディタ上のアセットの更新
+ AddLog($"CSVファイルの作成完了 : {path}");
}
catch (System.Exception e)
{
+ AddLog(e.ToString());
}
}
}
+ /// <summary>
+ /// ログを出力
+ /// </summary>
+ private void DrawLogGUI()
+ {
+ if (string.IsNullOrEmpty(log)) { return; }
+
+ GUILayout.FlexibleSpace();
+ GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(3));
+
+ GUILayout.Label(" [Log] ");
+ GUILayout.Label(log, new GUIStyle() { wordWrap = true, normal = new GUIStyleState() { textColor = new Color(1, 1, 1, 1) } });
+
+ if (GUILayout.Button("Clear Log"))
+ {
+ log = string.Empty;
+ }
+ GUILayout.Space(7);
+ }
}
}
#endif
ポイント:
- DrawLogGUI() : ログが空でなければ、ログ内容を描画
- GUILayout.FlexibleSpace() : ログに関するUIを一番下に表示
- GUILayout.Box() : 罫線を引く
最後に
どうでしたか、わかりやすかったでしょうか。GSSを用いてデータを管理することで、共同編集ができ、Gitでの競合の心配がなくなるので、積極的に使用したいところです。また、ネットワーク接続時は、アプリ起動時にGSSを取得してCSVファイルを上書きし、そうでないときは、すでにあるCSVファイルを参照するといった設計にすると、アプリ配信後にも対応が出来るので便利だと思われます。
また、以下のような多言語での文言の管理に適しています :
これをCSVで出力することで、コンマ区切りの文字列として取得できるので、StringReaderクラスなどを使うことで、行ごとにstring配列(例 : {W001, 文言, wordings})としてリストに格納しておくことにより、容易にデータを参照することができるようになります。以下に、その実装を記したコードを置いておきます :
CsvReader.cs (tap)
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using System.IO;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace App.ReadCsv
{
public class CsvReader : MonoBehaviour
{
[SerializeField]
private TextAsset csv;
public List<string[]> ReadDatas { get; private set; } = new List<string[]>();
/// <summary>
/// idを用いて、データを検索
/// </summary>
public string[] FindDataWithId(string id)
{
var lineData = ReadDatas.FirstOrDefault(e => e[0] == id);
if (lineData == default) { return lineData; }
var lineDataExceptId = new string[lineData.Length - 1];
for (var i = 0; i < lineData.Length - 1; i++) // idを取り除く
{
lineDataExceptId[i] = lineData[i + 1];
}
return lineDataExceptId;
}
#if UNITY_EDITOR
private void OnValidate()
{
if (!csv) { return; }
var path = AssetDatabase.GetAssetPath(csv);
if (!path.Contains(".csv"))
{
csv = null;
Debug.LogError("CSVファイルを指定してください。");
return;
}
ReadDatas = Read(csv.text);
}
/// <summary>
/// データの読み込み
/// </summary>
private static List<string[]> Read(string text)
{
var readDatas = new List<string[]>();
if (string.IsNullOrEmpty(text)) { return readDatas; }
var reader = new StringReader(text);
while (reader.Peek() != -1) // 読み込む文字がなくなるまで実行
{
var line = reader.ReadLine(); // 1行づつ読み込む
readDatas.Add(line.Split(",")); // 行を分割して保存
}
Debug.Log($"Read CSV :\n {string.Join("\n", readDatas.Select(e => string.Join(", ", e)))}");
return readDatas;
}
#endif
}
#if UNITY_EDITOR
[CustomEditor(typeof(CsvReader))]
public class CsvReaderEditor : Editor
{
private string[] data;
private string id;
private bool isFoldout;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
isFoldout = EditorGUILayout.Foldout(isFoldout, "データ確認用");
if (isFoldout)
{
id = EditorGUILayout.TextField("ID:", id);
if (GUILayout.Button("データの取得"))
{
var creator = (CsvReader)target;
data = creator.FindDataWithId(id);
}
if (data != default)
{
EditorGUILayout.TextField("取得したデータ:", string.Join(", ", data));
}
}
}
}
#endif
}
- Read() : CSVファイルを行ごとに読み込み、リスト(List<string[]> ReadDatas)に格納
- FindDataWithId(string id) : idから行データを取得
- CsvReaderEditorクラス : インスペクター上から、データ取得確認ができるようにしている。
- 上記スプレッドシートの場合、idをW001としてデータを取得すると、{文言, wordings}が返される。
また気が向いたら何か書きます。それでは。
宣伝 : CUPLEXという時間差アクションパズルゲームを作成しています。私は主にプログラムとプロジェクト管理を担当しています。よかったら、覗いてみてください↓
https://store.steampowered.com/app/2499100/CUPLEX/