179 lines
4.8 KiB
C#
179 lines
4.8 KiB
C#
using UnityEngine;
|
|
|
|
namespace Vanguard.Battle
|
|
{
|
|
/// <summary>
|
|
/// Единственный владелец кинематографичного движения камеры.
|
|
/// Снимок исходного pose берётся один раз на цепочку и
|
|
/// восстанавливается идемпотентно.
|
|
/// </summary>
|
|
public sealed class CameraDirector
|
|
{
|
|
private struct Pose
|
|
{
|
|
public Vector3 Position;
|
|
public Quaternion Rotation;
|
|
public float FieldOfView;
|
|
}
|
|
|
|
private readonly Camera _camera;
|
|
private Pose _origin;
|
|
private Pose _from;
|
|
private Pose _to;
|
|
private float _duration;
|
|
private float _elapsed;
|
|
private bool _moving;
|
|
private bool _hasOrigin;
|
|
|
|
public CameraDirector(Camera camera)
|
|
{
|
|
_camera = camera;
|
|
}
|
|
|
|
public bool IsActive => _hasOrigin && _camera != null;
|
|
public bool IsMoving => _moving;
|
|
|
|
public void BeginChain()
|
|
{
|
|
if (_camera == null || _hasOrigin)
|
|
return;
|
|
|
|
_origin = Capture();
|
|
_hasOrigin = true;
|
|
}
|
|
|
|
public void ZoomTo(
|
|
Vector3 target,
|
|
float duration,
|
|
float fieldOfView = 34f)
|
|
{
|
|
if (_camera == null)
|
|
return;
|
|
|
|
BeginChain();
|
|
var current = Capture();
|
|
var targetPosition = target
|
|
+ (Vector3.back * 3.4f)
|
|
+ (Vector3.up * 2.6f);
|
|
var targetRotation = Quaternion.LookRotation(
|
|
target - targetPosition,
|
|
Vector3.up);
|
|
AnimateTo(
|
|
new Pose
|
|
{
|
|
Position = targetPosition,
|
|
Rotation = targetRotation,
|
|
FieldOfView = fieldOfView,
|
|
},
|
|
duration);
|
|
}
|
|
|
|
public void SetImmediate(Vector3 position, Vector3 target, float fieldOfView)
|
|
{
|
|
if (_camera == null)
|
|
return;
|
|
|
|
_moving = false;
|
|
var rotation = Quaternion.LookRotation(target - position, Vector3.up);
|
|
Apply(new Pose
|
|
{
|
|
Position = position,
|
|
Rotation = rotation,
|
|
FieldOfView = fieldOfView,
|
|
});
|
|
}
|
|
|
|
public void MoveTo(
|
|
Vector3 position,
|
|
Vector3 target,
|
|
float fieldOfView,
|
|
float duration)
|
|
{
|
|
if (_camera == null)
|
|
return;
|
|
|
|
var rotation = Quaternion.LookRotation(target - position, Vector3.up);
|
|
AnimateTo(
|
|
new Pose
|
|
{
|
|
Position = position,
|
|
Rotation = rotation,
|
|
FieldOfView = fieldOfView,
|
|
},
|
|
duration);
|
|
}
|
|
|
|
public void ReturnToOrigin(float duration)
|
|
{
|
|
if (!_hasOrigin)
|
|
return;
|
|
|
|
AnimateTo(_origin, duration);
|
|
}
|
|
|
|
public void Restore()
|
|
{
|
|
if (_camera == null || !_hasOrigin)
|
|
return;
|
|
|
|
_moving = false;
|
|
Apply(_origin);
|
|
_hasOrigin = false;
|
|
}
|
|
|
|
public void Tick(float deltaTime)
|
|
{
|
|
if (!_moving || _camera == null)
|
|
return;
|
|
|
|
_elapsed += deltaTime;
|
|
var t = Mathf.Clamp01(_elapsed / Mathf.Max(0.01f, _duration));
|
|
var eased = t * t * (3f - (2f * t));
|
|
Apply(new Pose
|
|
{
|
|
Position = Vector3.Lerp(_from.Position, _to.Position, eased),
|
|
Rotation = Quaternion.Slerp(
|
|
_from.Rotation,
|
|
_to.Rotation,
|
|
eased),
|
|
FieldOfView = Mathf.Lerp(
|
|
_from.FieldOfView,
|
|
_to.FieldOfView,
|
|
eased),
|
|
});
|
|
|
|
if (t >= 1f)
|
|
_moving = false;
|
|
}
|
|
|
|
private void AnimateTo(Pose target, float duration)
|
|
{
|
|
if (_camera == null)
|
|
return;
|
|
|
|
_from = Capture();
|
|
_to = target;
|
|
_duration = Mathf.Max(0.01f, duration);
|
|
_elapsed = 0f;
|
|
_moving = true;
|
|
}
|
|
|
|
private Pose Capture() => new Pose
|
|
{
|
|
Position = _camera.transform.position,
|
|
Rotation = _camera.transform.rotation,
|
|
FieldOfView = _camera.fieldOfView,
|
|
};
|
|
|
|
private void Apply(Pose pose)
|
|
{
|
|
if (_camera == null)
|
|
return;
|
|
|
|
_camera.transform.position = pose.Position;
|
|
_camera.transform.rotation = pose.Rotation;
|
|
_camera.fieldOfView = pose.FieldOfView;
|
|
}
|
|
}
|
|
}
|