Posted On: 2020-02-10
One feature that attracted me to Yarn Spinner is that custom Yarn Commands are architected to be loosely coupled by default. Loosely coupled code is beneficial for a variety of technical reasons - but in Yarn Spinner's case, it is actually used as a way to make writing custom commands more developer-friendly. As such I thought I should write a bit about how Yarn Spinner achieves that, in the hopes it will encourage more developers to do the same.
Before diving into the meat of this topic, I thought I should provide a few quick background definitions - in case you aren't familiar with some of this:
Yarn Spinner
Yarn Spinner is an open-source branching-narrative engine. If you're familiar with Twine, it's similiar, but designed for use with the Unity game engine.
Yarn
Yarn is the programming language used by Yarn Spinner. It's designed for writers, so it's intentionally easy to learn.
Yarn Command
A Yarn Command is an instruction sent from the dialogue engine (Yarn Spinner) to the game engine (Unity). There are a few built-in commands
(such as "wait"), but the Yarn language is open to allow for custom commands - and Yarn Spinner
makes it relatively easy for developers to author those custom commands.
Loosely Coupled Code
Software is considered "coupled" when one part of the code depends upon code that is somewhere else (such as a different class or library.)
This coupling may be "tighter" (when any change to one requires changes to the other) or "looser" (when either one can change independently
of one another) across a gradient - so one piece of code may be said to be "more tightly coupled" than another.
Yarn Spinner is set up to have two different ways of registering custom commands. One way, which is very tightly coupled, is to explicitly call the AddCommandHandler
method on the DialogueRunner
, specifying the name of the command and the code to execute when that command is called. This is a straightforward, reasonable way to accomplish this, but
there is another, equally simple, and arguably better way to do so: using attributes.
Using attributes to register a command is as simple as adding the [YarnCommand]
attribute before a method, and specifying the name (the one required paramater.) Adding the attribute to any method will cause that method
to be called whenever that command is run by Yarn Spinner. This is quite loosely coupled, as neither the command nor the DialogueRunner
need to know about each other*.
Using an attribute to register the command is not just beneficial due to loosely coupling the code - it's also elegantly self-documenting. The attribute clearly states
the command name that is used to invoke it, and one doesn't have to cross-reference the Start
method to see whether or not it is actually registered. Finally, while I don't personally know which one is easier for writers to learn to use, I think the
documentation
listing the attribute approach first implies that it is at least no harder to learn.
I am quite fond of the use of attributes for registering custom commands. When I was first learning to use Yarn Spinner, I found it easy to learn and intuitive to reason about. For developers considering using Yarn Spinner, or writers looking to learn to use commands in their scripts, I highly recommend using the attribute approach, as it will be both better for your architecture and easier to learn.
Yet, despite how great attribute-based commands are, I actually don't use them at all in my current project. Tune in next week, when I will explain why I stopped using them, as well as what I use instead.