Snapshots. Better actions
This commit is contained in:
1 parent
71bb4c3b5b
commit
31850e77bc
73 files changed
+31948
-80048
No files matched your search
@@ -0,0 +1,231 @@
|
||||
using System;
|
||||
using Code.Infrastructure.Utils;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.OfficeGame.BattleMode.Actions
|
||||
{
|
||||
public struct EntitySnapshot
|
||||
{
|
||||
// Transforms
|
||||
public Vector3 Position;
|
||||
public Quaternion Rotation;
|
||||
|
||||
// Animator
|
||||
AnimatorSnapshot AnimatorSnapshot;
|
||||
|
||||
public static EntitySnapshot GetSnapshot(EntityProperties entityProperties)
|
||||
{
|
||||
EntitySnapshot snapshot = new EntitySnapshot()
|
||||
{
|
||||
Position = entityProperties.KinematicCharacterMotor.TransientPosition,
|
||||
Rotation = entityProperties.KinematicCharacterMotor.TransientRotation,
|
||||
AnimatorSnapshot = entityProperties.animator.GetSnapshot()
|
||||
};
|
||||
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
public static void RestoreSnapshot(EntityProperties entityProperties, EntitySnapshot snapshot)
|
||||
{
|
||||
entityProperties.animator.RestoreSnapshot(snapshot.AnimatorSnapshot);
|
||||
|
||||
entityProperties.KinematicCharacterMotor.SetPositionAndRotation(snapshot.Position, snapshot.Rotation);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Создаёт новый экземпляр снапшота с текущим состоянием аниматора
|
||||
/// </summary>
|
||||
public static AnimatorSnapshot GetSnapshot(Animator animator)
|
||||
{
|
||||
var snapshot = new AnimatorSnapshot();
|
||||
snapshot.CaptureFrom(animator);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Записывает текущее состояние аниматора в этот снапшот (без лишних аллокаций при повторном вызове)
|
||||
/// </summary>
|
||||
public void CaptureFrom(Animator animator)
|
||||
{
|
||||
if (animator == null) return;
|
||||
|
||||
// 1. Сохраняем локальный трансформ модели (защита от смещения Root Motion)
|
||||
Transform animTransform = animator.transform;
|
||||
_localPosition = animTransform.localPosition;
|
||||
_localRotation = animTransform.localRotation;
|
||||
_speed = animator.speed;
|
||||
|
||||
// 2. Сохраняем состояния и кадры всех слоёв
|
||||
int layerCount = animator.layerCount;
|
||||
if (_layers == null || _layers.Length != layerCount)
|
||||
{
|
||||
_layers = new LayerStateSnapshot[layerCount];
|
||||
}
|
||||
|
||||
for (int i = 0; i < layerCount; i++)
|
||||
{
|
||||
// Если аниматор находился в процессе перехода (Transition),
|
||||
// можно при необходимости брать GetNextAnimatorStateInfo(i), но для покадрового Play обычно используется текущий:
|
||||
AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(i);
|
||||
|
||||
_layers[i] = new LayerStateSnapshot
|
||||
{
|
||||
FullPathHash = stateInfo.fullPathHash,
|
||||
NormalizedTime = stateInfo.normalizedTime,
|
||||
LayerWeight = animator.GetLayerWeight(i)
|
||||
};
|
||||
}
|
||||
|
||||
// 3. Сохраняем значения всех параметров
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Восстанавливает состояние в целевой аниматор (оригинал или клон-призрак)
|
||||
/// </summary>
|
||||
public void RestoreTo(Animator targetAnimator)
|
||||
{
|
||||
if (targetAnimator == null || _layers == null) return;
|
||||
|
||||
// 1. Временно выключаем Root Motion, чтобы Update(0f) не сдвинул модель повторно
|
||||
bool prevRootMotion = targetAnimator.applyRootMotion;
|
||||
targetAnimator.applyRootMotion = false;
|
||||
targetAnimator.speed = _speed;
|
||||
|
||||
// 2. Восстанавливаем параметры
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Восстанавливаем стейты и точный кадр (NormalizedTime) на каждом слое
|
||||
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);
|
||||
}
|
||||
|
||||
// 4. Принудительно пересчитываем кости скелета в текущем кадре
|
||||
targetAnimator.Update(0f);
|
||||
|
||||
// 5. Возвращаем локальное положение модели и настройки Root Motion
|
||||
Transform targetTransform = targetAnimator.transform;
|
||||
targetTransform.localPosition = _localPosition;
|
||||
targetTransform.localRotation = _localRotation;
|
||||
|
||||
targetAnimator.applyRootMotion = prevRootMotion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создаёт глубокую копию самого снапшота
|
||||
/// </summary>
|
||||
public AnimatorSnapshot Clone()
|
||||
{
|
||||
var clone = new AnimatorSnapshot
|
||||
{
|
||||
_localPosition = _localPosition,
|
||||
_localRotation = _localRotation,
|
||||
_speed = _speed
|
||||
};
|
||||
|
||||
if (_layers != null)
|
||||
{
|
||||
clone._layers = new LayerStateSnapshot[_layers.Length];
|
||||
Array.Copy(_layers, clone._layers, _layers.Length);
|
||||
}
|
||||
|
||||
if (_parameters != null)
|
||||
{
|
||||
clone._parameters = new ParameterSnapshot[_parameters.Length];
|
||||
Array.Copy(_parameters, clone._parameters, _parameters.Length);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user