Tuesday, November 3, 2015

Unity3D At Home Project - Day 7 - Walking with Robots


So it's time to move into the next phase of our project. We've done some simple experimentation, and dabbled in some of the core pieces of a 3rd person action game. We created terrain, texturing hills and mountains, and learned about how trees & foliage work. We got a character controller and a camera controller to allow us to drive a character around. We mapped some animations to a simple biped, and learned about the animator controller. And we established simple multiplayer support, connecting a client to a host, and letting two players run around the world together.  But none of this is giant robots fighting each other, which is our goal. So with our base pieces in place, it's time to say goodbye to Unity-Terrain project, and start working with giant robots.


The Mech
The first thing of course, is to acquire some robots. This means a shopping expedition at the Unity Asset store. There is absolutely no shortage of models and resources aimed towards mechs and giant robots, because well, duh - giant robots! I spent quite a bit of time shopping various models and pieces, but decided upon the Mech Constructor pack.  This pack provides the mechs as a collection of interchangeable pieces that fit together. This system is going to provide a lot of flexibility in building the mechs, and providing weapon variations, which in turn provides us with a lot of flexibility in our game design.

Here's a cold hard truth about indie game design. If you're building something traditional, and commercial, often your asset availability will dictate your design, not the other way around.  In an ideal world, we design our game without thought towards asset or code availability - we build what we think is fun, and then create the assets to support that. But in the real world, if you're a small team trying to make something big, any design you create will have to be supported by assets. And if you can't easily acquire them, you'll have to tackle making them - and you may or may not have the ability to do that, readily anyway.  So for instance, say in our giant robot fighting game, we want our robots to have shields. We'll to support that, we'll need some kind of cool shader effect to apply to the robot that has the shields, sound effects for powering up and down the shield, and probably some spawn particle effects as well. Maybe a collection of weapon impact particle effects and sounds to support hitting the shielded mech. Realizing that you don't have the time, money, or resources to easily build all of that, you may decide shielded mechs may not be a thing in our game.  Unity's greatest strength to indie developers is the availability they give to acquire assets and technology so that you have more flexibility in making those tradeoffs. But make no mistake, every design decision you'll make should be influenced by whether or not you have the resources to support it.

Okay back to our project. So I purchased the mech constructor pack, created a new project called RobotSandbox, and imported the new assets. Within a relatively short amount of time, I had pieced together my first mech. A light mech - a chicken walker (if you're familiar with mech types, and who isn't!), with a matching pair of cannons for weapons.  Good stuff - he'll do fine for our prototyping.

New Controller & Camera
The Standard Assets pack that comes with Unity has a pretty robust controller & camera rig. I ditched my simple camera & controller from the last project, and set about using these more robust pieces. Now, I like to preserve stock code and assets intact if I can, so I made copies of these classes, called MechController & MechCamera, and put them in my own project's scripts directory. That way I can modify these classes to my heart's content, and not worry about messing up the originals. And modify them I will! 

So I added the new controller and created a very simple animation controller that just contained a single blend tree for the robot's motion that had two states - idle and walk. I then added the necessary parameters to the animator for the controller to be able to work with it.  Set up all the pieces, and pushed the play button. And nothing. Well, not exactly nothing, but not what we want either. The mech animated correctly (mostly), and would turn, but wouldn't walk at all. Time to dig into that controller and take control. So to speak.

Root Node Motion
So it didn't take too long to figure out the problem here. Unity's Standard Asset 3rd person controller is heavily dependent upon root node motion animations. So what does that mean, exactly? In a typical motion & animation set up, the avatar's movement is determined by external forces, and the animation state is derived from the movement. So user inputs get turned into velocities and rotations,, the velocities & rotations are fed to the animator, and it blends to the right animation state. Your animations are all animated in-place, which is to say the root node of the model doesn't translate in anyway through the animation.

In a root node motion animation system though, the animation drives the movement. Movement animations, such as run and walk, actually translate the root node of the model through the duration of the animation some distance. Inputs are fed into the animation, the animation blends to the correct state, the model is translated some distance through the course of the animation, and that distance is fed back to the simulation, which updates the actual position of the avatar. So in brief, typically motion drives animation, but in root-node motion system, the animations drive motion.

Which is right for you?
For just about as long as I've been making games, at the beginning the project, we have this discussion. Root node animation movement, or standard movement animation? The artists always want to use root-node animation, as it provides the best linking between animation and movement. Your characters no longer slide, or rotate in place, or move about in any way without a one-to-one sync with an appropriate animation.  The programmers & designers though inevitably want to use a standard motion system, because there are so many things that can drive character speed, and they want complete control over that, and don't want to funnel it through the animation system. In every game that we started out attempting to use root node motion, we eventually abandoned it, and returned to the traditional method of movement driving animation.

What am I using?
Well despite the preponderance of evidence I have from my own game development career, this is, as I've often said, an experiment. And I'm kind of interested to see if I can make root node animation work. There's only one problem. The animations that came with my mech pack don't have movement built into them. They are in-place animations.  Remember what I said earlier about your assets driving your design? Yeah that.

But as it turns out, there is a poor-man's way of rigging a root node motion derived system onto in-place animations. You can do it through a curve. Unity allows you to add arbitrary animation curves to any existing animation. If you create a parameter in the animator with the same name as the curve, the values of that parameter will be driven by the curve values during the playback of the animation. So by creating a float parameter called WalkSpeed, I could vary this parameter through the duration of the walk cycle to more closely resemble what the mech was actually doing during that animation. Then in the callback provided by the animator, I can query that parameter, and turn it into a velocity applied to the avatar's transform.  Viola! Crude root-node animation!

WalkSpeed Curve
for the walk animation
Of course, my first guess at these values was absolutely terrible. The values were nowhere near correct, plus I still needed a speed scalar to actually get the mech moving. The mech looked terrible turning in place, as there are no in-place turn animations (and the walk anim really wasn't suitable). So this is where you cue the tedious and incredibly time consuming task of tweaking values, adjusting equations, watching the results, and repeating to get what you want. I spent pretty much an entire Saturday morning doing exactly that, and in the end finally settled on an animation curve and movement speed scalar that provided the best variations for both the walk and the run cycles that produced the least sliding. But I wasn't done yet, not by a long shot. 

Acceleration
So I had the mech's walk & run motion looking pretty good, when it was at full speed. But it still looked wonky going from rest to  moving, and then returning to rest. After thinking about it a moment, it occured to me that the sudden shift in movement value was the culprit. The input controller returns a value from -1 to 1 along the vertical axis (forward). But when you're using a keyboard, it goes immediately, in a single update, from 0 to 1. It was slightly better with the gamepad, but still you tend to just jam the stick forward when you want the player to move, so it largely produced the same results. So going from rest to full speed created all kinds of problems. The feet slide all over the place as the animator tries to blend through three animations over the course of a few updates. This is a mech - it's not meant to go from rest to full blast in a single update. We needed an acceleration factor, so that no matter how much you jammed on the forward vector, the mech would take time to get up to speed (and conversely, time to return to rest). Not only does this make the mech feel better, but it gives the animation system time to blend through all the animations in a satisfying manner.

At first I tried applying the accleration directly to the physics equation on the animation callback, where the actual motion is set. The first version of this had the unintended but hysterical result of causing the mech to shoot off into space, sailing over the edge of the platform and plummeting to oblivion. Fun, but not really desired. I also realized that this was a bad place to modify the movement, because it would adjust the speed after the animation, which meant the animation couldn't take advantage of it. That is, I was directly undoing the work I did in setting up the root node animation to begin with! No, for this to work right, the dampening had to occur on the input side, so that it was fed into the animation, so it could adjust appropriately.

Once I realized that, it was a pretty simple change. Instead of taking the forward motion value directly from the input value, I made the forward motion value interpolate over time to the desired input value. I then added an accleration scalar into the equation, so we could have some mechs accelerate faster than other mechs. This worked perfectly!  The last piece to handle was turning in place. And I decided that these mechs just don't turn in place. My move controller (remember, borrowed from Standard Assets) allows an "at rest" rotational speed, and a "moving" rotational speed. It calculates the current turn speed by interpolating between those two values by the current value of forward motion. By setting the at test rotational velocity to zero, the mech doesn't turn if it's at full rest, but as soon as you start moving, you can turn. I like this effect. It's a mech. If you want to turn, you need to get it moving.

Final Blend Tree for the Mech
Walking Backwards
This turned out to be a tougher problem than you would think to solve. At this point, my animator only interpreted forward motion values from 0 to 1. If you haul backwards on the stick, you get values that range to -1. So I modified the range of values for the blend tree to accommodate a range of -1 to 1. But what animation to play when walking backwards? I don't have a "walk backwards" animation. I tried just playing the walk cycle animation, but ugh, it just looked terrible when the mech was trying to move backwards.  Plus, when moving backwards, I noticed the mech kept trying to turn sharply in one direction or another. This was a mess.

You've probably already figured out what I needed to do here, but I'm not always terribly bright. So I thought, well maybe these mechs don't walk backwards. Maybe when you haul backwards on the stick, they move forward, but try to turn sharply about. So I tried this. I set up the animator to use the walk animation from -1 to 1. From -1 to 0, the blend would play the walk animation only though - it wouldn't blend to run. The rotational value was already trying to calculate a turn from forward to 180 behind you, but dampened by the rotational acceleration, so as soon as you started moving by hauling back on the stick, the mech would immediately try to turn around.

So, this worked pretty well, in that it had the desired result. But there were still two problem. The first, was a design issue - in that in this mode, every time you wanted to back away from an opponent, your had to turn your guns off the opponent. You could argue this is a good thing - makes you think about turning away! But I had a feeling it would drive players crazy. I think it would drive me crazy. It also just felt unnatural. Mech battles are, for better or worse, often two mechs standing toe to filling each other up with rockets and lasers, while slowly backing away. But design considerations aside, there was another issue. When you pulled back on the stick, and the mech started to turn and move, it would accelerate. But then as you got turned around, and the camera swung about behind the mech, you would naturally shift from pulling back to pushing forward. Well this would cause the input values to vary from -1 to 1. But because of our own acceleration parameters, this would cause the mech to slow to a crawl, and then start to accelerate again. That meant as you turned 180 degrees, and then started to move forward, you couldn't maintain speed.

Set the animation speed to -1 to play the anim backwards!
So, this wasn't terrible. I played around with the mech like this for  a long time, and had just about decided that was how it would work.  But I really wanted it to walk backwards. So I went back to the animator one more time to see if there was something I could do.  And it was there, while scrubbing through the walk cycle, it hit me. I just needed the stupid walk cycle to play backwards. If I played the walk cycle backwards during the backwards movement phase, it did exactly what I wanted! Surely there was a way to specify to Unity that I wanted to play an animation backwards? And it turns out, there was! In the blend tree parameters, there is an animation speed value, that defaults to 1. You can adjust this to make the animation play slower or faster during that phase of the blend. And if you set it to -1, it plays the animation in reverse! Perfect!

So by now it's time to skip to the end. That was the key to getting walking backwards working correctly. I damped backwards movement by half, so you can only move at walk speed going backwards (not running). And it took me awhile to figure out how to adjust the turnspeed amount when moving backwards so the mech didn't immediately try to turn about when you pulled back, but that was a pretty simple change as well.

Multiplayer!
Multiplayer Again
The last thing I did was to also convert this project into multiplayer, like I did for the Terrain project. It was a relatively simple process, now that I've done it once before. I had one gotcha where nothing would move over the network because I forgot to add the NetworkTransform component to my mech avatar. Turns out that's pretty important.



Mechs in Motion
At last, I was really happy with my mech's movement. It rests comfortably in idle. Jam forward on the stick, and she moves out, matching the animations really closely. Turn left and right, and she turns with you, slowing as you turn, which is perfect. Let go of the stick, and she rumbles to a rest satisfactorily. And haul back, and she slowly moves backwards, and you can turn her left and right as she moves. I love it! Man, all I need now are guns!

Here's a video of our finished mech in motion:


Next!
I'm in crunch at work this week, so who knows when I'll get a chance to work on the project again. I think the very next thing I want to add are sound effects. Our mech just isn't a mech until it sounds like one. That will mean a trip back to the asset store, undoubtedly. After that, I think I'm happy enough with camera and motion to start thinking about weapon systems. 

Until then, if you have any questions for comments, please leave them below.  Here are some useful links:

This is the asset I used for the mech models and animations

This was the tutorial I used for figuring out how to get my poor man's root motion animation working

Unity3d At Home Project
The master blog page for the entire project




No comments:

Post a Comment