Skip to content

Instantly share code, notes, and snippets.

@randalfien
Created October 2, 2023 15:17
Show Gist options
  • Save randalfien/54d73ebc41dcf68e31e331cdf30bae04 to your computer and use it in GitHub Desktop.
Save randalfien/54d73ebc41dcf68e31e331cdf30bae04 to your computer and use it in GitHub Desktop.
using UnityEngine;
using UnityEditor;
using Yarn.Unity;
using System.Collections.Generic;
using System.IO;
class YarnAssetPostProcessor : AssetPostprocessor
{
private const string ScriptFolder = "Assets/Yarn/";
private static readonly string _filePath = ScriptFolder + "YarnNodesRef.cs";
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths, bool didDomainReload)
{
foreach (string str in importedAssets)
{
if (str.ToLowerInvariant().EndsWith(".yarnproject"))
{
OnYarnProjectChanged();
}
}
}
private static YarnProject GetYarnProject()
{
var assetGUIDs = AssetDatabase.FindAssets("t: YarnProject");
if (assetGUIDs.Length <= 0)
{
Debug.LogError("Yarn Project not found");
return null;
}
var assetPath = AssetDatabase.GUIDToAssetPath(assetGUIDs[0]);
return AssetDatabase.LoadAssetAtPath<YarnProject>(assetPath);
}
private static void OnYarnProjectChanged()
{
var project = GetYarnProject();
if( NodesChanged( project.NodeNames ) )
{
GenerateNodeReferenceClass();
}
}
private static bool NodesChanged(string[] projectNodeNames)
{
var infile = YarnNodesRef.All;
if (infile.Length != projectNodeNames.Length) return true;
foreach (var projectNodeName in projectNodeNames)
{
var nodeFound = false;
foreach (var classNodeName in infile)
{
if (classNodeName == projectNodeName)
{
nodeFound = true;
break;
}
}
if (!nodeFound) return true;
}
return false;
}
public static void GenerateNodeReferenceClass()
{
var nodeNames = GetYarnProject().NodeNames;
var sceneList = new List<string>();
var nodesMap = new Dictionary<string, List<string>>();
foreach (var nodeName in nodeNames)
{
var underscoreIndex = nodeName.IndexOf('_');
if (underscoreIndex == -1)
{
Debug.LogWarning("node name has no prefix! "+nodeName);
continue;
}
var prefix = nodeName[..underscoreIndex];
if (prefix.Length > 4)
{
Debug.LogWarning("node name prefix too long! "+nodeName);
}
if (!sceneList.Contains(prefix))
{
sceneList.Add(prefix);
nodesMap.Add(prefix, new List<string>());
}
nodesMap[prefix].Add(nodeName);
}
using StreamWriter outfile = new StreamWriter(_filePath, false);
var listofall = new List<string>(); //keep track of all added constants
// Class contents generation
outfile.WriteLine("/// <summary>");
outfile.WriteLine("/// Auto-generated class");
outfile.WriteLine("/// </summary>");
outfile.WriteLine("");
outfile.WriteLine("public static class YarnNodesRef");
outfile.WriteLine("{");
foreach (var prefix in sceneList)
{
var nodes = nodesMap[prefix];
outfile.WriteLine("public static class "+prefix); //nested class for each prefix
outfile.WriteLine("{");
if (nodes != null)
{
for (var i = 0; i < nodes.Count; i++)
{
var node = nodes[i];
if (node == null)
{
Debug.LogWarning("node name is NULL");
continue;
}
var nameWithoutPrefix = node.Replace(prefix+"_", "");
outfile.WriteLine("\tpublic static string " + nameWithoutPrefix + " = \"" + node + "\";");
listofall.Add(prefix+"."+nameWithoutPrefix);
}
}
outfile.WriteLine("}");
}
outfile.WriteLine("public static string[] All = {");
foreach (var item in listofall)
{
outfile.WriteLine(item+",");
}
outfile.WriteLine("};");
outfile.WriteLine("}");
}
}
/// <summary>
/// Auto-generated class
/// </summary>
public static class YarnNodesRef
{
public static string[] All = {
};
}
@randalfien
Copy link
Author

This utility for YarnSpinner in Unity generates a static class for easy references to nodes. You can start dialogs by writing stuff like StartDialog(YarnNodesRef.Scene1.Introduction). It assumes you use prefixes with your node names like node name is Scene1_Introduction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment