In Unity, the static method MoveTowards of the Vector3 structure allows us to smoothly move our GameObject to a target position by specifying a maximum distance to cover per frame.

Table of contents

MoveTowards Basic Usage

MoveTowards returns a Vector3 and has three parameters: a starting position (usually the position of your GameObject), a target position to reach, and finally, a maximum distance to cover.

The returned Vector3 takes the starting position and approaches the target position within the specified maximum distance.

Example with 2 positions:

var position = Vector3.MoveTowards(Vector3.zero, new Vector3(0f, 5f, 0f), 1f); 
// position = new Vector3(0f, 1f, 0f)

How do you make an object go to another object?

MoveTowards is designed for this task; we just need to implement it in an Update function:

using UnityEngine;

public class MoveCharacter : MonoBehaviour
{
    [SerializeField]
    private GameObject _targetGameObject;

    private float _speed = 3f;

    private void Update()
    {
        transform.position = Vector3.MoveTowards(transform.position, _targetGameObject.transform.position, Time.deltaTime * _speed);

        if (Vector3.Distance(transform.position, _targetGameObject.transform.position) < 0.001f)
        {
            // Our GameObject reached the target
        }
    }
}

Note: This example can be improved by directly referencing the Transform of our target.

Time.deltaTime allows us to synchronize regardless of our fps:

Unity MovetoWards with Time.deltaTime

Acceleration in Unity

To achieve frame-dependent acceleration, we need to multiply the speed to be added before and after MoveTowards by dividing it by 2:

using UnityEngine;

public class MoveCharacter : MonoBehaviour
{
    [SerializeField]
    private GameObject _targetGameObject;

    private float _speed = 3f;

    private void Update()
    {
        float incrementalSpeed = 0.1f;

        _speed += incrementalSpeed * Time.deltaTime * 0.5f;
        transform.position = Vector3.MoveTowards(transform.position, _targetGameObject.transform.position, Time.deltaTime * _speed);
        _speed += incrementalSpeed * Time.deltaTime * 0.5f;
    }
}

For more details on accelerations and Time.deltaTime in general, I recommend this video:

Youtube – Jonas Tyroller – Dear Game Developers, Stop Messing This Up!

MoveTowards replace speed by duration

To accomplish a move over a defined duration, although MoveTowards is a feasible option, I recommend using Lerp instead, as indicated immediately after this approach.
Rather than entering a distance, we opt to insert a velocity per frame, following these steps:

  1. First, we evaluate the distance between the two points using Vector3.distance.
  2. This allows us to calculate the distance covered per second: “distance / duration“.
  3. Finally, this distance is multiplied by Time.deltaTime to obtain a velocity per frame.
using System.Collections;
using System.Threading.Tasks;
using UnityEngine;

public class MoveByDuration : MonoBehaviour
{
    [SerializeField]
    private Vector3 _position;

    private void Start()
    {
        StartCoroutine(MovetowardsDuration(3f));

        // if you prefer async
        // MoveAsync(3f);
    }

    private IEnumerator MovetowardsDuration(float duration)
    {
        float distance = Vector3.Distance(transform.position, _position);
        float distanceBySeconds = distance / duration;

        float startTime = 0;

        while (startTime < duration)
        {
            transform.position = Vector3.MoveTowards(transform.position, _position, distanceBySeconds * Time.deltaTime);
            startTime += Time.deltaTime;
            yield return null;
        }
    }

    // async
    private async void MoveAsync(float duration)
    {
        float distance = Vector3.Distance(transform.position, _position);
        float distanceBySeconds = distance / duration;

        float startTime = 0;

        while (startTime < duration)
        {
            transform.position = Vector3.MoveTowards(transform.position, _position, distanceBySeconds * Time.deltaTime);
            startTime += Time.deltaTime;
            await Task.Yield();
        }
    }
}

In this context, I prefer to use the Lerp method. The linear interpolation it performs allows us to directly obtain our position based on the elapsed time.

To do this, we only need to increment a time counter with Time.deltaTime in each frame and then divide this counter by the duration to get a time value between 0 and 1.

using System.Collections;
using System.Threading.Tasks;
using UnityEngine;

public class LerpTest : MonoBehaviour
{
    [SerializeField]
    private Vector3 _positionTarget;

    private void Start()
    {
        StartCoroutine(Lerp(3f));
    }

    private IEnumerator Lerp(float duration)
    {
        float start = 0;
        var startPosition = transform.position;

        while (start < duration)
        {
            transform.position = Vector3.Lerp(startPosition, _positionTarget, start / duration);
            start += Time.deltaTime;
            yield return null;
        }
    }

    // If you prefer async
    private async void LerpAsync(float duration)
    {
        float start = 0;
        var startPosition = transform.position;

        while (start < duration)
        {
            transform.position = Vector3.Lerp(startPosition, _positionTarget, start / duration);
            start += Time.deltaTime;
            await Task.Yield();
        }
    }
}

This alternative provides a ready-made transition to the next topic:

What is the difference between LERP and MoveTowards?

  • Lerp interpolates linearly between two points (returns an intermediate point according to a coefficient t ranging from 0 to 1) used to achieve movement with increasing deceleration.
  • MoveTowards moves a point toward another target point, taking into account a maximum distance, allowing smooth effects in movements.

Additionally, MoveTowards is mainly used for movements, while Lerp is used in all aspects of gameplay, allowing transitions, direction changes, and more.

To conclude Unity’s MoveTowards method provides a versatile tool for achieving smooth object movements with specified speed limits, while understanding the nuances between Lerp and MoveTowards enhances developers’ control over transitions and gameplay dynamics in Unity.

Leave a Reply

Your email address will not be published. Required fields are marked *