Trending
Opinion: How will Project 2025 impact game developers?
The Heritage Foundation's manifesto for the possible next administration could do great harm to many, including large portions of the game development community.
Featured Blog | This community-written post highlights the best of what the game industry has to offer. Read more like it on the Game Developer Blogs or learn how to Submit Your Own Blog Post
Collision/Physics engine in unity have been a blessing, but it has it’s fair share of flaws when trying to adapt it to a fast paced platformer, now I will talk about these and the workarounds to get near perfect collision detection.
*This blog post has been written by Alex, our lead programmer. You can read his original post on our website.
You can follow up with all the posts from the series :
The Making of Toto Temple Deluxe: Platforming (Part 1)
The Making of Toto Temple Deluxe: Platforming (Part 2)
The Making of Toto Temple Deluxe: Collisions for Platforming
The Making of Toto Temple Deluxe: The Unique Experience
Collision/Physics engine in unity have been a blessing, but it has it’s fair share of flaws when trying to adapt it to a fast paced platformer, now we will talk about these and the workarounds to get near perfect collision detection.
The character must have several collision zones, each has its use: one attack hit box, one weak hit box (to receive the attacks) and one physical collider. This gives the dev much needed control over how threats/collectibles/events/enhancements/enemy weak points interact with the main characters.
Usually, we like to make the main characters weak collision smaller than his physical collision, so there are more “holy fetuccini, that was close!” moments and less “But I wasn’t even touching it!” moments.
Inversely, we like to make the main characters attack collisions a little bigger, so if there is visual ambiguity, the game will cooperate with the will of the player.
Here is a toto at rest, let’s study its anatomy.
In Unity, there can be only one collider per entity, so we must put the other colliders inside the character, and reference the character to those colliders through a simple home-made mediator system.
void Awake () {//when the object initializes _master = (ICollidable)master;//change the master in parameter to a usable ICollidable master } void OnTriggerEnter(Collider other) {//When there is a (not physicial) collision... if (receiveSignal) {//if the container is made to receive the signal... ColliderContainer otherCol = other.GetComponent<ColliderContainer>();//find the other container if (otherCol == null) {//if there is no container... _master.enterCollision(other.gameObject, LayerMask.LayerToName(other.gameObject.layer), other.bounds);//send the message of the collision to my master, referencing the gameObject that hit me } else if (otherCol.master!= master) {//if there is a container and it has not the same master _master.enterCollision(otherCol.master.gameObject, LayerMask.LayerToName(other.gameObject.layer), other.bounds);//send the message of the collision to my master, referencing the master of the gameObject that hit me } } }
According to my understanding of unity collision/physics engine, PhysX, my characters overlap with the level colliders for a frame before beign replaced at the correct place, this leads to a really cheap feeling and some errors in the interpretation of collisions at high speeds.
Interpenetration (overlap) is a plague we must act against!
This leads me to make a function called “PreventCheapInterpenetrating” which is executed after the movement of the character is done and before any physical calculation, that checks if the player will touch something over it’s course in the next frame, if yes, it reduces the character’s velocity so it just gently touches the surface instead of ramming into it, this way, physics calculations to determine the position of the character afterward are more accurate and the brief overlap is not visible anymore.
Slow down before your collision, little toto!
Now that we prepare for impact, we must properly analyse the impact; do I touch a ground, a wall, a ceiling, what angle is it? Sadly, the native collision interpretation can be strange sometimes, giving contact points that are out of both the receiving and the colliding collider, with surface informations that are impossible. For example, if I land on the ground, the collision points could be higher than the ground, way off at the right, and the angle of the collision would be in diagonal.
I cannot use the collision points, happily there is a function called ClosestPointOnBounds, that returns the bounds of a collision (extreme values forming a box around the collider, not the actual closest point in the collider itself). I need to know without a doubt the nature and angle of the surface I hit, how do I do it? I know that all my characters will have square physical colliders and won’t ever rotate, so I can use character.ClosestPointOnBounds(collider.center). Then cast a ray between that point and the center of the collider, with regular shapes it should always give the good surface you hit.
Here is the code needed to do so:
protected Vector3 GetNormalOfHit(Collider collider) {//get the angle of the collision Vector3 rayDestination = collider.bounds.center;//destination of ray (center of collider) Vector3 rayOrigin = col.ClosestPointOnBounds(rayDestination);//origin of ray (point on the character) Vector3 rayDiff = rayDestination - rayOrigin;//vector describing the difference between the 2 previous vectors rayOrigin -= rayDiff.normalized * 5;//inset the origin according to the angle between origin and destination so if there is overlap, the ray will still hit. rayDiff = rayDestination - rayOrigin;//rebuild the difference with new origin ray.origin = rayOrigin;//set the origin of the ray (I‘m reusing rays this is why there is no instantiation.) ray.direction = rayDiff.normalized;//set the direction of the ray RaycastHit hit; if (rayDiff.magnitude > 0) {//make sure there are no errors due to exceptionnal occurences. collider.Raycast(ray, out hit, rayDiff.magnitude + 2f);//cast the ray on the collider (only the collider) return hit.normal;//return the normal, it’ll determine we hit the top/sides or bottom of the object. } return new Vector3(); }
If you did not understand a word said in the previous paragraph, this picture should be of help.
In Rogue Legacy, there are times when you get hit by projectiles that are under a platform when you land on it, because at some point in the code, your downward speed made you pass through the platform before beign placed back to the top of it.
Still in Rogue Legacy, there are relatively rare incidences of neighbooring collision issues, but all in all, it’s a game that kept me going for a long time despite the crudeness of its platforming!
Navigation is important, and it needs some tweaking, but collisions must be perfect in order for navigation to be efficient, so there goes the importance of colliding well! In this post I presented you some ways to overcome some lacks in the collision engine. If you ever want some specific details, don’t be shy! Next time we’ll talk about the design decisions behind the original idea of Toto Temple, so stay tuned!
Read more about:
Featured BlogsYou May Also Like