I've only ever seen this solved with the animation being controlled by a script - This can be done in the Update() method or via a tweening plugin like DoTween.
In the Update method you have a few options, including manual movement (manually manipulating transform.position
's x, y and z parameters), using transform.position = Vector3.MoveTowards(transform.position, _movementTarget, MaxSpeed * Time.DeltaTime);
and transform.position = Vector3.Lerp(_movementStart, _movementTarget, _animationState)
where _animationState
goes from 0 to 1 to indicate how far through the animation you are. The choice between MoveTowards
and Lerp
comes down to whether you care more to set the movement speed, or the time it'll take to get to the target.
For rotation, you can also use Transform.LookAt()
, Quaternion.Lerp()
and Quaterion.LookRotation()
however I tend to compute this manually to absolutely ensure the Z rotation is never changed.
In my solution I've gone with the Lerp
approach for position, and am manually animating rotation by only lerping the y axis, you could also separately lerp the x rotation axis, that'd be fine, just make sure to never change the z axis.
using UnityEngine;
namespace Assets
{
public class MoveMe : MonoBehaviour
{
private bool _isMoving = false;
private Vector3 _movementStart;
private Vector3 _movementTarget;
private float _rotationStartEulerY;
private float _rotationTargetEulerY;
private const float AnimationSpeedFactor = 2f;
private float _animationProgress = 0;
private void Update()
{
if (_isMoving)
{
_animationProgress += Time.deltaTime * AnimationSpeedFactor; //Animation progress 0..1
transform.position =
Vector3.Lerp(_movementStart, _movementTarget, _animationProgress); //Animate position
var rot = transform.rotation.eulerAngles;
transform.rotation = Quaternion.Euler(rot.x,
Mathf.Lerp(_rotationStartEulerY, _rotationTargetEulerY, _animationProgress),
rot.z); //Animate rotation
if (_animationProgress >= 1)
{
_isMoving = false; //End animation, player free to move camera manually
}
}
if (Input.GetKeyDown(KeyCode.A)) StartAnimationTowards(new Vector3(0, 1, -2.96f), -51.145f);
if (Input.GetKeyDown(KeyCode.S)) StartAnimationTowards(new Vector3(-2.88f, 1, -3.52f), 39.054f);
if (Input.GetKeyDown(KeyCode.D)) StartAnimationTowards(new Vector3(-8.97f, 3.97f, 3.2f), 103.39f);
}
private void StartAnimationTowards(Vector3 position, float rotation)
{
_isMoving = true;
_movementStart = transform.position;
_movementTarget = position;
_rotationStartEulerY = ClampRotation(transform.rotation.eulerAngles.y);
_rotationTargetEulerY = ClampRotation(rotation);
_animationProgress = 0;
}
private float ClampRotation(float rotation)
{
//Ensures the returned value is between -180 .. 180. This ensures rotation doesn't animate the long way around the circle.
rotation %= 360;
return (rotation > 180)
? rotation - 360
: rotation;
}
}
}