A common requirement is to have characters move around a scene. There are different ways of achieving this with different degrees of complexity. As I write this post, I have not finalized my opinion on the best way, so this post is sharing some approaches rather than a final recommendation.
In a game, it is common to have characters move in the direction a player requests it to move. It can move forward, speed up, turn, go backwards and so on. Moving characters in a movie has different needs. Characters often need to arrive at precise locations at specific times. They need to stay in sync with other characters in the scene they may be talking or interacting with. The path they are going to take is predetermined and must be precise and repeatable.
So let’s explore ways of achieving this.
Using Animation Clip Root Motion
Many animation clips have root motion built in. (You may need to turn on a flag to have root movement applied.) Dragging a walk animation clip into a timeline will make the character move while the animation clip is playing. To have a character arrive at a particular point at a particular time, adjust the length of the clip, the play rate, or the start position of the character. This is the simplest approach, but has the least control.
Animating Position
Another approach is to turn off root motion for the animation clip (so it animates, but the character stands still) and animate the Transform component position X/Y/Z properties. You can use the timeline to set specific values at specific time points. This makes it easier to hit a specific position at a specific time.
A negative of this approach is if the speed of movement is too far off, the character can look like they are ice skating. The feet move at a different speed than the ground. This can be minimized using the above approaches – set the start and end positions and animate between them, adjusting other settings so it looks “good enough”.
This approach works. It can result in slightly lower quality at times, but is a serious option. An example problem is the animation clip plays at a constant speed, but if you use curves to adjust the speed of the character through the clip you may get a little ice skating (particularly at the start or end). Another problem is it does not work as well if walking along a curved path. It can be hard to control the character moving along the path correctly. Even if just walking in straight lines with corners. It takes a little effort to get the character moving at a constant speed through a series of waypoints. (And I have not started talking about having the character face in certain directions at particular times!)
So let’s explore some other techniques.
Make a Character Follow a Path
One way to have a character follow more complex paths is to create a Cinemachine Dolly and Cart. The Dolly track allows you to visually define “waypoints” and will curve the path to meet them. (One video I saw showed a free Bezier spline component you can get from the asset store to do nice curves, but the default CinemachineSmoothPath already supports simple curves so I did not explore it further. I suspect the component gives you more control at the corners.) Then on the character add the “Cinemachine Dolly Cart” component to the character and drag the Dolly Track into the Path property. The character position and rotation will then follow the dolly track by adjusting the Position property of the character.
But how to have two characters walk side by side? You could create two dolly tracks and animate the cart on both, but instead I tried an experiment and I copied the “CinemachineDollyAndCart” script provided by Unity (which you add as a component to make the character follow the cart) and added a new “offset” property. This allowed me to have two characters follow the same cart, but with a offset between them (so they walked side-by-side).
My first attempt at the script was not perfect – if the character walked on a curved path, the offset did not take into account the direction the character is walking, so an offset of “to the side” turns into “behind” if it goes around a corner. Another challenge was if the path does go around a corner, the outer character would zoom around faster in order to stay exactly next to the other character. This can look strange. So the offset is probably only useful on relatively straight walking paths.
I also discovered some important extra details along the way.
- It is recommended to create a GameObject that is the parent of the character, then have the CinemachineDollyAndCart move the parent object. This allows animation clips to adjust the child character object position. Some walk clips I found make subtle X/Y/Z position adjustments as the character bobs up and down, and sways left and right, as they walk, or slows down and speeds up the body is subtle ways as a part of the normal walk motion. These are lost if the body moves perfectly linearly. By having the two objects, the dolly cart moves the parent object (e.g. Helen_Offset) and the animation can make minor tweaks to the child character (e.g. Helen). The parent/child relationship merges the two effects.
- I discovered that Animation clips can have movement baked in, even if “apply root motion” is turned off for the clip! (That took more head scratching than it should have.) For example, I had a walk animation from mixamo.com that was a .fbx file. It was marked as “Read Only” in Unity. The solution was to clone the file (which for me created a .anim version of the animation) and then edit it. The root bone animation increments the Z coordinate (moving forward). Delete the Z property and the character started walking in place.
For the second point, here is the original animation. Notice the “Animator.Root T” which I believe is the root bone transform.
Stepping through the sequence the value for “Z” increases, indicating the character is being moved forwards.
To stop the character from moving forwards I duplicated the animation (which created a “.anim” version I could edit), renamed it, then edited the copy of the animation clip and removed the “Root T.z” property. I kept the “Root T.y” (causes the character to bob up and down while walking) and “Root T.x” (causes the character to sway from side to side when walking).
This is not a perfect solution. I may come back and try this again by working out the initial and final Z values for the clip, then adjusting each Z value to keep subtle body movement but not have the character move forward. For example, if the clip starts with Z = 0 and after 10 frames ends with Z = 100, then I would subtract 10 from the second frame, 20 from the third frame, 30 from the forth frame and so on so the last value has 100 subtracted. This would retain the subtle body movements, but stop the character from moving forwards.
Locomotion
My current plan however is to leverage the “Locomotion” package available for free from the Unity Asset store. This asset does not solve the movement problem above, but it solves other problems around a character walking along uneven terrain (e.g. step down a curb at the edge of a street, or walking up a set of stairs). It does a good job of making the legs of the character look more natural, but introduces additional complexity when merging with animation scripts.
It works by taking the animation clip leg movements, then introducing additional knowledge of the ground and gravity. It therefore augments the animation clip settings with programmatic calculations made on the fly based on objects the character is standing on. This additional capability seems worth the effort, but imposes additional limitations on the overall approach.
The demo scene in the package uses the “Character Controller” component to move the character around. This is capabilities like “what is the steepest slope a character will walk up”? It also stops characters walking into each other. So it has a number of advantages also worth using. The demo also includes several scripts that I found to be *almost* useful. They demonstrated concepts, but did not work quite as I wanted. So I plan to use the sample scripts and write my own shortly.
But it is going to fit together in a slightly different way than before. Rather than controlling the position of the character, I plan to use a Dolly and Cart to move a GameObject that is the “target” location for the character. I am then going to write a script to move the character to the target location using whatever speed and direction required.
To allow two characters side by side, I add two child GameObjects of the cart and move them so they are next to each other.
I made the GameObjects cubes to make them easier to see. The two cubes follow the track, staying side by side even around the corner (the other cube moves a bit faster at the corner).
The character then has a script (currently the “Follower” script in the Locomotion package) which tells it to move (follow) to the specified object (one of the target cubes under the dolly cart).
The sample scripts in the Locomotion package have other scripts to limit the maximum speed the character can walk, and limits on how fast the character can speed up (you cannot go from 0 to 100 instantly in real life). I plan to simplify these two scripts into a single script. I don’t want the restrictions imposed by the script as I plan to animate all the properties by hand. I also had some issues with rotation in the supplied scripts resulting in characters walking sideways instead of forwards. But I will leave that to another post as there are some other issues to sort out still.
Conclusions
The easiest way to animate character positions is to play a walk animation clip with no root motion applied, then adjust the Position X/Y/Z of the character. This works reasonably well on flat surfaces, to a certain extent.
As I personally plan (!!) to do a series, I don’t mind investing more effort upfront to end up with a better quality result that makes each episode easier to create. (To be honest, I may never create the series, but learning is fun!) As I result I have decided to use the Locomotion component (allowing me to have characters walk over non-flat terrain) and write my own version of a Follow script based on the provided samples. They will adjust the speed of the character to move to a “follow” point.
Before deciding how to have characters move I want to build a few more real scenes and interactions. The Dolly Cart and Track in the Cinemachine package might have been designed for camera movement, but it seems also useful for moving characters around (or to be precise, the follow targets for characters). But there may be better approaches still to discover.