Advertisement

Best way to implement wall jump?

Started by July 14, 2017 03:11 PM
6 comments, last by cmac 7 years, 4 months ago

I'm trying to implement a wall jump but I don't know what the best way to go about doing it is. Specifically, I don't know how to deal with the collision between the player and wall.

Obviously I want the game to know that what the player just collided with was a wall which is at the correct angle to allow a wall jump; A wall angled at a 75 degree angle naturally wouldn't be acceptable for wall jumping. So my question is, what would be the best way to calculate this collision (in Unity)? Would I get the normal vector of the wall to use for a specific range, and if so, how? Or is there another, better way to do it?

 

Thanks, all help appreciated!

One way -- perhaps not the best way for your game, I don't know -- is this:

Create a new layer and a zero-friction physics material with that layer mask. Apply this as a physics collider to all your walls that allow a wall jump.

In your character controller, if the person jumps you should already be doing a test to see if they are touching the ground. In this case add a second test: if they are not on the ground then see if they are within a radius of the physics object. That's either Physics2D.OverlapCircle() or Physics.OverlapSphere(), and pass in the layer mask used for your walls. That will tell you if you are close enough.

You can do more advanced checks if you want to loop through the results. OverlapSphere() returns all the colliders and you can do your more advanced tests with them if you'd like.

 

Advertisement

I think you're on the right track. You'll have the normal of the surface in OnCollisionEnter(collision) in collision.contacts, in contact.normal. Just use the dot product to determine if this surface isn't right for a wall jump. https://en.wikipedia.org/wiki/Dot_product

If 90 to 75 degrees is your range of acceptable walls for jumping than any wall that satisfies the correct conditions will have cos(75 degrees) => Vector3.Dot(<surface normal>, <up vector>).

Responding to a collision may not give the desired results. It potentially gives an extremely small window, perhaps even a single mid-frame update (since those should be done in the fixed update of physics rather than the randomly-paced graphical update time). 

By running it on a sphere of arbitrary size that you describe using Physics.OverlapSphere(), the developer or designer can fine-tune the distance wall-jumping needs. 

5 hours ago, samoan62 said:

I think you're on the right track. You'll have the normal of the surface in OnCollisionEnter(collision) in collision.contacts, in contact.normal. Just use the dot product to determine if this surface isn't right for a wall jump. https://en.wikipedia.org/wiki/Dot_product

If 90 to 75 degrees is your range of acceptable walls for jumping than any wall that satisfies the correct conditions will have cos(75 degrees) => Vector3.Dot(<surface normal>, <up vector>).

Thanks. 

About the last bit- what does the cosine (and sin/tan) produce? And how do you know, in Unity, which one to  use?

On 7/14/2017 at 4:29 PM, frob said:

By running it on a sphere of arbitrary size that you describe using Physics.OverlapSphere(), the developer or designer can fine-tune the distance wall-jumping needs. 

I suppose you're right that OnCollissionEnter being called outside of FixedUpdate() can lead to awkward race condition scenarios. I never really thought of that. I use OnCollissionEnter because OverlapSphere only returns Collider[] and it's very weird to try and get things like distance between the two objects or normal of the closest surface. I have yet to find a good way of doing this.

 

20 hours ago, Manyula said:

About the last bit- what does the cosine (and sin/tan) produce? And how do you know, in Unity, which one to  use?

If you look into the dot product, the most meaningful thing to take away is that the dot product of two vectors (normalized vectors in this case) is equal to the cosine of the angle between them. cos(75 degrees) is about .25, so your code would really say:

if(0.25f => Mathf.Abs(Vector3.Dot(<surface normal>, <up vector>))).....

If you notice that that number (.25) is too lenient you can always decrease it a little or increase it if you'd like more horizontal walls to be valid.

Advertisement

This thread contains everything you need to implement wall jumps, but since I know you're going for a sm64 style this might simplify the implementation a bit.

In sm64, you can only wall jump off a surface if its surface normal is parallel to the player's floor plane. We talked a bit about rotating the gameplay plane, but assuming youre sticking with the world x/z plane, you can assume that all walljump-able surfaces have a surface normal whose y-component is 0.

To get the surface normal of the surface you're hitting, just raycast from the player's position along its forward vector. The raycasthit object will contain the struck surface's normal.

If you use another method to get the surface normal you'll have to ensure the player is hitting the wall from in front, not beside or behind with dot product. But using a raycast to get the normal can guarantee this if you set it up right. Note however that if you hit the wall too widely in sm64 you can't wall jump off it, so maybe add an angle check between the player forward and the wall's normal and ensure it's within a certain theshold (sm64 feels like it's slightly more than 45 degrees)

SM64 walljumps always have the same horizontal trajectory relative to the surface normal (reflect player forward off the wall's normal - http://www.3dkingdoms.com/weekly/weekly.php?a=2). Their speed is affected by how early in the walljump window the jump button is pressed (first possible frame always goes furthest), and their height by how long you hold the jump button, as is normal for mario air physics.

This topic is closed to new replies.

Advertisement