The MovingObject2D
component is a very simple system for moving platforms (or any object) back and forth between two points. Its main purpose is just to demonstrate the way the Character Physics interacts with moving platforms, but in a real game you would likely want to find or make a more powerful system with features like following multiple points in a path, curved paths, and variable speeds.
The script is set to update very early so that it moves before other objects do anything based on its position or velocity:
[DefaultExecutionOrder(DefaultExecutionOrder)]
public sealed class MovingObject2D : MonoBehaviour
{
public const int DefaultExecutionOrder = -10000;
The fields have tooltips explaining what they do, Units Attributes where appropriate, and an OnValidate
method to automatically find the _Rigidbody
if it's missing and prevent the _Speed
from being set to a negative value:
[SerializeField]
[Tooltip("The object to move")]
private Rigidbody2D _Rigidbody;
[SerializeField]
[Tooltip("The destination to move to (relative to the starting position)")]
private Vector2 _Movement;
[SerializeField, MetersPerSecond]
[Tooltip("The speed at which the object moves")]
private float _Speed = 3;
[SerializeField]
[Tooltip("Should it keep moving back and forth between the start and end?")]
private bool _Loop = true;
#if UNITY_EDITOR
private void OnValidate()
{
gameObject.GetComponentInParentOrChildren(ref _Rigidbody);
PlatformerUtilities.NotNegative(ref _Speed);
}
#endif
On startup, it stores its current position as the start point:
private Vector2 _StartingPosition;
private void Awake()
{
_StartingPosition = _Rigidbody.position;
}
In FixedUpdate
it calculates its destination
based on the _StartingPosition
and adds the _Movement
if it is currently _Return
ing:
private bool _Return;
private void FixedUpdate()
{
var movement = _Speed * Time.deltaTime;
var startingPosition = _Rigidbody.position;
var position = startingPosition;
Move:
var destination = _StartingPosition;
if (!_Return)
destination += _Movement;
Then it calculates how far it has to go to reach that destination
:
var offset = destination - position;
var distance = offset.magnitude;
If the remaining distance
is still larger than the movement
this frame, add it to the current position:
if (distance > movement)// Still going.
{
position += offset * movement / distance;
_Rigidbody.velocity = (position - startingPosition) / Time.deltaTime;
_Rigidbody.MovePosition(position);
}
Otherwise, it will reach the destination
this frame so if _Loop
is enabled, subtract the remaining distance
from the amount left to move this frame then go back up to the Move:
label near the start of the method to continue the rest of this frame's movement back the other way:
else if (_Loop)
{
position = destination;
movement -= distance;
_Return = !_Return;
goto Move;
}
Otherwise just snap to the destination
and stop moving:
else
{
_Rigidbody.velocity = default;
_Rigidbody.MovePosition(destination);
enabled = false;
return;
}
Gizmos
The script also has an OnDrawGizmos
method which draws some lines in the Scene view to help visualise where the _Movement
will actually end up.
#if UNITY_EDITOR
private void OnDrawGizmos()
{
var collider = GetComponent<Collider2D>();
if (collider == null)
{
var start = UnityEditor.EditorApplication.isPlaying ? (Vector3)_StartingPosition : transform.position;
Gizmos.DrawLine(start, start + (Vector3)_Movement);
return;
}
var bounds = collider.bounds;
var center = UnityEditor.EditorApplication.isPlaying ? (Vector3)_StartingPosition : bounds.center;
var extents = bounds.extents;
var corner = extents;
Gizmos.DrawLine(center + corner, center + corner + (Vector3)_Movement);
corner.y = -corner.y;
Gizmos.DrawLine(center + corner, center + corner + (Vector3)_Movement);
corner.x = -corner.x;
Gizmos.DrawLine(center + corner, center + corner + (Vector3)_Movement);
corner.y = -corner.y;
Gizmos.DrawLine(center + corner, center + corner + (Vector3)_Movement);
}
#endif
}