Been awhile! Don’t plan to post here too much in the near future, so figure I should put a link to my Portfolio Site if anyone wants to see what I’m up to now and again. I also have a Twitter and Reddit now that I’m somewhat active on.
Being the amateur Unity evangelist that I am, I have introduced several friends and colleagues to the engine (like at least three, possibly four). And each time, I find myself teaching the same set of tools and knowledge. To avoid doing that a fifth time, I’ve set out to write a series of Unity tutorials for the absolute beginner. I’m aiming to cover the most important topics in Unity—things like character movement, spawning objects and enemies, importing textures and other assets. Ideally, the series will eventually be comprehensive enough to expose a complete beginner to all facets of developing a complete game, from coding to art to sound.
The tutorials are available below, or from the top bar. They start with a completely blank project, eventually building a simple third person shooter.
Due to multiple requests, I’ve built a GitHub repository for the Super Character Controller project. It can be accessed here.
The project currently on the repo is extremely similar to the 2.0 release from several weeks ago, with the only main differences being that the demo models are now in the .FBX format, as opposed to the .max format. This is to ensure that users without 3ds Max will be able to download and contribute to the project without any errors.
This is the first open source project I’ve created, so please bear with me while I iron out any details I’ve missed and get the hang of this. As always, any contributions, comments or tips and tricks are appreciated.
Immediately after completing the Mario 64 HD demo project I began working on the second version of the Super Character Controller. For over a month now I’ve had a download link up on the controller’s page for a Beta version of the project, which allowed users to see what I was working on at the time and contribute to the latest version.
As per usual, the Super Character Controller can be downloaded from it’s main page.
2.0.0 adds a number of features that are discussed in detail in the accompanying documentation, changlog, or previous blog posts, but below is an outline of the major changes.
The grounding algorithm in version 1 of the controller had numerous problems, mostly that it was inaccurate and inconsistent when dealing with several edge cases. Most of these have been resolved, with the solutions described in a previous post here.
Replacing the RPGMesh class is the BSPTree. BSPTree recursively partitions a mesh’s triangles into two sets using a series of partition planes. The component is complete, but not necessarily fully optimized or the best possible implementation of a BSPTree. More can be read in the Future Work section of the documentation.
Other updates can be viewed on the controller’s main page, as well as a list of contributors to the project. These changes are also showed in the README in the package, and all code and libraries used are sourced in the PDF document also included. If I missed anyone in the changelog or anyone’s code in the sources section, let me know so I can fix it.
There’s been a lot of interest in having a Git repo setup for this project, so now that version 2 is out it’s definitely on my radar, and hopefully something I can get setup soon. I’ve never setup and maintained an open-source project, so if anyone has any suggestions on best practices or tips n’ tricks, please leave them in the comments below.
Despite having written five posts about the Super Character Controller, up until now I’ve only briefly touched on the issue of ground detection. Knowing what your controller is standing on is a hugely important topic, since a great many of your player’s actions will depend on what kind of ground he is standing on, if any at all. Good ground detection can make all the difference between a smooth playing experience and a terrible one.
So what do we want to know about the ground beneath our character? We definitely need to know how far away it is. We’ll want to know if our character has his “feet” touching the surface of ground or if he’s 6 meters above it. We also will want to know the location of the point on the surface of the ground directly below us, as this is important for ground clamping, highlighted in a previous post. Thirdly, we will want to know the normal of the surface directly below us, a direction represented by a Vector3. And lastly it will be necessary that we know the GameObject that the ground belongs to, to allow us to retrieve any attached components to the object that may be relevant to grounding. (In the Super Character Controller, we retrieve a SuperCollisionType component that describes properties on the object).
Looking at an earlier post surveying the Unity Physics API, the most obvious solution to our problem comes in the form of Physics.Raycast. We can fire a ray directly downwards at the ground below us. Through the RaycastHit structure we can retrieve the contacted point, the distance traveled, the normal of the surface, and the object the ray collided with. This seems to fulfill all of our requirements at first glance, but looking more closely reveals a significant problem.
Our character controller is represented by a series of spheres to form a capsule, which means that when he is standing on directly flat ground, the nearest point on the surface of the ground will be exactly radius distance from the center of the lower sphere in the capsule. This is fine, but problems appear once the character is standing on a slope. If we now fire a raycast directly downwards (as before) the point we contact is no longer the closest point to our “feet.” This will cause issues with our ground clamping method (among other things).
Luckily, we have a savior in the form of Physics.SphereCast. Instead of casting a thin ray downwards, we cast a sphere (if it wasn’t already clear enough in the name). This will solve the above issue by ensuring that our controller is properly aligned to the surface below us, regardless of it’s normal.
While SphereCast works extremely well in representing our controller’s “feet,” it comes with a few issues. The first is one you will encounter regardless of what you’re using SphereCast for: when the SphereCast contacts the edge of a collider (rather than a face directly on) the hit.normal that is returned is the interpolation of the two normals of the faces that are joined to that edge. You can think of it similar to the Vector3.Lerp method, with the normals of the two faces being your from and to values. The t value would then be represented by how far the contacted point was from the center of the sphere that contacted the surface; if it hit dead center, the from and to would be equally weighted with a t value of 0.5, and as you move along the edge it would increase or decrease.
I discovered fairly early on that it was necessary to know the actual normal of the surface you are standing on, rather than just the interpolated normal. To solve the problem presented by SphereCast’s interpolation, I would follow up the SphereCast with two separate Raycasts aimed at each of the two joining faces which would retrieve for me the correct normals. (In the Super Character Controller’s ProbeGround method, these are called nearHit and farHit, representing the normals of the closest and furthest face from the center of the controller, respectively.)
The next issue with SphereCast is specific to using it for ground detection but highly essential to ensuring proper accuracy. We’ve been assuming that anything the SphereCast collides with is valid ground that our character can stand on (or slide or, in the case of some slopes for certain games). In practice however this is not always true! We can reasonably assume that the physical surfaces of a game world (that the controller collides with) can be divided into ground and walls, with the idea that only the ground surfaces should be detected by ground probing methods (like the SphereCast defined above). The simplest way to partition the world into ground and walls would be to say that any surface with any angle (relative to the Vector3.up of the world) less than 90 degrees is ground, while surfaces angled 90 and above are walls and cannot be detected as ground. Therefore we should ensure that we never mistakenly detect a wall in our ground probing method. Naively it looks like this problem is implicitly solved by the nature of our ground probing: we are casting a SphereCast directly downwards, which means it should (in theory) never contact a 90 degrees wall. However, very often the normals of the walls in a game world will only be near 90 degrees, or somewhere between 85 and 90. We want to treat these 85 degree angle surfaces as walls, meaning that they should be ignored in our SphereCast.
The most obvious solution would be to use Physics.SphereCastAll. It would ideally collide with both the steep wall and the flat ground, and we could iterate through all contact points to decide what we are standing on. Unfortunately, SphereCastAll only picks up a single contact point per object, so if the ground and wall are part of the same mesh collider, this solution will not work.
Trying something simpler, we could just reduce the radius of the SphereCast by a small amount to account for the above error. And for very slight errors, this does solve some problems, but not all. We still need to find a way to reliably retrieve the proper ground when flush up against a steep slope.
[ Note: In the Super Character Controller, the angle of a “steep slope” is defined as the value StandAngle in the SuperCollisionType component attached to each object the controller collides with. ]
To do this, let’s make the assumption that there is some sort of ground beneath us, and that if there was not a steep slope blocking us our SphereCast would have contacted it. To retrieve this ground, we can Raycast down the steep slope our SphereCast hit. This is primarily to verify that there is some sort of ground there, and (more importantly) to retrieve it’s normal.
This tells us what’s below us, but because we used a Raycast, and not a proper SphereCast, we once again run into the problem presented earlier, where the shape of our ground detection does not correctly align with the shape of the bottom of the controller. Given the information we have (the normal of the surface below us), can we somehow transform our Raycast data into an approximation of SphereCast data? Yessir.
When you SphereCast downwards from the bottom of the controller and contact a surface, there exists a relationship between the normal of the surface and the point on the sphere that the contact takes place. When we Raycast down the steep slope have the normal of the proper ground surface, so it’s our job to find the point along the bottom of the controller that a SphereCast would connect with. We also have the planar direction (from top view) towards the point (given by the direction of the slope below us), allowing us to tackle this problem in 2-dimensional space (finding a point on a circle, as opposed to a sphere) and then convert our result to 3-dimensional space.
Since we are attempting to find a point in 2d space, we are looking for two values which we can call x and y. If calculated properly, x and y will describe a point along the position of our circle. Referring to the diagram above as an example, we would be given the normal of the ground surface, and our job would be to calculate the position of the light blue point.
Luckily, this really isn’t all that difficult. Referring back to grade 8 math, we can use the Sine and Cosine functions to calculate the x and y positions, with the normal of the surface being the angle we pass in.
x = Mathf.Sin(groundAngle); y = Mathf.Cos(groundAngle);
Pretty handy. Note that in Unity the Sine and Cosine methods require you to pass in the angle in radians, so ensure you convert your angles beforehand.
We can now use these values to find our point by adding them to the position of our controller (multiplied by it’s radius). Effectively, we’ve now converted our Raycast data in SphereCast data, and nobody is the wiser.
[ Note: In the Super Character Controller, the method for approximating SphereCast data based off a surface normal is called SimulateSphereCast. ]
This sums up the current technique I am using for ground detection (in the Beta controller). Unlike previous articles, the question of finding a solution to the problem of detecting ground is fairly open—the above is by no means the optimal solution, although I’ve found it works very well in practice. I plan to write a follow-up to this showing how to actual use the data we’ve worked so hard to retrieve, since it’s a somewhat involved process.
I’ve noticed when talking to various Unity users (and browsing the forums) there seems to be a bit of confusion about the purpose of the Lerp methods, and how and when to use them. This isn’t too surprising given that the documentation on Lerp is bizarre (it uses Time.time as a t value, something that by the end of this tutorial you’d know is somewhat odd). I’ve also supplied a Unity webplayer below to visualize how Lerp works.
Lerp stands for Linear Interpolation. Unity has a family of Lerp methods that all function pretty much the same way but have different applications: Mathf.Lerp, Vector3.Lerp, Color.Lerp, and Quaternion.Lerp.
Lerp essentially lets you pick two values, a from and to value, and then select a t value that returns an interpolation between the from and to. The t value is clamped between 0 and 1 (regardless of what you put in the method as a parameter) so you can think of it like a percentage; 0.4 is 40%, 0.6 is 60%, and so on. This leads to the idea of thinking that when you run Lerp on a from and to values, you will get a value t percent between the two. Lets look at some examples.
Mathf.Lerp(0, 100, 0.5f)
This is trivial. We can read this as asking Lerp to give us the value 50% between 0 and 100. This will return 50.
Mathf.Lerp(50, 80, 0.3f)
This will return the value 30% between 50 and 80, which is 59. To make Lerp’s inner workings less opaque, you can actually verify these values using simple arithmetic. You really just need to get the range between the two values, multiply that by the t value and then add that to the from value. For the example above you would do.
range : 80 - 50 = 30 distance by t value : 30 * 0.3 = 9 interpolated value : 50 + 9 = 59
(The above is in pseudocode and not C#)
As stated above, the other Lerp methods work in pretty much the same way. Vector3.Lerp has you select two Vector3s for the from and to values, Color.Lerp uses Colors (duh), Quaternion.Lerp, rotations.
You don’t always have the luxury of having the t value easily supplied for you. Often when using Lerp you are attempting to relate two different values to each other. An example of this would be simulating camera shake when a player is nearby a cannon firing: the further the player is away from the cannon, the less the camera shakes. Therefore there is a clear relation between the distance the player is from the cannon and the magnitude of the camera shake. (This example is based on the cannons from the Mario 64 HD project I built, specifically the FiringCannon class.)
Unity supplies a method named InverseLerp. InverseLerp takes a from and to parameter, and then a value parameter which should be somewhere between from and to. I’ll do an example before we try it out on the camera shake problem stated above.
Mathf.InverseLerp(30, 60, 45);
We can read this as “how far is 45 between 30 and 60?” This will return 0.5, as 45 is halfway between 30 and 60.
For our cannon, lets assume we have some CameraShake(float magnitude) method that we will use. We will state that the maximum shake magnitude is 20, and the minimum is 0. We will also say that the maximum distance the player can be from the cannon and still feel the shake is 50, and the minimum 0. Let’s solve this.
float t = Mathf.InverseLerp(0, 50, Vector3.Distance(cannon.position, player.position));
Where cannon and player are Transforms. This will give us a t value that is 0 when the player is immediately beside the cannon, and 1 when the player’s distance is 50 or greater. We now need to get the proper shake magnitude.
Mathf.Lerp(20, 0, t);
You’ll notice that the from value is larger than the to value. This perfectly acceptable and is done because we want the shake magnitude to be a maximum when the player’s distance is 0 (and the t value is 0) and at a minimum when the player’s distance is 50 or greater (and the t value is 1). If for whatever reason you need the from value to be the smaller of the two, you can always invert t by subtracting it from 1.
I’ve added a demo below showing Lerping between two values (Mathf.Lerp), positions (Vector3.Lerp) and colors (Color.Lerp), with the t value controlled by the slider. Hopefully this will clear up how Lerp works and has demonstrated how useful a method it is.
Due to requests from Nintendo, this project is no longer available. Head over to the Mario 64 HD page to read more. The original blog post is unaltered below. Below is a video of the mod being featured on cobanermani456‘s channel.
Given the amount of time this took to build, I’m going to do away with the usual preamble typically accompanying these posts (or is it too late already?). For the time being, I’m finished working on both the Super Character Controller as well as the demo for it, Mario 64’s Bob-Omb Battlefield. Since the Super Character Controller is (was?) the primary focus of this project, I’m planning to post a write-up and retrospective on it, including what could be done to improve it in the future. Both projects include a PDF fully documenting them. In addition to the using the Super Character Controller library (written by me, but adapted from code by fholm), Mario 64 HD also uses a couple other libraries from the community. All references to these (as well as sources for any art and sound assets I did not make myself) can be found on the downloads page, but since some people may never make the pilgrimage there from this post I figured it would be nice to include them here. Pixelplacement’s iTween was used to help animate the rolling metal balls as they path around the mountain. Since Unity doesn’t expose the InputManager to be modified at runtime (another one of it’s lovable quirks), a heavily modified version of cInput v1.4 was used. Past version 2 cInput is no longer free, but since it looks like the author has been working on it for awhile I suspect there are tremendous improvements from 1.4, which wasn’t all that difficult to integrate anyways. Probably worth the $30 on the asset store!
Most of the art assets that were not constructed by me are from Mario Galaxy, but I did all the animations for them. Sound assets likewise were primarily ripped from different Mario games, or acquired from freesound.org, a very useful (and free, in case you didn’t pick up on that from the name) online sound library. If anyone sees (or hears!) any assets in the project that they themselves made, please contact me so I can give the proper credit. I imported the project to Unity 5 and fixed all the errors that cropped up, but none of this project was developed using any of the new tools available, like Global Illumination or Render To Texture. For anyone wondering what tools I used, they were as follows: Unity (duh), Photoshop, 3ds Max, and Adobe Audition. I used Adobe Premier and Fraps to make the trailer above, as well as the capture videos from the original Mario 64 to use as reference for animations. I used the N64 emulator Project64 for this, which I also used to capture sounds directly from the game. All of the mesh files are currently in the .max format, so while you will be able to open the project without issue, unless you have 3ds Max you’ll be unable to edit the files.
I probably am not going to expand on this project any further, since it would be insanely time consuming to continue any further, and my primary motivation to build this was to drive development on the Super Character Controller and provide a demo project for it. That said, if anyone wants to continue working on this, feel free! The project is open source and can be used for anything you like, short of selling it. If anyone is interested in playing the original (and superior) version of Mario 64, it’s available on the Nintendo 64 as well as the Wii’s Virtual Console.