Last active
January 27, 2024 11:06
-
-
Save thebeardphantom/405ff72ef4621a2a9405976b66c16936 to your computer and use it in GitHub Desktop.
An example of generating ScriptableObjects and populating their serialized properties via CSV files.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Linq; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
using UnityEditor; | |
using UnityEngine; | |
public static partial class CsvDataGenerator | |
{ | |
#region Fields | |
private const string OUTPUT_FOLDER_GUID = "9009c602285b7e34ab2a99ab7de80022"; | |
#endregion | |
#region Methods | |
[MenuItem("Utils/CSV Generation/Online")] | |
private static void GenerateOnline() | |
{ | |
Generate(true); | |
} | |
[MenuItem("Utils/CSV Generation/Offline")] | |
private static void GenerateOffline() | |
{ | |
Generate(false); | |
} | |
private static void Generate(bool online) | |
{ | |
var csvFolder = AssetDatabase.GUIDToAssetPath("6d57745f5a886444caf5743b9c90fa47"); | |
if (online) | |
{ | |
AssetDatabase.ImportAsset( | |
csvFolder, | |
ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate | ImportAssetOptions.ImportRecursive); | |
} | |
var csvs = AssetDatabase.FindAssets( | |
"t:TextAsset", | |
new[] | |
{ | |
csvFolder | |
}) | |
.Select(AssetDatabase.GUIDToAssetPath) | |
.SelectMany(AssetDatabase.LoadAllAssetRepresentationsAtPath) | |
.Cast<TextAsset>() | |
.ToDictionary(t => t, t => new CsvFile(t)); | |
var outputFolderPath = AssetDatabase.GUIDToAssetPath(OUTPUT_FOLDER_GUID); | |
try | |
{ | |
AssetDatabase.StartAssetEditing(); | |
foreach (var csv in csvs.Values) | |
{ | |
for (var i = 0; i < csv.EntryCount; i++) | |
{ | |
var subfolderName = csv["Subfolder"][i]; | |
var assetName = csv["AssetName"][i]; | |
var folder = $"{outputFolderPath}/{csv.Filename}/{subfolderName}"; | |
Directory.CreateDirectory(folder); | |
var assetPath = $"{folder}/{assetName}.asset"; | |
var asset = AssetDatabase.LoadAssetAtPath<ScriptableObject>(assetPath); | |
if (asset == null) | |
{ | |
asset = ScriptableObject.CreateInstance(csv.Filename); | |
AssetDatabase.CreateAsset(asset, assetPath); | |
} | |
asset.name = assetName; | |
var so = new SerializedObject(asset); | |
so.Update(); | |
foreach (var column in csv.Columns) | |
{ | |
var property = FindProperty(so, column.ColumnName); | |
if (property == default) | |
{ | |
continue; | |
} | |
switch (property.propertyType) | |
{ | |
case SerializedPropertyType.Enum: | |
{ | |
var enumValueIndex = string.IsNullOrEmpty(column[i]) | |
? 0 | |
: Array.IndexOf(property.enumNames, column[i]); | |
property.enumValueIndex = enumValueIndex; | |
break; | |
} | |
case SerializedPropertyType.Float: | |
{ | |
property.floatValue = float.Parse(column[i]); | |
break; | |
} | |
case SerializedPropertyType.Integer: | |
{ | |
property.intValue = int.Parse(column[i]); | |
break; | |
} | |
case SerializedPropertyType.String: | |
{ | |
property.stringValue = column[i]; | |
break; | |
} | |
} | |
} | |
so.ApplyModifiedProperties(); | |
EditorUtility.SetDirty(asset); | |
} | |
} | |
} | |
finally | |
{ | |
AssetDatabase.StopAssetEditing(); | |
AssetDatabase.SaveAssets(); | |
} | |
} | |
private static SerializedProperty FindProperty(SerializedObject so, string name) | |
{ | |
var sb = new StringBuilder(); | |
var parts = name.Split('.'); | |
foreach (var part in parts) | |
{ | |
var arrayMatch = Regex.Match(part, @"(.*?)\[(\d+)\]$"); | |
if (arrayMatch.Success) | |
{ | |
var arrayName = arrayMatch.Groups[1].Value; | |
var arrayIndex = arrayMatch.Groups[2].Value; | |
sb.Append($"<{arrayName}>k__BackingField.Array.data[{arrayIndex}]."); | |
} | |
else | |
{ | |
sb.Append($"<{part}>k__BackingField."); | |
} | |
} | |
// Remove last dot | |
sb.Remove(sb.Length - 1, 1); | |
var rebuiltPath = sb.ToString(); | |
var property = so.FindProperty(rebuiltPath); | |
return property; | |
} | |
#endregion | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using UnityEngine; | |
public static partial class CsvDataGenerator | |
{ | |
#region Types | |
public class CsvFile | |
{ | |
#region Types | |
public class Column | |
{ | |
#region Fields | |
public readonly string ColumnName; | |
public readonly string[] Values; | |
#endregion | |
#region Constructors | |
public Column(string columnName, int valueCount) | |
{ | |
ColumnName = columnName; | |
Values = new string[valueCount]; | |
} | |
#endregion | |
#region Methods | |
public string this[int index] => Values[index]; | |
#endregion | |
} | |
#endregion | |
#region Fields | |
public readonly string Filename; | |
public readonly int EntryCount; | |
public readonly Column[] Columns; | |
#endregion | |
#region Constructors | |
public CsvFile(TextAsset textAsset) | |
{ | |
Filename = textAsset.name; | |
var fileContents = textAsset.text; | |
var lines = fileContents.Split(Environment.NewLine); | |
EntryCount = lines.Length - 1; | |
var columnNames = lines[0].Split(','); | |
Columns = new Column[columnNames.Length]; | |
for (var i = 0; i < Columns.Length; i++) | |
{ | |
Columns[i] = new Column(columnNames[i], EntryCount); | |
} | |
for (var i = 0; i < EntryCount; i++) | |
{ | |
var line = lines[i + 1]; | |
var values = line.Split(','); | |
for (var j = 0; j < values.Length; j++) | |
{ | |
Columns[j].Values[i] = values[j]; | |
} | |
} | |
} | |
#endregion | |
#region Methods | |
public Column this[string columnName] | |
{ | |
get | |
{ | |
return Columns.SingleOrDefault(c => c.ColumnName == columnName); | |
} | |
} | |
#endregion | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment