0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Unityでスプレッドシートを読み込む

Posted at

使用ライブラリ

UniTask

事前準備

スプレッドシートの共有設定をリンクを知っている全員にする。

image.png

↓みたいな感じに変数名とかを合わせる。

image.png

    [Serializable]
    public class AttackData 
    {
        public string attackKey;
        public int power;
        public Attribute attribute;
    }

JsonUtilityではなくJsonConvertを使ってるので、SerializeFieldを使えない。
泣く泣くpublic変数にします。
大文字小文字も多分合わせないといけないはず。

コード

using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;

namespace Takenokohal.Utility
{
    public static class CsvConverter
    {
        public static string CsvToJson(string csv)
        {
            var v = JsonConvert.SerializeObject(CsvToDictionary(csv));
            return v;
        }


        private static IReadOnlyList<IReadOnlyDictionary<string, object>> CsvToDictionary(string csv)
        {
            const string splitKey = @"""";
            var rows = csv.Split(new[] { splitKey + "\n" }, StringSplitOptions.None);
            var dataList = rows
                .Select(SplitRowToData).ToList();

            var cleanedData = CleanUp(dataList).ToList();

            var header = cleanedData[0];

            header.RemoveAll(string.IsNullOrEmpty);

            var dicList = new List<Dictionary<string, object>>();
            foreach (var currentRow in cleanedData.Skip(1))
            {
                var dic = new Dictionary<string, object>();
                for (int i = 0; i < header.Count; i++)
                {
                    var dataString = currentRow[i];
                    var key = header[i];
                    if (int.TryParse(dataString, out var result))
                    {
                        dic.Add(key, result);
                        continue;
                    }

                    dic.Add(header[i], dataString);
                }

                dicList.Add(dic);
            }

            return dicList;
        }
        
        private static string[] SplitRowToData(string row)
        {
            return row.Split(new[] { ',' }, StringSplitOptions.None);
        }

        private static List<List<string>> CleanUp(IReadOnlyList<IReadOnlyList<string>> origin)
        {
            var clone = new List<List<string>>();
            foreach (var data in origin.Where(value => !string.IsNullOrEmpty(value[0])))
            {
                var list = data.Select(s => s.Trim(new[]
                {
                    '"'
                })).ToList();

                clone.Add(list);
            }

            return clone;
        }
    }
}
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using Newtonsoft.Json;
using UnityEngine.Networking;

namespace Takenokohal.Utility
{
    public static class SpreadSheetLoader
    {
        public static async UniTask<IReadOnlyList<T>> LoadData<T>(string url)
        {
            var req = UnityWebRequest.Get(url);
            var result = await req.SendWebRequest();
            var csv = result.downloadHandler.text;
            var json = CsvConverter.CsvToJson(csv);

            var target = new List<T>();
            JsonConvert.PopulateObject(json, target, new JsonSerializerSettings()
            {
                ObjectCreationHandling = ObjectCreationHandling.Replace
            });
            return target;
        }
    }
}

解説

SpreadSheetLoader

csv読み込み

urlを引数に、好きなデータのデータのリストを持ってきます。

var req = UnityWebRequest.Get(url);
var result = await req.SendWebRequest();
var csv = result.downloadHandler.text;

この部分でスプレッドシートをstringとして読み込んでる。

URLの書き方は、

スプレッドシートの編集画面のURLが

https://docs.google.com/spreadsheets/d/hogehoge/edit?gid=0#gid=0

となっているはずなので、このhogehoge部分をコピペして、

"https://docs.google.com/spreadsheets/d/hogehoge/gviz/tq?tqx=out:csv&sheet=";

みたいに書く。

sheet=

の後にシート名を書く。

Jsonにコンバート

CsvConverter(後述)を使ってJsonにしてもらう。

            var json = CsvConverter.CsvToJson(csv);

好きなデータに変換

JsonConvert.PopulateObjectで変換。
UnityのJsonUtilityは使いにくかったのでNewtonsoft.JsonのJsonConvertを使いました。

            var target = new List<T>();
            JsonConvert.PopulateObject(json, target, new JsonSerializerSettings()
            {
                ObjectCreationHandling = ObjectCreationHandling.Replace
            });
            return target;

CsvConverter

考え方

Jsonだとクラスの中身とDictionaryの中身が同じ形になることを利用して、まずDictionaryに変換してからそれをJsonにすることで好きなデータとして拾ってます。

csvをListのListに

            const string splitKey = @"""";
            var rows = csv.Split(new[] { splitKey + "\n" }, StringSplitOptions.None);
            var dataList = rows
                .Select(SplitRowToData).ToList();
        private static string[] SplitRowToData(string row)
        {
            return row.Split(new[] { ',' }, StringSplitOptions.None);
        }

\nで切ってしまうと、スプレッドシート内で改行ができなくなってしまうので、""\nで区切ってます。
ダウンロードしてきたcsvデータに毎回ゴミ列が含まれていたのでそれを利用しました。

データを整理

            var cleanedData = CleanUp(dataList).ToList();
        private static List<List<string>> CleanUp(IReadOnlyList<IReadOnlyList<string>> origin)
        {
            var clone = new List<List<string>>();
            foreach (var data in origin.Where(value => !string.IsNullOrEmpty(value[0])))
            {
                var list = data.Select(s => s.Trim(new[]
                {
                    '"'
                })).ToList();

                clone.Add(list);
            }

            return clone;
        }

1列目が空になっている行を削除。
1列目以外はわざと空欄にしてるかもしれないので消さない。
csvの生のデータを読んだ時に「"」がすごい邪魔そうな気がしたのでTrimしたが、要らないかも。

変数名を取得

            var header = cleanedData[0];

            header.RemoveAll(string.IsNullOrEmpty);

image.png

1行目は絶対こんな感じのヘッダーになっていると思うのでそれを取得。
空欄を削除。

Dictionaryに変換

            var dicList = new List<Dictionary<string, object>>();
            foreach (var currentRow in cleanedData.Skip(1))
            {
                var dic = new Dictionary<string, object>();
                for (int i = 0; i < header.Count; i++)
                {
                    var dataString = currentRow[i];
                    var key = header[i];
                    if (int.TryParse(dataString, out var result))
                    {
                        dic.Add(key, result);
                        continue;
                    }

                    dic.Add(header[i], dataString);
                }

                dicList.Add(dic);
            }

            return dicList;

データの1行目はさっきのヘッダーになっているのでスキップ。
要素を取っていって、intにパースできそうならパースする。改造すればfloatにもできるかも。

実際に使っているコード

using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using Sirenix.OdinInspector;
using SpellProject.Utility;
using Takenokohal.Utility;
using UnityEditor;
using UnityEngine;

namespace SpellProject.Data.Database
{
    public abstract class DatabaseBase<T> : SerializedScriptableObject
        where T : IKeyHoldingData
    {
        protected abstract DatabaseURL.SheetName GetSheetName();

        [SerializeField] private List<T> data;

        public IReadOnlyList<T> DataList => data;

        public T FindByKey(string key) => data.First(value => value.Key == key);
        public T FindByIndex(int index) => data[index];
        public int GetIndex(string key) => data.FindIndex(value => value.Key == key);

#if UNITY_EDITOR
        public async UniTask UpdateAsync()
        {
            data.Clear();
            var v = await SpreadSheetLoader.LoadData<T>(DatabaseURL.GetDataURL(GetSheetName()));

            data = v.ToList();
            EditorUtility.SetDirty(this);
            AssetDatabase.SaveAssets();
        }
#endif
    }
}
using SpellProject.Utility;
using UnityEngine;

namespace SpellProject.Data.Database
{
    [CreateAssetMenu(menuName = "Create AttackDatabase", fileName = "AttackDatabase", order = 0)]
    public class AttackDatabase : DatabaseBase<AttackData>
    {
        protected override DatabaseURL.SheetName GetSheetName()
        {
            return DatabaseURL.SheetName.Attack;
        }
    }
}

実際に自作ゲームで使ってるコード。
UpdateAsynceを呼んだらデータベースが更新されるようにしてる。

結果

image.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?