using UnityEngine; namespace Code.OfficeGame.BattleMode.Actions { public struct EntitySnapshot { public Vector3 Position; public Quaternion Rotation; public AnimatorSnapshot AnimatorSnapshot; public static EntitySnapshot GetSnapshot(EntityProperties entityProperties) { return new EntitySnapshot { Position = entityProperties.KinematicCharacterMotor.TransientPosition, Rotation = entityProperties.KinematicCharacterMotor.TransientRotation, AnimatorSnapshot = AnimatorSnapshot.GetSnapshot(entityProperties.animator) }; } public static void RestoreSnapshot(EntityProperties entityProperties, EntitySnapshot snapshot) { // 1. Возвращаем капсулу KCC entityProperties.KinematicCharacterMotor.SetPositionAndRotation(snapshot.Position, snapshot.Rotation); entityProperties.KinematicCharacterMotor.BaseVelocity = Vector3.zero; // 2. Возвращаем позу и локальное смещение меша snapshot.AnimatorSnapshot.RestoreTo(entityProperties.animator); // 3. Синхронизируем физику Physics.SyncTransforms(); } } public struct AnimatorSnapshot { public struct LayerStateSnapshot { public int FullPathHash; public float NormalizedTime; public float LayerWeight; } public struct ParameterSnapshot { public int NameHash; public AnimatorControllerParameterType Type; public float FloatValue; public int IntValue; public bool BoolValue; } private LayerStateSnapshot[] _layers; private ParameterSnapshot[] _parameters; private Vector3 _localPosition; private Quaternion _localRotation; private float _speed; public static AnimatorSnapshot GetSnapshot(Animator animator) { var snapshot = new AnimatorSnapshot(); snapshot.CaptureFrom(animator); return snapshot; } public void CaptureFrom(Animator animator) { if (animator == null) return; Transform animTransform = animator.transform; _localPosition = animTransform.localPosition; _localRotation = animTransform.localRotation; _speed = animator.speed; int layerCount = animator.layerCount; if (_layers == null || _layers.Length != layerCount) _layers = new LayerStateSnapshot[layerCount]; for (int i = 0; i < layerCount; i++) { AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(i); _layers[i] = new LayerStateSnapshot { FullPathHash = stateInfo.fullPathHash, NormalizedTime = stateInfo.normalizedTime, LayerWeight = animator.GetLayerWeight(i) }; } int paramCount = animator.parameterCount; if (_parameters == null || _parameters.Length != paramCount) _parameters = new ParameterSnapshot[paramCount]; for (int i = 0; i < paramCount; i++) { AnimatorControllerParameter param = animator.GetParameter(i); int hash = param.nameHash; var paramSnapshot = new ParameterSnapshot { NameHash = hash, Type = param.type }; switch (param.type) { case AnimatorControllerParameterType.Float: paramSnapshot.FloatValue = animator.GetFloat(hash); break; case AnimatorControllerParameterType.Int: paramSnapshot.IntValue = animator.GetInteger(hash); break; case AnimatorControllerParameterType.Bool: case AnimatorControllerParameterType.Trigger: paramSnapshot.BoolValue = animator.GetBool(hash); break; } _parameters[i] = paramSnapshot; } } public void RestoreTo(Animator targetAnimator) { if (targetAnimator == null || _layers == null) return; targetAnimator.Rebind(); // Отключаем applyRootMotion на время Update(0f), чтобы Play() не начислил дельту смещения bool prevRootMotion = targetAnimator.applyRootMotion; targetAnimator.applyRootMotion = false; targetAnimator.speed = _speed; if (_parameters != null) { for (int i = 0; i < _parameters.Length; i++) { var p = _parameters[i]; if (targetAnimator.IsParameterControlledByCurve(p.NameHash)) continue; switch (p.Type) { case AnimatorControllerParameterType.Float: targetAnimator.SetFloat(p.NameHash, p.FloatValue); break; case AnimatorControllerParameterType.Int: targetAnimator.SetInteger(p.NameHash, p.IntValue); break; case AnimatorControllerParameterType.Bool: targetAnimator.SetBool(p.NameHash, p.BoolValue); break; case AnimatorControllerParameterType.Trigger: if (p.BoolValue) targetAnimator.SetTrigger(p.NameHash); else targetAnimator.ResetTrigger(p.NameHash); break; } } } int layerCount = Mathf.Min(_layers.Length, targetAnimator.layerCount); for (int i = 0; i < layerCount; i++) { var layer = _layers[i]; targetAnimator.SetLayerWeight(i, layer.LayerWeight); targetAnimator.Play(layer.FullPathHash, i, layer.NormalizedTime); } targetAnimator.Update(0f); // ОБЯЗАТЕЛЬНО восстанавливаем сохраненные локальные координаты меша после Update(0f)! Transform targetTransform = targetAnimator.transform; targetTransform.localPosition = _localPosition; targetTransform.localRotation = _localRotation; targetAnimator.applyRootMotion = prevRootMotion; } } }