Yarn Command Attributes - Part 2

Posted On: 2020-02-17

By Mark

Last week, I wrote about the benefits of Yarn Command Attributes. This week, I will describe some of the pitfalls of using attributes, as well as an alternative. If you haven't read last week's post, I recommend doing so, as this one builds upon that foundation.

Coupled Names

Running a Yarn command from a script requires two key pieces of information: the name of the command and the name of the object that the command is attached to. Consider the following example:
<<Explode Barrel>>
This tells the engine (Yarn Spinner) to look for an object named "Barrel" and run a command called "Explode" on it.

So long as there is only one barrel in the scene, everything works fine. If, however, new barrels are added to the scene, perhaps as background decorations, then the script may no longer work as intended. The engine assumes that there is only one object named "Barrel", so if the level designer isn't careful (or is unaware of the script) a new object named "Barrel" may end up being used by this script instead of the intended explosive barrel.

Similar issues can also occur if objects in the game scene are renamed, perhaps to make them more clear. If the explosive barrel has its name changed from "Barrel" to the more descriptive "Explosive Barrel", then the command won't run, since there is no "Barrel" object. Further still, if one detects and attempts to correct that issue, they would have to change the name again, this time to something without a space (such as "Explosive_Barrel"), since Yarn Spinner requires that names of objects used with commands must not contain spaces.

Thus, the use of Yarn Command Attributes tightly couples the Yarn script to the names of objects in Unity's hierarchy. While most forms of coupling are inconvenient, this particular form is especially troublesome: there are no tools to automate detecting issues, so it is up to the game developers/writers to identify and resolve any mistakes in naming.

One final issue with the Yarn Command Attributes tightly coupling to Unity's names is that it is fairly common to rely on Unity names in systems design. As a result, new systems, including third-party ones, may introduce new constraints or force name changes. As such, tools or assets that might otherwise be quite useful may have additional rework costs that need to be evaluated, depending on whether they impact names or naming conventions.

Alternatives

If the prospect of tightly coupling names is unacceptable, one might be tempted to stick to tightly coupling the code (such as using Yarn's AddCommandHandler) instead. This is simple and straight-forward, of course, but large projects are especially prone to change and rework, so the pitfalls of tightly-coupled code will likely be amplified.

There is, however, another approach available: One could choose to write custom code to resolve commands in a decoupled way that does not rely on Unity's names. Yarn Spinner is designed to support developers adding or modifying the way commands are resolved (such as using the onCommand event on the Dialogue UI class.) With a bit of extra work one can build a system that is loosely coupled, both in the code and also with Unity's naming system.

Personally, I have chosen to invest the time to implement a custom command resolver. Speaking broadly, this has allowed me improve the organization of my commands while also keeping them loosely coupled - both with regard to the code and the objects' names. Doing so has also provided a number of additional benefits as well - though the details of that are unique to my particular approach.

Conclusion

Hopefully now you've seen that, while command attributes are easy to use and useful, they have their own share of problems. If those problems are too much, there are alternatives, both topics covered before (tightly coupling) and also approaches that involve writing one's own custom code. If you're interested in the approach I used for my own work, be sure to tune in next week: I'll be covering a high-level overview of how I used custom code to avoid naming pitfalls.