One of the most common questions I get from students looking to build a platformer is this: how do I build a moving platform in Unity? Well, if you are looking for code that you can just copy-paste into your project, look no further.
What are moving platforms made of?
If you want a moving platform in your game, you will need a GameObject that is able to do 2 things:
- Collide with other objects.
- Cannot be affected by forces, from collision or otherwise.
That means that you will need a GameObject that has a collider attached, as well as a Rigidbody2D
component marked as Kinematic.
Once that is done, you will need to either write a script to move the platform, or create an animation for the platform.
The moving platform scripts
Here, we have 2 scripts that you can import into your project:
KinematicPlatform2D.cs
using UnityEngine; [RequireComponent(typeof(Rigidbody2D),typeof(Collider2D))] public abstract class KinematicPlatform2D:MonoBehaviour { public float cycleRunTime = 3f; // Time it takes to complete 1 cycle of movement. public float cycleWaitTime = 2f; // How long to wait before starting another cycle. protected float currentCycleTime = 0; // For subclasses to check what the current cycle time is. protected float currentWaitTime = 0; // For subclasses to check what the current wait time is. // After a cycle completes, does the time start from 0 again? Or does it go in the opposite direction? public enum CycleType { repeat, pingPong } public CycleType cycleType; protected sbyte cycleDirection = 1; // Only for ping pong mode. protected Rigidbody2D rb; // Rigidbody for child classes, so they don't have to call it again. // We use Awake() here so we don't have to override start in child classes. protected virtual void Awake() { // We require a Rigidbody as this makes collider movement more efficient. // See https://forum.unity.com/threads/non-static-colliders-without-rigidbody-still-cost-in-unity-5.452177/#post-4343569 rb = GetComponent<Rigidbody2D>(); } protected virtual void Reset() { // Force the Rigidbody to kinematic mode when this component is added. GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Kinematic; } // This is protected because it needs to be overriden. protected virtual void Update() { if(currentWaitTime <= 0) { currentCycleTime += Time.deltaTime * cycleDirection; HandleCycleEnd(); } else { currentWaitTime -= Time.deltaTime; HandleCycleRestart(); } } void HandleCycleRestart() { if(currentWaitTime < 0) { currentCycleTime -= currentWaitTime; currentWaitTime = 0; } } // Checks if the cycle has ended. If it has, set the object // to wait mode. Algorithm works if cycleWaitTime is 0. void HandleCycleEnd() { // If the cycle is finished, set the Wait time and the currentCycleTime. // Works if cycleWaitTime is 0. switch(cycleType) { case CycleType.repeat: if(currentCycleTime > cycleRunTime) { // We also subtract the wait time by the time we already exceeded. currentWaitTime = cycleWaitTime - (currentCycleTime - cycleRunTime); currentCycleTime = 0; // The next cycle time to start from. } break; case CycleType.pingPong: // Invert the cycle direction if we exceed the run time. if(currentCycleTime > cycleRunTime) { // We account for if the currentCycleTime exceeds the cycleRunTime. currentWaitTime = cycleWaitTime - (currentCycleTime - cycleRunTime); cycleDirection = -1; // Set the next cycle time to start from. currentCycleTime = cycleRunTime; } else if(currentCycleTime < 0) { currentWaitTime = cycleWaitTime - currentCycleTime; cycleDirection = 1; // Set the next cycle time to start from. currentCycleTime = 0; } break; } } // Parents any Rigidbody when they step on the platform, // so it moves along with the platform. protected virtual void OnCollisionEnter2D(Collision2D collision) { collision.rigidbody.transform.SetParent(transform); } // Unparents any Rigidbody when they leave the platform, // so it stops moving along with the platform. protected virtual void OnCollisionExit2D(Collision2D collision) { collision.rigidbody.transform.SetParent(null); } }
Article continues after the advertisement:
StraightMovingPlatform2D.cs
using UnityEngine; using UnityEditor; public class StraightMovingPlatform2D:KinematicPlatform2D { public Vector2 endPoint = new Vector2(5,0); Vector2 origin; void Start() { origin = transform.position; } // Update is called once per frame protected override void Update() { // Needed since it manages the cycle time. base.Update(); // Since the cycle time is there, we can just use Lerp. transform.position = Vector2.Lerp(origin,origin + endPoint,currentCycleTime / cycleRunTime); } protected override void Reset() { base.Reset(); // Set the endpoint to always be a straight line forward by default. For convenience. endPoint = transform.position + transform.right * 8; // Doesn't make sense for straight moving platforms to repeat. cycleType = CycleType.pingPong; } private void OnDrawGizmosSelected() { Vector2 start = EditorApplication.isPlaying ? origin : (Vector2)transform.position, end = start + endPoint; Gizmos.color = Color.cyan; Gizmos.DrawLine(start,end); Gizmos.DrawWireSphere(start,0.25f); Gizmos.DrawWireSphere(end,0.25f); } }
You can attach the StraightMovingPlatform2D
component onto your moving platform after importing your script.
The KinematicPlatform2D
is an abstract class, and is not meant to be used. It contains code to control how the platform moves, and you can extend it to create other platform components that move in different ways (e.g. circular moving platform).
Configuring your platform
The StraightMovingPlatform2D
script has editor gizmos coded into it to make them easy to use:
You can also change the Cycle Type property to control whether the platform cycles between 2 points:
Or only moves in a single direction:
Using the platform
Whenever any Rigidbody2D
object falls onto the platform, it will automatically move along with the platform.
Conclusion
If there are any bugs or issues with our code, please share them in the comments below!
Article continues after the advertisement: