Adventure Creator Wikia

Note: An official integration for Yarn Spinner can be found on AC's Downloads page.

Yarn Spinner is an easy-to-use tool that lets you quickly write game dialogue and choices.  This page shows how you can make use of Yarn files in your AC game.

  1. Import Yarn's Unity integration into your project, and set up your scene with Yarn's Dialogue Runner and Dialogue UI components.
  2. Create a new C# file named ACYarnVariables.cs, and copy in the code below.  Attach the new AC Yarn Variables component to your Dialogue Runner.  Assign this as the Dialogue Runner component Inspector's Variable Storage field.
  3. To connect a Yarn variable with AC, create either a Global or Local variable of type Bool, Int, Float, or String, and give it the same name as the Yarn variable, but without the '$' at the start.  For example, $sally_warning in Yarn becomes sally_warning in AC.
  4. Create a new C# file named ActionYarn.cs, and copy in the code below.  Place this in a new folder and install this folder as a custom Actions directory in AC's Actions Manager.  For more on installing custom Actions, see this tutorial.
  5. You can now use the new "Dialogue: Yarn" Action in your AC ActionLists to trigger Yarn nodes.  This can also optionally assign the Yan Program file that contains the node.
  6. To have AC characters automatically play a talking animation, create a new C# file named ACYarnSpeaker.cs, and copy in the code below. Attach to your Dialogue Runner, and assign as a Dialogue View. For each character, define an "IsTalking" bool parameter in their Animator. This should not be referenced in the character's Inspector - if you wish to use AC speech Actions as well, create a second such parameter with a new name, and configure that name in the AC Yarn Speaker component.


ACYarnVariables.cs:

using System.Collections.Generic;
using Yarn.Unity;

namespace AC
{ 

    public class ACYarnVariables : VariableStorageBehaviour
	{

		public override void SetValue (string variableName, string stringValue)
		{
			GVar variable = GetVariable (variableName);
			if (variable != null)
			{
				variable.TextValue = stringValue;
			}
		}


		public override void SetValue (string variableName, float floatValue)
		{
			GVar variable = GetVariable (variableName);
			if (variable != null)
			{
				variable.IntegerValue = (int) floatValue;
				variable.FloatValue = floatValue;
			}
		}


		public override void SetValue (string variableName, bool boolValue)
		{
			GVar variable = GetVariable (variableName);
			if (variable != null)
			{
				variable.BooleanValue = boolValue;
			}
		}
		
	 
	 	public override bool TryGetValue<T> (string variableName, out T result)
		{
			GVar variable = GetVariable (variableName);
			if (variable != null)
			{
				switch (variable.type)
				{
					case VariableType.Boolean:
						result = (T) (object) variable.BooleanValue;
						return true;

					case VariableType.Integer:
						result = (T) (object) variable.IntegerValue;
						return true;

					case VariableType.Float:
						result = (T) (object) variable.FloatValue;
						return true;

					case VariableType.String:
						result = (T) (object) variable.TextValue;
						return true;

					default:
						break;
				}
			}
			result = default (T);
			return false;
		}


		public override bool Contains (string variableName)
		{
			GVar variable = GetVariable (variableName);
			return variable != null;
		}


		public override void Clear () {}


		public override void SetAllVariables (Dictionary<string, float> floats, Dictionary<string, string> strings, Dictionary<string, bool> bools, bool clear = true)
		{
			foreach (var key in floats.Keys)
			{
				SetValue (key, floats[key]);
			}

			foreach (var key in strings.Keys)
			{
				SetValue (key, strings[key]);
			}

			foreach (var key in bools.Keys)
			{
				SetValue (key, bools[key]);
			}
		}


		public override (Dictionary<string, float> FloatVariables, Dictionary<string, string> StringVariables,  Dictionary<string, bool> BoolVariables) GetAllVariables ()
		{
			var floats = new Dictionary<string, float> ();
			var strings = new Dictionary<string, string> ();
			var bools = new Dictionary<string, bool> ();

			return (floats, strings, bools);
		}


		private GVar GetVariable (string variableName)
		{
			string varName = variableName;
			if (varName.StartsWith ("$"))
			{
				varName = varName.Substring (1);
			}

			if (!varName.StartsWith ("l_"))
			{
				GVar variable = GlobalVariables.GetVariable (varName);
				if (variable != null)
				{
					return variable;
				}
			}

			if (!varName.StartsWith ("g_"))
			{
				GVar variable = LocalVariables.GetVariable (varName);
				if (variable != null)
				{
					return variable;
				}
			}

			ACDebug.LogWarning ("Cannot find AC Variable of the name " + varName);
			return null;
		}

	}
	
}

ActionYarn.cs:

using UnityEngine;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif

using Yarn.Unity;

namespace AC
{

    [System.Serializable]
	public class ActionYarn : Action
	{

		public override ActionCategory Category { get { return ActionCategory.Dialogue; } }
		public override string Title { get { return "Yarn"; } }
		public override string Description { get { return "Runs a Yarn node."; } }

		[SerializeField] private string node;
		[SerializeField] private YarnProject yarnProject = null;
		private DialogueRunner dialogueRunner;

		public override float Run ()
		{
			if (dialogueRunner == null)
			{
				dialogueRunner = FindObjectOfType <DialogueRunner>();
			}

			if (dialogueRunner == null)
			{
				LogWarning ("Cannot find a DialogueRunner component in the scene.");
				return 0f;
			}

			if (!isRunning)
			{
				if (yarnProject && !dialogueRunner.NodeExists (node))
				{
					dialogueRunner.SetProject (yarnProject);
				}

				dialogueRunner.StartDialogue (node);
				isRunning = true;
				return defaultPauseTime;
			}
			
			if (dialogueRunner.IsDialogueRunning)
			{
				return defaultPauseTime;;
			}

			isRunning = false;
			return 0f;
		}

		#if UNITY_EDITOR

		public override void ShowGUI ()
		{
			node = EditorGUILayout.TextField ("Node:", node);
			yarnProject = (YarnProject) EditorGUILayout.ObjectField ("Yarn project (optional):", yarnProject, typeof (YarnProject), false);
		}

		#endif

	}

}

ACYarnSpeaker:

using UnityEngine;
using Yarn.Unity;
using AC;

public class ACYarnSpeaker : DialogueViewBase
{

    private AC.Char speaker;
	public string isTalkingParameter = "IsTalking";


	public override void RunLine (LocalizedLine dialogueLine, System.Action onDialogueLineFinished)
	{
		string characterName = dialogueLine.CharacterName;
		speaker = !string.IsNullOrEmpty (characterName) ? FindCharacter (characterName) : null;
		onDialogueLineFinished();
	}


	public override void DismissLine (System.Action onDismissalComplete)
	{
		if (speaker) speaker.GetAnimator ().SetBool (isTalkingParameter, false);
		onDismissalComplete.Invoke ();
	}


	public override void InterruptLine(LocalizedLine dialogueLine, System.Action onDialogueLineFinished)
	{
		if (speaker) speaker.GetAnimator ().SetBool (isTalkingParameter, false);
		onDialogueLineFinished.Invoke ();
	}


	AC.Char FindCharacter (string searchName)
	{
		foreach (var character in KickStarter.stateHandler.Characters)
		{
			if (character.speechLabel == searchName || character.gameObject.name == searchName)
			{
				return character;
			}
		}

		Debug.LogWarning("Couldn't find a YarnCharacter named {0}!", searchName );
		return null;
	}

}