Posted On: 2020-03-02
Last week's post ended with something of a cliffhanger: although I described an alternative to Yarn Command Attributes, I didn't describe any of the of the advantages or pitfalls that are unique to that alternative. This final post in the series aims to remedy that: I will describe several benefits and pitfalls, as well as how they affect my project, specifically. It is my hope that, in so doing, this post can illuminate some of the trade-offs one would need to consider when attempting to build their own alternative to Command Attributes.
Please note that, even more than any other post in this series, this one requires that you are familiar with Part 3. If you haven't read it recently, I recommend doing so, as this picks up right where that one left off.
The primary benefit of using a custom command handler is being able to avoid the pitfalls of Command Attributes. Rather than relying on Unity object names, which are trivially simple to change (for better or worse), a custom command handler can use other means for locating objects (such as by Id or by a name stored in code). When implemented correctly, these other means should be more change resistant and extensible - and they may even afford opportunities for additional safety mechanisms* to reduce the risk of the script desychronizing from the object identifiers.
The particular custom command handler I use also provides the benefits of decoupling commands, using interfaces and types to keep everything organized. Adding new commands requires little effort: implementing interfaces is a common programming task, and many Integrated Development Environments (IDEs) have built-in support for speeding the process up (such as automatically generating placeholder implementations.) Additionally, by having the interface clearly define the essential parts of a command, one is in no danger of forgetting to implement any part of it (a risk one might have with some other loosely-coupled approaches.)
Beyond the primary benefits, the particular approach I use has a few extra benefits. Firstly, by keeping all the commands together, I make it easier to verify the existence of any particular command. Specifically, I keep the dialogue runner, UI, and commands together in a single prefab, which ensures that all dialogue has the same set of commands. Furthermore, I have a second, separate prefab with just the runner and commands for the menu system - keeping those separate helps make sure that certain particularly dangerous commands (such as "Quit to Desktop") are only available in menus and not during random npc conversations.
The second benefit of this approach is the ability to customize many of the details associated with running commands. Since all commands run through this one custom command handler, I can implement new features that might otherwise be quite difficult to achieve (given the variety of ways that commands can be handled by default.) So far, this has primarily been useful for integrating the Salience Engine, which leverages return values to tell a (modified) Yarn Spinner to generate options based on the salience scores of eligible nodes. As the needs of the dialogue continue to grow, I expect the ability to customize it will be increasingly valuable.
As with any solution, my approach is not without its flaws. Firstly, it expects more programming literacy than the attributes approach. In particular, since commands aren't directly attached to the relevant actors, adding new commands requires a deeper understanding of how systems are set up and how best to locate and modify actors. This is not an issue for me (I wrote all the systems after all) but could be an issue for some teams.
Another flaw in this approach is that it has locked me into a specific version of Yarn Spinner. While the high-level approach described last post should work with any version, the implementation details vary based on which version one uses. For me, this is but one of several factors that makes upgrading to the latest version of Yarn Spinner unjustifiably difficult. Since I am already locked into a particular version*, this isn't an issue for me, but it may be relevant for others that are considering the approach.
This concludes my four-part series on Yarn Spinner Command Attributes. As you saw in parts 1 and 2, Yarn Spinner Command Attributes are powerful and easy to use, but come with some significant disadvantages. Fortunately, as described in part 3, implementing an alternative approach is not too hard - though it does come with its own disadvantages, which, as seen here, are unique to that particular implementation. Hopefully this has been an interesting series for you - whether that is from your own interest in using Yarn Spinner or merely as a curious observer.
As always, if you have any thoughts or feedback, please let me know.