Our controller can now leap into the air and gracefully pirouette throughout the level. On the other hand, right now he might be a bit too acrobatic in the sky. Playing the game, you’ll notice that while jumping we retain a fine control over our movement direction. It’s very common in games to lose control of most (or even all) of your ability to change movement direction and velocity in jumping. This makes sense intuitively, as it is our legs contacting the ground that allow our bodies to accelerate and rotate. In this lesson, we will modify our controller to remove movement control in the sky.Before we sink our teeth into this delicious new feature, we should begin to do some cleanup in our code. You’ll notice each time we want to make use of the CharacterController, we need to call the GetComponent method. While this works fine, it’s a waste of resources to call this method over and over each frame, when the returned value never changes. Instead, we’ll cache the component in a variable. Add the following variable to the class:
private CharacterController controller;
This will store a reference to the CharacterController attached to our Player object. It won’t retrieve the component automatically; we’ll have to call GetComponent once to get the object. We’ll do this in the Start() method, which is called exactly once on every script. Add this line inside it:
controller = GetComponent<CharacterController>();
And now we have a handy reference to the CharacterController that we can use whenever we want. To make use of the variable, replace all the GetComponent calls with “controller”. For example, we would change the line checking the isGrounded property to the following
In addition to being more efficient with computational resources, it also makes the code much cleaner and easier to read.
Our next step is to remove the ability to move in-air, as well as ensure our controller maintains his forward velocity after jumping. Since this means that we’ll need to keep track of our character’s velocity at all times, let’s add a new variable to do so:
private Vector3 moveVelocity = Vector3.zero;
This will store our characters velocity on the Z-X plane, controlled by WASD. Next, let’s make sure that we can only move our character when our feet are on the ground. Remove the line that retrieves the keyboard input, and in the if statement that checks if the controller is grounded place this line:
moveVelocity = transform.TransformDirection(new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"))) * speed;
We’re not doing much new here—it’s simply a few lines of code we’ve written before compiled together into one, and then the result stored in moveVelocity. You’ll notice now that at any given frame, we are able to access our controller’s total velocity. Our keyboard movement is stored in moveVelocity, and our up-down in yVelocity. Let’s put them together using the following line:
Vector3 velocity = moveVelocity + yVelocity * Vector3.up;
Place this just above the final call to Move, and then replace the current controller.Move call with this:
controller.Move(velocity * Time.deltaTime);
…which is much simpler and far more concise! By building a velocity variable this way, we’re able to keep track (and modify) our player’s total movement vector at any time. Running the game will show the results of what we’ve done: our controller can now only move when he is grounded on the floor.
However, the overall movement feels a bit tight—on the ground, the controller is able to change direction on a dime, and having no movement in the air feels a bit too constraining. In a future lesson, we’ll once more modify our controls to add in smooth acceleration and deceleration, creating a better overall playing experience.
Thanks for the great tutorial!
Learned some new things and hope you keep releasing more lessons 🙂
Glad you enjoyed them!
Please keep this up! Huge fan of your work on character controllers. it’s something I definitely want to specialize in myself.
this tutorial is really really awesome. I hope you will go on with this!
Will there be more episodes? A continue of this? 😛
Definitely! Can’t end at unlucky number 13.
Hey dude! Glad you’re making these tutorials! I’m a huge fan of your SM64 character controller! I know Ninty made you rip it off the net, but there are a few ghost copies floating around the web, and I would love it if you’d give people like me an inkling of the structure of your character controller looked like in it by offering, say, the name of each script and a summary of what each script in the project does for Mario’s character controller (no need to go into the how!) because, at the very least, those of us who can program our own, would love to understand what you had to go through to recreate such an awesome controller — even if it’s all theory! — because that’s some stellar work to be able to create such great theory an execute it so well!
I’d even be satisfied with a simple flowchart showing the interconnection of the objects and scripts used (perhaps with simple summaries) to see a skeleton of your approach in how you actually applied the Super Character Controller in practice to recreate Mario’s moveset!
I know I’m not the only one who would greatly appreciate you sharing your approach with us as well! Pretty please, with cherries on top!! D:
After studying your SM64 character controller classes in-depth for an entire 24 hours straight, after previously reading through all of the SuperCharacterController tutorials you’ve made here, I’ve decided that the one ultimate tutorial we’d ALL appreciate a million-times more than what I suggested in my previous comment is actually a “Math3d” library tutorial describing how you applied things like Mario’s wall-skirting, ledge-grab/climbing, wall-kick, and belly-sliding down steep slopes, using things like normals, vector-addition + rotations, and the relative importance / use-of certain math functions that made all of those things possible using stuff like “Math3d.ProjectVectorOnPlane”.
Your SuperCharacterController tutorials are great, and they’ve totally helped me overcome my fear of how to approach basic 3d movement… all except for when I think of doing things like setting the movement speed/direction of a player that’s sliding backwards down a sloped hill just after a jump — while also facing a wall. 😦
😦 😦 😥
Looking heavily at your “MarioMachineStates.cs” script tells me there are lots of things like “Math3d.ProjectVectorOnPlane” and “SuperMath.PointAbovePlane” or “Math3d.SetVectorLength” that makes stuff like “Quaternion.AngleAxis(-angle, r) * controller.up;” and “if (Vector3.Angle(col.normal, controller.up) > 80.0f)” look pretty scary when you know what normals and angles are, but you don’t know how to use them to do stuff in Unity (like setting movement speed / direction while sliding backwards down a sloped hill while also facing a wall) properly. Here’s hoping you might be willing to share some of your advanced knowledge with us aspiring wannabes who just want to make great game-physics like you! 😦
A lot of it comes down to learning the basics of 3D math. Beyond some Vector projection, there isn’t a ton of super complex stuff going on. In theory, this tutorial set would eventually address most of the core 3D math operations (and some of them already have been addressed, such as converting vectors from world to local space). Unfortunately, I don’t have a ton of time to expand on these right now, as I’ve recently started a new job and am still putting time into my multiplayer game. If you’re interested in learning more, I’d suggest reading up on how some of the primitive operations work—Dot, Cross, Projection, Determinants and whatnot, and what their properties are (i.e., why are they useful?). Anyways, best of luck on your projects, I’ll take your suggestion to heart and make sure to include some stuff on the topic as the tutorials get more advanced.
Hi there! 🙂 Thanks for your reply! I definitely understand the lack of time!
Tbh, I’ve been doing a LOT of studying of these primitive operations, and I get the how they all work — in theory — but just as one does not typically build a 3d character model using cubes and spheres, these concepts are just the tip of the iceberg.
I need to know where to go from here.
The major problem is, there’s not much info out there that applies this abstract math in any truly practical ways related to games (i.e. ledge-grabbing and then climbing up them in Unity, skirting a wall in 2 directions, wall-jumps, or sliding down backwards on hills that are too steep as you try to walk up them a little — real basic, practical, move-set stuff!) You mention to look for how VectorProjection and Dot/Cross Products can be usefully applied in games, but I cannot, for the life of me, seem to find anything on the net for why/how these are important for, say, a character’s movement in a 3d game-environment, yet I’ve seen you use them extensively in your Mario controller. Maybe you’ve had a few classes in 3dmath?
You understand this stuff really well, so you learned to use these concepts somewhere in regards to collision/speed/geometry/normal-angles/etc., but through all my searching, I’ve found almost nothing practical about moving around in a 3d environment, only very basic or VERY ambiguous tutorials — just basic theory and mentions of stuff such as “dot products: learn them. they’re useful for engineering and games.”
I’d be insanely-grateful for some hint of your approach to (even in theory!) basic things like “ledge-grabbing checks, then climbing to a point above” or telling the angle of the wall or ground in front of you (preferably with your SuperCharacterController!), or really any other really-basic-moveset or 3dmath functions you might think useful to share (i.e. what is Vector Projection, and why is it useful?, or what’s the true use of “SuperMath.cs” included in your project?) It’s like the answers to these questions have been hidden away in the dark recesses of google or something because I’m apparently not the only one attempting to find how to use this stuff to make games! — especially in Unity (with all its quirks!)
I want to be at least on a level where I could recreate the Mario controller myself at some point. I would love to follow in your footsteps, but I don’t know where else to go! I’d love to dive deeper than just the tip of the iceberg with this stuff! And I know you don’t have much time to offer, but I’ll take anything at all. Thank you so much for the time you have given already. I really hate to ask for anything more.
By and large it comes down to learning the underlying math and seeing them as a set of tools. Once you deconstruct a problem, you’ll begin to see how the tools can be applied. Wall grabbing is a good example for this. What are the steps to getting Mario to grab a wall? First you’ll need to define what you want to happen, or your [i]expected behaviour[/i]. You expect that when Mario is colliding with a wall, and the user is pushing the control stick towards that wall, and the wall is angled towards Mario, and there is ground on the cliff to climb to, and Mario is high enough up the wall…he can then move into the LedgeGrab state.
Once the problem is broken down, you can start applying various geometry and logic tools to solve it. Teaching all of them would probably be beyond the scope of a comment reply, but to answer your direct questions:
– Vector projection is, for the most part, a property of the dot product.
– Dot product is also called the scalar product, and can be thought of as the multiplication of vectors. It basically gives you a number between -1 and 1 (for two normalized vectors) that changes based on the angle between the vectors. You can use the dot product to the find the angle between vectors (but you don’t have to; Unity has a method for it).
– Cross product gives you a vector perpendicular to the two provided vectors.
– SuperMath is just a static utility class where I dumped useful methods. Most of them aren’t really all that math-y (I just liked naming things Super____.cs).
Bear in mind lots of Mario’s moves are hidden behind clever animations. Try turning off his art asset and just watch the capsule move around–things will seem much simpler.
I never really had a comprehensive resource for studying this. I do have a CS degree, but I didn’t do any 3d math courses during my tenure. I just made lots of games, most of them terrible, which taught me along the way. I always recommend asking questions on the Unity forum, since everyone is really friendly.
amazing i love your tutorials man,
Glad you liked them!
This is exactly what I avoid when making games. In my opinion, there was nothing wrong with the ability to change trajectory in mid-air. I was happy with the other tutorials, but this tutorial only serves to mess up a perfectly good setup. Moving in mid air helps with precision platforming and making jumps. Yes, it is not realistic, but video games are an escape from real life, so having the ability to change trajectory while in the air is a good thing, especially when making long leaps onto small platforms. There is nothing intuitive about limiting the player’s ability to change their trajectory in the air. I prefer to give the player more control over jumps, because most of my games revolve around jumping. Other than that, I found your other tutorials very helpful. It’s just this one I have a beef with.
Hey Dylan, I definitely agree with you—there’s no reason to make games adhere to realistic movement standards. However, in the majority of platformers out there (such as Mario) that do allow air movement, the amount of control you have is either limited (such as in Super Mario Bros) or different in some way (Mario 64 does not allow turning, for example). The reasoning I included zero-ing out air movement in this tutorial was primarily for teaching purposes as a stepping stone for future tutorials where we would modify the controller to have different movement behavior in the air. Unfortunately up until recently I hadn’t had the time to write any future tutorials, but hopefully soon I’ll continue the series.
I’m glad you found them useful either way!