After using Unity over the years for various projects, I’ve come to two conclusions: overall it’s a terrific engine that I would recommend to anyone interested in getting into game development, and that it’s built in character controller sucks. I’ve been working on a custom character controller for a couple weeks and noticed that finding any kind of reference or learning material on the subject is pretty difficult. So since I couldn’t find anything out there to read…I figured I’d write something instead! I intend to post a few pieces here outlining what I’ve learned so far and some issues I’ve encountered. For any actual implementation, I’ll be using the aforementioned Unity game engine. You can visit their website here and download their latest version here. I really dig Unity. It takes care of a lot of the low level under the hood side of game development but still gives you enough freedom to do just about anything. It also has a really great and active community that is the epitome of helpfulness.
Unfortunately, as previous stated Unity is also home to the world’s worst character controller. I suppose I should loosely define what a “character controller” is before I build my case against Unity’s. More or less it’s the code (or class, or whatever) that handles and resolves your character’s collisions in the world. Unlike boxes and barrels and whatnot that can be taken care of by the rest of the physics engine, characters require special code to behave differently. However, since we’re doing collision tests, we still need to pick a geometric shape to represent our character. Most 3D games use a capsule collider.
Capsule colliders are great for a wide variety of reasons, but we’ll see that later. For now, I’m going to go over the basics of character controllers in just 2 dimensions, to keep it simple for now.
You’ll notice I’ve labelled the axes z and x, instead of x and y. This is because I’m going to treat this as a top down view of a three dimensional world, which we’ll eventually move onto. The character here is seen as a blue circle, as a capsule seen from top view is a circle! The green rectangle we will treat as a wall. Ideally, characters cannot walk through walls. So should the character intersect with it, we’ll want to detect the collision, and properly resolve it. We’re going to pass over the actual collision detection, i.e., checking to see if the circle is intersecting the box, for two reasons. One is that Unity has a fair amount of resources (which we’ll go over later) to handle this, and two is that there is a pretty good selection of reference material out there for collision detection. We’ll focus on getting the colliding object to resolve it’s position properly based on expected behavior.
Shown above we have the controller attempting to move into the wall. To prevent this we run a function that performs a sweep test, from the initial position of the controller to the desired position of the movement. The test detects that a wall is in front of us, and returns the distance. Using the value we move the controller in the direction of the test the distance the check traveled, placing it directly beside the wall. (Aside: Unity has several built in functions for this, including Rigidbody.Sweeptest, Physics.SphereCast and Physics.CapsuleCast).
However, this really isn’t the kind of behavior we want. If we use this method, the character will be immediately halted in any movement he makes if he collides with an object, if even only slightly. This is undesirable as it doesn’t reflect the way real world objects tend to bounce and slide off each and, more importantly, it would be annoying as hell to play.
This is a much more desirable behavior. The initial sweep test is performed in the movement direction for the movement distance. When the sweeptest contacts the wall, the character is moved directly to it, just like before. However, this time around we further move the character upwards to make up for the lost movement, which allows it to slide along surfaces. This is a great example of the desired behavior of the controller, but it isn’t the best way to implement it. For one, it’s not very efficient: every time you want to move the controller, you need to run this function. This is fine if you just move it once per frame, but if you plan on doing something different–for whatever reason–you’ll need to rerun the function. Two, collisions resolution is reliant on character movement direction and distance. If he just magically finds his way into a solid wall (as character controllers are wont to do) he’s not going to get automatically pushed out. In practice, I’ve found this method a massive headache.
Here is that terrifying situation put onto screen. We see our hero is currently within the walls of the object. Instead of looking at collision resolution as a response to a movement, like the previous examples, instead we are going to treat it completely independently. We no longer are concerned with what direction the player moved or how far he moved. Instead, we will consider only where he is at this moment, and whether his location is a problem or not. In the above figure, we can see that currently the player is intersecting the wall (he’s inside it!), and therefore his current location is a problem and needs to be rectified. Since we are no longer resolving collision as a response to movement, we do not know where his previous position was or how far he moved. All we know is that currently he is stuck inside a wall, and we need to move him out of the wall. But where to put him? Just like in previous examples, we should only push him out so that he is just touching the edge of the wall. We have many locations that are candidates for this…
Each of the transparent yellow circles indicate a possible position for the character controller that satisfies our goal–to push him out of the wall to a point somewhere on it’s surface. But which point do we choose? Simply put, calculate the closest point on the surface of the wall with respect to the controller’s location.
Here we have calculated that the nearest point to our controller’s current location lies to the right of us. We then move the controller to that point, plus the radius of the controller (shown in red).
This concludes the first part of our epic adventure into the mysterious realm of character controllering. Next time I’ll start talking about my implementation in Unity, and some of the more complex functions character controllers use.
Acknowledgements and References:
Most of the knowledge here I’ve acquired by exploring two main information sources: a Unity forums post by the user techmage, and reading through/reverse engineering a Unity custom character controller package by user fholm. I don’t know how to pronounce that either. Fuhholllme. Disgusting. Reminds me of phlegm. Anyways, you can download his package off his github here, under the RPGController directory. This is an amazing project overall that I will be exploring the next few sections, and is really terrifically coded. Apparently he does Unity consulting too if anyone needs that kind of services. Finally, if anyone has any good reference material for this subject, or anything relating, sharing it here would be a really great way to expand on the topic!