using System; using System.Threading; using Code.Game.Gameplay.Player; using Code.OfficeGame.BattleMode.Timeline; using Cysharp.Threading.Tasks; using KinematicCharacterController; using UnityEngine; using UnityEngine.Serialization; namespace Code.OfficeGame.BattleMode.Actions.HoloCopy { public class HoloCop : MonoBehaviour { private static readonly int VelocityForward = Animator.StringToHash("VelocityForward"); private Func _action; [SerializeField] private MovementController movementController; [SerializeField] private KinematicCharacterMotor motor; [SerializeField] private KinematicCharacterMotor originMotor; [SerializeField] private Animator animator; [SerializeField] private Animator originAnimator; public MovementController Controller => movementController; public Animator Animator => animator; private Vector3 _initialMotorPosition; private Quaternion _initialMotorRotation; private int _initialAnimationState; private float _initialNormalizedTime; private void Snapshot() { _initialMotorPosition = originMotor.TransientPosition; _initialMotorRotation = originMotor.TransientRotation; AnimatorStateInfo stateInfo = originAnimator.GetCurrentAnimatorStateInfo(0); _initialAnimationState = stateInfo.shortNameHash; _initialNormalizedTime = stateInfo.normalizedTime; animator.applyRootMotion = false; animator.SetFloat(VelocityForward, originAnimator.GetFloat(VelocityForward)); animator.Play(_initialAnimationState, 0, _initialNormalizedTime); animator.Update(0f); animator.applyRootMotion = true; } private void ReturnToSnapshot() { bool prevRootMotion = animator.applyRootMotion; animator.applyRootMotion = false; animator.Play(_initialAnimationState, 0, _initialNormalizedTime); animator.Update(0f); animator.transform.localPosition = Vector3.zero; animator.transform.localRotation = Quaternion.identity; originMotor.SetPositionAndRotation(_initialMotorPosition, _initialMotorRotation); animator.applyRootMotion = prevRootMotion; Physics.SyncTransforms(); } public async UniTask Simulate(Func action, CancellationToken ctx) { _action = action; float accumulator = 0f; Snapshot(); try { gameObject.SetActive(true); while (!ctx.IsCancellationRequested) { await UniTask.Yield(PlayerLoopTiming.Update, ctx); accumulator += Time.unscaledDeltaTime; while (accumulator >= BattleTimeline.TickDelta) { accumulator -= BattleTimeline.TickDelta; if (_action == null) return; bool result = _action(this, BattleTimeline.TickDelta); animator.Update(BattleTimeline.TickDelta); Physics.Simulate(BattleTimeline.TickDelta); Physics.SyncTransforms(); if (result) ReturnToSnapshot(); } } } finally { ReturnToSnapshot(); gameObject.SetActive(false); } } } }