Gamasutra: The Art & Business of Making Gamesspacer
View All     RSS
November 1, 2014
arrowPress Releases
November 1, 2014
PR Newswire
View All
View All     Submit Event





If you enjoy reading this site, you might also want to check out these UBM Tech sites:


 
The Making of Toto Temple Deluxe: Collisions for Platforming
by Yowan Langlais on 03/04/14 05:12:00 pm   Featured Blogs

The following blog post, unless otherwise noted, was written by a member of Gamasutraís community.
The thoughts and opinions expressed are those of the writer and not Gamasutra or its parent company.

 

ttd_makingof_03

*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.

Collider for each task.

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.

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
        }
    }
}

Collisions need polish.

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!

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!

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();
}

Custom collision handling because unity gives weird infos.

If you did not understand a word said in the previous paragraph, this picture should be of help.

Colliding opinions!

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!

In Conclusion

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!

 

Related Jobs

Twisted Pixel Games
Twisted Pixel Games — Austin, Texas, United States
[10.31.14]

Senior Graphics and Systems Engineer
Twisted Pixel Games
Twisted Pixel Games — Austin, Texas, United States
[10.31.14]

Mid-level Tools and Systems Engineer
Sega Networks Inc.
Sega Networks Inc. — Madison, Wisconsin, United States
[10.31.14]

Mobile Game Engineer
Forio
Forio — San Francisco, California, United States
[10.31.14]

Web Application Developer Team Lead






Comments


Wes Jurica
profile image
I haven't done anything with 2D collisions in Unity but, in making our physics reliant 2.5D racing game, I can understand a lot of the problems encountered here. Workarounds are a necessity.

Alexandre Daze-Hill
profile image
We use 3D collisions, we started development with Unity 4.2 and earlier, so everything you see there is good for 3D programming. I didn't know that collisions were not made for racing either, it's a shame, what is/are the main flaws in PhysX for a racing game?

Charles Zapata
profile image
Very helpful hints - thanks for posting! I'm curious - do you use the colliders as triggers (so you handle all positioning yourself), or as actual physical surfaces? And did you notice any difference (performance or accuracy) if using the Discrete, Continuous and Dynamic Continous collider options?

Alexandre Daze-Hill
profile image
We use physical colliders, they mostly do a great job if it were not for the interpenetration thing. Using triggers would result in way more dev time, more chances of bug and all that, for a similar result if on par with the PhysX collision engine. So we just add a layer of correction over the already established engine. I did not notice any difference between the collider options, I did not try any performance benchmarks, but I tried to see if there were some built-in ways to prevernt interpenetration and these options seemed the kind of things to try, but I did not notice any difference in behaviour. Maybe I just did not use them well and it is why I started this block with "According to my understanding of unity collision/physics engine, PhysX".

Thomas Happ
profile image
I've not used Unity, but I feel uneasy about the 1-frame lag in Unity resolving collisions, like maybe your main game loop is doing things out of order? Does Unity let you control that kind of thing?

Yowan Langlais
profile image
I've tested many cases and ways to organize things before coming to these conclusions, not to say they are the absolute truth or anything, but I searched as much as I could to fix this without having to compensate. If anyone is ever able to tell me I have been wrong all along, I would be more than happy as it would mean that I'll be able to get rid of this module.


none
 
Comment: