102 lines
3.3 KiB
C#
102 lines
3.3 KiB
C#
using UnityEngine;
|
|
using DG.Tweening;
|
|
|
|
public class CrateEscapeCameraFollow : MonoBehaviour
|
|
{
|
|
[Header("Targets")]
|
|
public Transform player; // follow target
|
|
public Transform topDownAnchor; // start view (pos+rot)
|
|
public Transform gameplayAnchor; // after-intro view (pos+rot)
|
|
|
|
[Header("Timing")]
|
|
public float introHoldSeconds = 3f; // top-down hold
|
|
public float transitionSeconds = 1f; // tween duration to gameplay view
|
|
public bool useUnscaledTime = false; // set true if you pause timeScale during intro
|
|
|
|
[Header("Follow")]
|
|
public float followSmoothTime = 0.20f; // SmoothDamp time
|
|
public float maxFollowSpeed = Mathf.Infinity;
|
|
|
|
// runtime
|
|
private Vector3 _worldOffset;
|
|
private Quaternion _lockedRotation;
|
|
private Vector3 _vel;
|
|
private bool _following;
|
|
private Sequence _seq;
|
|
|
|
void Start()
|
|
{
|
|
if (!player || !gameplayAnchor)
|
|
{
|
|
Debug.LogError("[CameraFollow] Assign player & gameplayAnchor.");
|
|
enabled = false;
|
|
return;
|
|
}
|
|
|
|
// snap to top-down if provided, otherwise keep current
|
|
if (topDownAnchor)
|
|
{
|
|
transform.position = topDownAnchor.position;
|
|
transform.rotation = topDownAnchor.rotation;
|
|
}
|
|
|
|
StartCoroutine(BeginFlow());
|
|
}
|
|
|
|
System.Collections.IEnumerator BeginFlow()
|
|
{
|
|
// 1) Hold top-down
|
|
if (introHoldSeconds > 0f)
|
|
{
|
|
if (useUnscaledTime) yield return new WaitForSecondsRealtime(introHoldSeconds);
|
|
else yield return new WaitForSeconds(introHoldSeconds);
|
|
}
|
|
|
|
// 2) Tween to your provided gameplay pose
|
|
_seq?.Kill();
|
|
_seq = DOTween.Sequence().SetUpdate(useUnscaledTime)
|
|
.Join(transform.DOMove(gameplayAnchor.position, transitionSeconds).SetEase(Ease.InOutSine))
|
|
.Join(transform.DORotateQuaternion(gameplayAnchor.rotation, transitionSeconds).SetEase(Ease.InOutSine))
|
|
.OnComplete(() =>
|
|
{
|
|
// 3) Lock rotation & compute world offset for follow
|
|
_lockedRotation = gameplayAnchor.rotation;
|
|
_worldOffset = gameplayAnchor.position - player.position;
|
|
_following = true;
|
|
});
|
|
|
|
yield break;
|
|
}
|
|
|
|
void LateUpdate()
|
|
{
|
|
if (!_following || !player) return;
|
|
|
|
// keep rotation fixed (no camera roll/yaw changes during follow)
|
|
if (transform.rotation != _lockedRotation)
|
|
transform.rotation = _lockedRotation;
|
|
|
|
Vector3 targetPos = player.position + _worldOffset;
|
|
|
|
if (useUnscaledTime)
|
|
{
|
|
transform.position = Vector3.SmoothDamp(
|
|
transform.position, targetPos, ref _vel, followSmoothTime, maxFollowSpeed, Time.unscaledDeltaTime);
|
|
}
|
|
else
|
|
{
|
|
transform.position = Vector3.SmoothDamp(
|
|
transform.position, targetPos, ref _vel, followSmoothTime, maxFollowSpeed);
|
|
}
|
|
}
|
|
|
|
void OnDisable() => _seq?.Kill();
|
|
|
|
/// Call if the player respawns or you reposition the gameplayAnchor at runtime.
|
|
public void RecenterToGameplayAnchor()
|
|
{
|
|
_lockedRotation = gameplayAnchor.rotation;
|
|
_worldOffset = gameplayAnchor.position - player.position;
|
|
}
|
|
}
|