Skip to content

Instantly share code, notes, and snippets.

@Cdddo
Last active September 6, 2022 22:24
Show Gist options
  • Save Cdddo/724561e1d4e3618cfe10d92cbf60c97e to your computer and use it in GitHub Desktop.
Save Cdddo/724561e1d4e3618cfe10d92cbf60c97e to your computer and use it in GitHub Desktop.
Yarn Spinner Notes

Trying to call Stop() alone just makes the current line replay, which is very much not what I want What I want is just to be able to dismiss the current line and prevent any new ones from being delivered, same as if I had put <> in the yarn script

https://discord.com/channels/754171172693868585/754171643990900776/983937895528267827 I think I know what’s happening, we stop the dialogue but we don’t tell the views that this has happened. So when the view finishes up it tells the runner to continue which then starts it all up again

Q: Dialogue Runner still runs after calling DialogueRunner.Stop(). Dialogue line view doesn't close either. Am I missing something here?

A: If you call Stop() on a dialogue runner while a line is running, and then your line view reports that the line is complete, the dialogue runner will call its Continue() method, which resumes running. If you call Stop(), don’t call the onDialogueLineFinished callback from your dialogue view (or any other callback)


YarnCommands use GameObject.Find()

If you want to avoid automatic use of GameObject.Find, then you can either:

  • Use DialogueRunner.AddCommandHandler() to register a method as a command, or

  • Use [YarnCommand] on a static method

  • In both of these situations, your method could take a string as its first parameter, and you could use that to look up a cached dictionary of GameObject references


The basic flow of how dialogue is displayed is:

  1. Dialogue Runner passed info to the Dialogue Views
  2. Dialogue Views "display" the info, then signal to the Dialogue Runner that they are ready for the next line
  3. The Dialogue Runner waits until all views are completed to then proceed to loop again. [6:20 PM] an Interrupt can only happen between steps 2 and 3, where the Dialogue Runner is still waiting for the Views to tell it that they're all finished displaying. [6:21 PM] what an interrupt does is basically tell all the views that they are interrupted (the InterruptLine method), and waits until they signal that they are all done handling the interrupt [6:24 PM] it sounds like you don't want an interrupt on this level, and instead only on one view. In that case you can have your continue prompt call one method, in that method you can check if you are still running the text displaying animation. If you are display all the text at once and wait for another continue call, and if you aren't continue as normal. (edited) [6:26 PM] This is what LineView does with the UserRequestedViewAdvancement method, checks whether the typewriter effect is playing and decides how to handle it.

You can read the docs to have a more detailed understanding on the differences between how to use UserRequestedViewAdvancement and InterruptLine. https://docs.yarnspinner.dev/api/csharp/yarn.unity/yarn.unity.dialogueviewbase/yarn.unity.dialogueviewbase.userrequestedviewadvancement https://docs.yarnspinner.dev/api/csharp/yarn.unity/yarn.unity.dialogueviewbase/yarn.unity.dialogueviewbase.interruptline https://docs.yarnspinner.dev/api/csharp/yarn.unity/yarn.unity.dialogueviewbase/yarn.unity.dialogueviewbase.requestinterrupt


i find a command for character face changes is more flexible than markup because you can use a command without a dialogue line, i.e. if you just want to use it with <> to send a character on a little face journey [6:52 AM] or if you want to change a different character's expression based on what another speaker is saying

If you just want one portrait for each character, you can look at the provided CharacterColorView.cs script and adapt it to change a sprite instead of the text colour. If each character has multiple portraits (for say moods), its easiest to embed it in the line metadata. Building on top of the previous example, you'd have different sprites for each character and mood. If you want the character's portrait to change while the text is displayed, you'd need to use markup and parse for that in the line. You'd need to have the dialogue view that's displaying the line talk to the one that's displaying the portrait and update it accordingly. If you want to combine all of that, plus a way to control an overworld sprite/avatar you'd also need to use commands. But at this point you'd also be making a separate view or manager entirely.


You can easily hook some UI control or in-game events up to the existing functionality for dialogue interrupts. To interrupt in the middle of a line relies on you telling whatever components you have that are reading/typing out the dialogue to stop. For the dialogue sequence to be selected based on some logic can be done either with in-Yarn logic or by just making a DialogueRunner that does some logic (e.g., based on node names and/or tags) and then runs the selected node.

Currently in-development is a more robust set of functionality for fact-based node selection (basically: storylets) and it looks viable so I imagine that will happen at some point soon. But it’s not anything you can’t write yourself with a custom DialogueRunner.


if you're going to save/load from a file with JSON to use a plugin that can handle dictionaries anyway like the Newtonsoft package or TinyJSON.


... what's the right way to skip the dialog typping animation to just display the full finished dialog box text on a button press, and then go to next line on the second button press (same input)? This seem very basic (most RPG, Ace Attorney, etc) yet I can't find a way to do it. dogboydog — 06/15/2022 dialogRunner.UserRequestedViewAdvancement(); I believe but that is the new name and I can't remember when they changed the name KXI System — 06/15/2022 Assuming you're making a custom dialogue view it is UserRequstedViewAdvancement, the docs page has a full breakdown on how to use it: https://docs.yarnspinner.dev/api/csharp/yarn.unity/yarn.unity.dialogueviewbase/yarn.unity.dialogueviewbase.userrequestedviewadvancement tl;dr - When the user requests to advance, decide what to display in this method

Aenimus: You can change the default LineView slightly by adding an else at the end of the advancement code

public override void UserRequestedViewAdvancement()
{
    if (currentLine == null)
    {
        return;
    }

    if (currentStopToken.CanInterrupt)
    {
        currentStopToken.Interrupt();
    }
    else // add this
    {
        requestInterrupt?.Invoke();
    }
}

What should I change in my dialogue system prefab to have it so the player can progress dialogue with a key input (i.e. Enter/Z)

-> You can add the DialogueAdvanceInput.cs script and set that up (located in Runtime/Views), I think the Intro sample and the Space sample has that script set up so you can take a look at that.

All the DialogueAdvanceInput.cs does is call UserRequestedViewAdvancement on a target Dialogue View (like Line View) on an input press, so if you want to script your own or add on to a different system you can.

So I'm using the new input system without keycodes, how could I have the dialogueadvanceinput.cs script recognize an "E" keypress?

-> You can bind "submit" to a secondary key, e.g., "z"


When you declare a variable, you’re doing two things:

  • First, you’re telling the compiler what the type of the variable is, so that the compiler is able to stop you from doing operations that don’t make sense.
  • Second, you’re setting the "default" value for the variable - if the variable hasn’t been set to anything, and you fetch its result, it’ll get the value used in the declaration. (We store these initial values as a read-only table in the compiled YarnProgram.)

The reason why you’re not seeing anything in your variable storage is because the variable storage is only updated when a variable is changed. Until you <> a variable, it won’t show up.


DialogueRunner.Stop exits the current dialogue as if it has reached the end.

calling DialogueRunner.Stop also doesn’t clean up your Dialogue Views, so if you call it you would need to handle that as well


I'm switching from [YarnCommand("")] to the CommandHandler method so I can do custom lookup instead of searching the entire scene for the gameobject.

It works with void functions, but how do I use commandhandler with IEnumerator functions? I get a "has the wrong return type" error

-> You’ll need to make Walk return a Coroutine, not an IEnumerator - make it start a coroutine using StartCoroutine and return the Coroutine you get back from that


To share variables between Unity and your Yarn scripts you can:

  1. Rely on Yarn-declared variables. This means using them in your script is the usual Yarn way, but accessing it from the Unity side would be done with a variable storage (you don’t need a custom one to do this, just treat it like a Dictionary-like class that is wrapping Yarn’s variable memory).
  2. Rely on C#-declared variables. This means using them like normal in your Unity stuff but when you want the value in your Yarn script you’d call a function you have declared to get or set the value.
  3. Use duplicate variables (don’t do this one, it’s a recipe for trouble).

So say in your script you have a variable called $coins, then you can get or set that in C# with:

void Start()
{
    variableStorage = GameObject.FindObjectOfType<InMemoryVariableStorage>();
}

int getCoins()
{
    int coins;
    bool success = variableStorage.TryGetValue("$coins", out coins);
    return success ? (int)coins : 0;
}

void addCoins(int coinsToAdd)
{
    int coins = getCoins();
    variableStorage.SetValue("$coins", coins + coinsToAdd);
}

This does use some temp C# variables but doesn’t keep them hanging around or make it so you’re responsible for keeping them in synch.


So I want yarnspinner to be able to access a database of facts so npcs “remember” or notice what the player does like move outside their talking range or talk to them multiple times or they have an item/have talked to someone else before And interruptible as in certain events will cause them to stop their line of dialogue and make it switch to a new one like if you move away from them

-> At the very basic level you basically want to access that information in your yarn story. If you already have a "database" or whatever system you have to store these "facts", you can use a Yarn Function to get that information from your C#/game into yarnspinner. For example, you can make a function called <<checkFact("Fact Name")>> where it will return a value of "Fact Name". Though if you're game is more story heavy it might be better to modify yarn variables directly, methods in the Variable Storage script would help you there (the default is the InMemoryVariableStorage.cs script). Its also worth looking into creating your own variable storage, similar to making a custom dialogue view, if the default one is lacking some features. Personally I would go with the yarn function method first, its simpler on the yarn spinner side to implement and gives you the most control over how your story access these "facts". tl;dr - Create yarn functions to pass a value based on the "facts", or store your "facts" as yarn variables and modify them in your game.


if you're doing it in a set variable call, you need a function. outside of a set call, a command should work fine. e.g. <<set $myVar to MyFunction()>>

and

<> SomeCharacter: {$myVar}


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