Update 17/11/2024
As of Unity Behavior v1.0.4 - the issue described in this post is fixed. Kudos to the dev team for their rapid turnaround!
I’m a big fan of tools that allow people to build fun stuff whatever their skill level, and that’s a big part of the reason why I like using Unity so much. Though it indeed remains pretty difficult to make a game without getting your hands dirty with some code sooner or later, I always find myself drawn to ‘no-code’ features - whether it be Visual Scripting or Unity’s recently-released Behavior (or ‘Behaviour’, if you like to spell things correctly) package, which allows developers to implement AI behaviour on characters or any other in-game system.
I like the educational and illustrative possibilities these tools provide, and they can also be an interesting way to approach a tricky problem from a new angle.
With the release of the Behavior package, Unity finally provides something that Unreal has had for years. Though the Unity asset store contains a fair few different implementations of this functionality, it feels only right that Unity would provide its own, so this is a welcome addition to the toolkit in my view.
Failing Successfully
In a recent post, I built something resembling a gladiator battle arena, and decided that this would be the perfect testing ground for the new Behavio(u)r package:
The behaviour I wanted to implement for my enemy gladiator is very basic, but would be enough to start building some battle mechanics around:
When the player is not close to the enemy, the enemy patrols the arena.
If the player comes within a certain distance of the enemy, then the enemy should abort its patrol and chase the player.
If the player moves too far away again, then the enemy should revert back to its patrolling behaviour.
Here’s what that looks like with the new Behavior editor:
Aside from the ‘Repeat’ and the ‘Abort If’ nodes, this tree should be fairly self-explanatory to anybody with a beginner-level understanding of Unity.
Unfortunately, this tree, as simple as it is, fails immediately with a cryptic warning spamming the console:
If we jump over to our behaviour graph and debug the enemy gladiator object, we see the following:
To work out what the above warning message is talking about, let’s take a look at that failing node in its default state:
This node requires three Blackboard Variables. In ‘AI lingo’, a Blackboard is a container for the data the AI system needs to do its job, and we can create multiple variables within a blackboard.
Nodes within the behaviour tree can read from, or write to, the variables in the Blackboard, and in this case, we need the following:
A ‘Target’ variable
This is the placeholder variable for the GameObject we want to look for.
An ‘Agent’ variable
This is the GameObject we’re treating as the ‘origin’ of our search.
A ‘Tag’ variable
This requires a string which corresponds to a Unity GameObject Tag
Let’s look again at how this node is used in our behaviour tree:
As you can see, all fields are populated with a Blackboard variable (with the exception of the ‘Tag’ field, which I just entered directly since I’m lazy).
The warning message Unity was incessantly spitting out at us reads:
FindClosestWithTagAction: No agent or target provided.
Clear as mud! We obviously have provided both an agent and a target, so what’s the issue?
Let’s look at our Blackboard:
Here, ‘Self’ is a special, pre-defined Blackboard variable which always refers to whichever GameObject is running the graph. This makes it very handy to use as the input to our node’s ‘Agent’ field.
‘Waypoints’ is the container for our list of waypoints that the enemy should patrol between, and ‘PlayerObject’ is the container for the object we’re looking to find.
As you can see, both ‘Waypoints’ and ‘PlayerObject’ exist as variables, but do not have a value set. That’s ok, because we just need these to act as placeholders and will populate their value later. Or at least, that was the plan…
Surrealism In Practice
If I asked you to ‘go and find me the nearest snack’, I would expect that you would (if you were nice), go away, look in the cupboard for something tasty and unhealthy, and then bring it back to me.
In the same way, when we ask Unity to “Find the closest object with the tag ‘Player'“, I would expect that it would perform a search of all objects with that tag, and return whichever object was closest.
What I would not expect, in our ‘find me a snack’ scenario, is for you to refuse to look for anything at all until I first handed you a trombone, because that would be utterly insane, but guess what?! That’s exactly what Unity does!
That’s correct: in order for the ‘Find Closest With Tag’ node to do its job, you first have to provide it with an object - any object - before it will perform the search. It does absolutely nothing with that object, of course, except immediately throw it away, but provide an object you must.
What’s going on here? First, let’s take a look at the much more reasonable, perfectly usable ‘Find Object With Tag’ node:
And to contrast, here’s the code for the very silly and functionally useless ‘Find Closest With Tag’ node:
Can you spot the problem?
In the former (Find Object With Tag), the initial safety check ensures only that you’ve provided a blackboard variable:
This makes perfect sense: the purpose of this node is to find a value and assign it to the blackboard variable.
In the latter (Find Closest With Tag), the initial safety check is subtly different:
Here, the code checks that the values of the provided blackboard variables are assigned, rather than checking for the mere presence of blackboard variables.
In the case of the ‘Agent’ variable, checking for the value does make sense: we need to compare distances, so it’s only logical that we need an object to compare distances to. However, in the case of the ‘Target’ variable, it makes no sense at all to check the variable’s value, since that’s what we’re looking to populate in the first place!
Let’s see the practical implication of this logic:
Note that our enemy character waits around for us to assign a value to its blackboard variable ‘PlayerObject’, at which point it springs into life, and discards the value we’ve just assigned for the one it’s now allowed to search for. When we refer back to the code behind the ‘Find Closest With Tag’ node, this now makes logical sense, but I can’t believe that this is the intended behaviour. From a design perspective, it is incredibly unintuitive, and the warning message provided doesn’t describe the problem with enough detail that beginners would be able to understand the issue or how to fix it.
Not A Robot is a reader-supported publication, but a subscription is not for everyone. If you’d like to support my work without a monthly commitment, click below!
Bug or Bad Design?
Though I assume that the behaviour of ‘Find Closest With Tag’ is a bug, the warning message confuses things a little. After all, the message is perfectly correct: you did not provide a Target. Unfortunately, it’s also ambiguous: is it referring to the Target variable, or the Target variable’s value? The only way to know for sure is to read the code - and if you’re a beginner, this might be daunting.
Though no-code tools are not strictly for beginners or casual users, they are attractive to those types of user for obvious reasons: not having to write or read a lot of code to make cool things is obviously a draw for any budding game developer without the experience or time to develop their coding skills.
As I’ve mentioned elsewhere, I love to see people create. There’s something incredibly satisfying about watching somebody build something, whatever their skill level, and for that reason, I feel that tools that help more people express themselves creatively is A Good Thing.
Unity’s Behavior package seems like a good fit here. Imbuing game characters with their own intelligence and behaviour can be a terrifyingly opaque task to someone with no background or experience in this area - and visual tools like the Behavior Graph can help to demystify the logic that underpins character AI in games.
Though it’s unfortunate that I hit a stumbling block with the Behavior package after playing with it for 5 minutes, I’m hopeful Unity continues to improve and expand the toolkit. I’ve submitted a bug report about this issue - and would encourage anybody else who tries the package to do the same if they come across anything similar. Unity has something of a reputation for starting work on new features and then abandoning them, but I am in favour of this kind of tool, and would love to see Unity continue to support it long into the future, so I would encourage anybody to have a play around with it, and share your creations here!
If you’ve enjoyed this post, take a look at some of my other Unity-related posts: