La gestion de la rotation dans Unity peut s'avérer complexe, notamment en raison des complications liées à l'interruption des animations lors de l'utilisation de solutions tierces comme DOTween. Une approche plus fiable consiste à implémenter une interpolation directement dans la méthode Update. Voici une série de méthodes d'extension pour la classe Transform, conçues pour simplifier et fiabiliser les opérations de déplacement et de rotatino.
Exemple d'utilisation
using UnityEngine;
public class EnemyBehavior : MonoBehaviour
{
[SerializeField] private Transform destination;
[SerializeField] private float velocity = 5f;
[SerializeField] private float angularSpeed = 180f;
private void Update()
{
// Déplacement vers une cible
bool arrived = transform.GotoTarget(destination, velocity, Time.deltaTime);
if (arrived)
{
Debug.Log("Point d'arrivée atteint.");
}
// Orientation vers la cible
bool oriented = transform.TurnTowards(destination, angularSpeed, Time.deltaTime);
// Rotation vers des angles eulériens définis
transform.SpinToEulerAngles(new Vector3(0, 90, 0), angularSpeed, Time.deltaTime);
// Déplacement vers un point précis
Vector3 point = new Vector3(10, 0, 5);
transform.GotoPoint(point, velocity, Time.deltaTime);
// Retour au point d'origine sur le plan XZ local
transform.GotoLocalOriginXZ(velocity, Time.deltaTime);
}
}
Implémentasion des méthodes
using UnityEngine;
public static class MovementAndRotationUtils
{
/// <summary>
/// Déplace de manière fluide un Transform vers la position d'un autre Transform.
/// </summary>
public static bool GotoTarget(this Transform self, Transform target,
float speed, float deltaTime, bool freezeY = true)
{
if (target == null)
{
Debug.LogError("La cible spécifiée est nulle.");
return false;
}
Vector3 selfPosition = self.position;
Vector3 targetPosition = target.position;
float currentDist = Vector3.Distance(selfPosition, targetPosition);
if (currentDist <= 0.01f)
{
self.position = targetPosition;
return true;
}
Vector3 updatedPos = Vector3.MoveTowards(selfPosition, targetPosition, speed * deltaTime);
if (freezeY)
{
updatedPos.y = self.position.y;
}
self.position = updatedPos;
float remainingDist = Vector3.Distance(updatedPos, targetPosition);
return remainingDist < 0.1f;
}
/// <summary>
/// Ramène progressivement l'objet à l'origine locale sur les axes X et Z.
/// </summary>
public static bool GotoLocalOriginXZ(this Transform self, float speed, float deltaTime)
{
Vector3 local = self.localPosition;
if (Mathf.Abs(local.x) < 0.01f && Mathf.Abs(local.z) < 0.01f)
{
self.localPosition = new Vector3(0f, local.y, 0f);
return true;
}
Vector3 originTarget = new Vector3(0f, local.y, 0f);
Vector3 moved = Vector3.MoveTowards(local, originTarget, speed * deltaTime);
self.localPosition = moved;
float offset = Vector2.Distance(new Vector2(moved.x, moved.z), Vector2.zero);
return offset < 0.01f;
}
/// <summary>
/// Effectue une rotation progressive vers une orientation définie par des angles eulériens.
/// </summary>
public static bool SpinToEulerAngles(this Transform self, Vector3 eulerAngles,
float degPerSec, float deltaTime)
{
Quaternion currentRot = self.rotation;
Quaternion desiredRot = Quaternion.Euler(eulerAngles);
if (Quaternion.Angle(currentRot, desiredRot) < 0.1f)
{
self.rotation = desiredRot;
return true;
}
Quaternion smoothedRot = Quaternion.RotateTowards(currentRot, desiredRot, degPerSec * deltaTime);
self.rotation = smoothedRot;
return Quaternion.Angle(smoothedRot, desiredRot) < 1f;
}
/// <summary>
/// Tourne de façon progressive vers la position d'une cible.
/// </summary>
public static bool TurnTowards(this Transform self, Transform target,
float turnSpeed, float deltaTime,
Vector3 rotationOffset = default, bool horizontalOnly = true)
{
if (self == null || target == null)
{
return false;
}
Vector3 toTarget = target.position - self.position;
if (horizontalOnly)
{
toTarget.y = 0f;
if (toTarget.sqrMagnitude < 0.0001f)
{
return true;
}
}
Quaternion finalRotation = Quaternion.LookRotation(toTarget.normalized, Vector3.up);
if (rotationOffset != Vector3.zero)
{
finalRotation *= Quaternion.Euler(rotationOffset);
}
Quaternion current = self.rotation;
float initialAngle = Quaternion.Angle(current, finalRotation);
if (initialAngle <= 0.1f)
{
self.rotation = finalRotation;
return true;
}
self.rotation = Quaternion.RotateTowards(current, finalRotation, turnSpeed * deltaTime);
float resultingAngle = Quaternion.Angle(self.rotation, finalRotation);
return resultingAngle < 1f;
}
/// <summary>
/// Déplace progressivement un Transform vers une position donnée.
/// </summary>
public static bool GotoPoint(this Transform self, Vector3 point,
float speed, float deltaTime, bool lockY = true)
{
Vector3 origin = self.position;
float distance = Vector3.Distance(origin, point);
if (distance < 0.01f)
{
self.position = point;
return true;
}
Vector3 interpolated = Vector3.MoveTowards(origin, point, speed * deltaTime);
if (lockY)
{
interpolated.y = self.position.y;
}
self.position = interpolated;
float newDist = Vector3.Distance(interpolated, point);
return newDist < 0.1f;
}
}