Friday, January 29, 2016

Production Post 2 : Moving the Node Dragon

The first (and still ongoing, but coming to a close) tasks for the new programming team have been regarding dragon movement. We wanted 5 different movement schemes (including the legacy rotation scheme) to be changeable at runtime, so we can add new types of playable dragons. Given the relatively large size of the programming team (4), it is crucial that these schemes are highly detailed and polished; and bug free. I couldn't have thought of a better set of tasks to welcome three new programmers to the team than four new movement schemes. It gives us a chance to all work relatively independently on 4 unique sets of code, while requiring that that code be implemented in such a way that it can be brought together easily at runtime.

This hasn't been an easy task by any means, though. Given that the tail functioned in some pretty heinous ways (if it looks polished enough move on to the next thing), and that the tail is the better half of all five movement schemes, getting everything going has been a challenge.


But let's talk about the state we've gotten it to! I'll start with how the head moves, as it is the driving force of the whole system.


Setting Up


We'll start by defining these four variables, publicly modifiable in the editor. These will control the rate at which our movement schemes move.


Next, in our Start() function, we add all five movement scheme scripts to the dragon's head, as components. We immediately disable all of them, and then make a call to the same function that will allow us to change movement type at runtime.



The function that lets us switch movement schemes at runtime. When the previous movement type != the current one, we switch contexts to the new one.


Movement_Behavior abstract class


Now that everything's instantiated, we can get the head moving. I'm only going to post about the base class that our behaviors inherit from:

abstract public class Movement_Behavior : MonoBehaviour

Since all of our movement schemes always move forward (unless paused), we can always move forward in the direction we are facing:

head.velocity = head.transform.forward * mForwardSpeed;

For now, we'll separate the forward motion from the rotation, and just focus on making the tail nodes smoothly look at the node in front of it. A simple LookAt vector can make the dragon function, but making it smoothly function is a bit more difficult.

AngleSigned is a function which returns the angle between two vectors as a value between [-180, 180].
mRigidbody is the dragon's head's Rigidbody component

Currently, adding a meaningful rotational acceleration seems to make the tail separate, and is largely unnoticeable. So, in our current implementation, we just set the acceleration to be higher than the max velocity so it always rotates at the max velocity.

Tail_Behavior

In order for the tail nodes to not slowly separate from each other, we use about the same RotateTowards function above. I've found that if the angular velocity of the tail is shorter than that of the head, then the tail will peel apart. This is because the magnitudes of the tail node velocities are the same, but the directions are not, so this slightly-off direction will in a sense steal some of the velocity's speed. These effects are amplified the further away a tail node is from the head.

We tell each tail node about the node in front of it, like a forward linked list. That parent node's position is used as the seekPos parameter we passed into the function above. Here's a snippit from that:




And that's the core of how the dragon moves!







No comments:

Post a Comment