In this post I will demonstrate a solution to the common problem of slipping off moving platforms in Unity when using RigidBody game objects. The problem can be encountered in both 2D and 3D games, I will cover two known solutions I have come across, and explain in detail the solution I have applied in the game I am working on BloodAndMead.

So what’s the problem?

Lets say you have a player with a RigidBody and BoxCollider attached. You also have a horizontal moving platform which too has a BoxCollider. A RigidBody player (or other object) drops onto the moving platform and expects to be carried along for a ride, but the platform continues to move underneath the object, until it eventually falls / slides off.

Children worldwide cry a river of tears.

faling off moving platforms in unity

There are a few causes of this problem, which will depend on how you have set up your world.

First of all, many people starting with Unity would probably move a platform with a Tweening library like iTween or LeanTween, or translate/ Lerp the x position. Mixing moving rigidbodies and non-rigidbodies together can cause all types of strange issues, as world physics is applied to one and not the other.

There are also dependencies in the way different libraries move objects.

And like most things in Unity, there are a several ways to address a problem.

Here is the result in action taken from Blood And Mead.

unity3D parenting to moving surface

 

So what approach worked for me?

Solution 1: Parent player to platform on collision

I know, sounds crazy right? I thought so anyway, and I wouldn’t have believed it to be a valid solution. But that’s something I have realised with Unity, sometimes the solutions can be a little unorthodox.

So basically, in your Player script, you want to store a reference to the moving platform instance when the player  and parent the player to it.

 

void OnCollisionEnter2D(Collision2D coll)
{
	if (coll.gameObject.tag == "MovingSurfaceCollider")
	{
		m_currMovingPlatform = coll.gameObject.transform;
		transform.SetParent(m_currMovingPlatform);
	}
}

The idea is that the player will jump onto the platform and become parented to it.

That’s great, but now we have the following to consider:

How do we limit player to only parent when the feet collide?

This diagram illustrates how I set up my moving platforms:

moving platform in unity3D

When the player jumps up below the platform and bumps their head on the white collider nothing unusual should happen, the player should get knocked back. The same goes for jumping sideways into a platform.

The short black rectangle illustrated in the diagram is the collider that the player will get parented to.

OK,  so how do we get off the platform?

There are a few conditions that should unlink the player from the platform: When the player’s walk/run input is triggered, when the player jumps, and when the player exits the collider (pushed off perhaps).

In your player script, find where you make the player move move and jump and add the following:

transform.parent = null;

And then when the player exits the collider, nullify the value of the property holding the collider.

 void OnCollisionExit2D(Collision2D coll)
 {
    if (coll.gameObject.tag == "MovingSurfaceCollider")
    m_currMovingPlatform = null;
 }

As mentioned, there are always several ways to solve a problem in Unity, and the solution that worked for me, may not for you for various reasons.

Another solution I explored with some success is the following:

Solution 2: Make moving platforms rigidbodies and use MovePosition

Ideally if you have a moving platform designed for RigidBodies, then it should also be a RigidBody and moved with the Rigidbody.MovePosition API.

Here is how the Unity docs describe MovePosition:

If Rigidbody interpolation is enabled on the Rigidbody, calling Rigidbody.MovePosition results in a smooth transition between the two positions in any intermediate frames rendered. This should be used if you want to continuously move a rigidbody in each FixedUpdate.

So within the FixedUpdate method, a simple implementation it will look like this:

void FixedUpdate() {
        rb.MovePosition(transform.position + transform.forward * Time.deltaTime);
    }

If this post has helped you, consider sharing it so more people can solve this problem.