Address
304 North Cardinal St.
Dorchester Center, MA 02124
Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM
We use cookies to ensure that we provide you with the best possible experience on our site.
Your essential source on Unity and web development
Your essential source on Unity and web development
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 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)
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:
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!
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:
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:
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.