ProjectDDD/Packages/SLUnity/UnitAnimatorController.cs
2025-07-08 19:46:31 +09:00

226 lines
6.5 KiB
C#

using System.Collections.Generic;
using Superlazy;
using UnityEngine;
public interface IUnitAnimatorBinder
{
void AddUpdate(IUnitViewUpdator updator);
void RemoveUpdate(IUnitViewUpdator updator);
bool UseAnimation { get; }
bool RequireReset { get; }
string Animation { get; }
int CurrentFrame { get; }
bool UseActionAnimationBegin { get; }
float ActionAnimationBegin { get; }
bool UseCrossFade { get; } // actionStop == false
float CrossFade { get; }
bool ActionStop { get; }
bool Loop { get; }
}
public interface IUnitViewUpdator
{
void OnUpdate();
}
public class UnitAnimatorController : MonoBehaviour, IUnitViewUpdator
{
private static readonly float frameTime = 1.0f / 60.0f;
private static readonly int[] animHashes = new int[] { Animator.StringToHash("Anim1"), Animator.StringToHash("Anim2") }; // Anim1, Anim2의 애니메이터 해시
private IUnitAnimatorBinder unitView;
private Animator animator;
private AnimatorOverrideController overrideController;
private int animatorFrame;
private float animatorTime;
private string currentAnim;
private AnimationClip[] clips;
private int animIndex = 0;
private bool requireReset = false;
private List<(Transform t, Vector3 p, Quaternion r, Vector3 s)> bindPoses;
private void Awake() // TODO: 빌드시점으로 옮길수 있으면 좋음
{
animator = GetComponentInChildren<Animator>();
animatorFrame = 0;
currentAnim = null;
animator.cullingMode = AnimatorCullingMode.AlwaysAnimate; // cullingMode가 걸려있는경우 꺼졌다가 켜진 상태에서 모든 동작이 X
if (animator.runtimeAnimatorController == null)
{
overrideController = new AnimatorOverrideController(SLResources.GetAnimatorController("BaseController"));
animator.runtimeAnimatorController = overrideController;
clips = overrideController.animationClips;
}
if (bindPoses == null)
{
var allChildren = animator.GetComponentsInChildren<Transform>();
bindPoses = new List<(Transform t, Vector3 p, Quaternion r, Vector3 s)>();
foreach (var child in allChildren)
{
bindPoses.Add((child, child.localPosition, child.localRotation, child.localScale));
}
}
}
private void OnEnable()
{
unitView = GetComponentInParent<IUnitAnimatorBinder>();
if (unitView == null) // Tool 등에서 유닛이 없는 경우 사용하지 않도록 설정
{
return;
}
unitView.AddUpdate(this);
}
private void ResetPose()
{
if (bindPoses == null) return;
foreach (var (t, p, r, s) in bindPoses)
{
t.localPosition = p;
t.localRotation = r;
t.localScale = s;
}
}
private bool UpdateCurrAnimation()
{
if (unitView.CurrentFrame - 1 - animatorFrame >= 0 && unitView.Animation == currentAnim) return false;
ChangeController(unitView.Animation);
currentAnim = unitView.Animation;
return true;
}
private void ChangeController(string clipName)
{
var clip = SLResources.Load<AnimationClip>(clipName);
if (clip == null)
{
SLLog.Error($"Can't Find: {clipName}.anim", this);
return;
}
if (requireReset)
{
ResetPose();
}
requireReset = unitView.RequireReset;
animIndex = (animIndex + 1) % 2;
overrideController[clips[animIndex]] = clip;
var startTime = 0.0f;
if (unitView.UseActionAnimationBegin)
{
startTime = (unitView.ActionAnimationBegin - 1) / 60.0f;
}
var currentFrame = unitView.CurrentFrame - 1;
startTime += currentFrame / 60.0f;
if (currentAnim != null && unitView.UseCrossFade && unitView.ActionStop == false)
{
animator.CrossFadeInFixedTime(animHashes[animIndex], unitView.CrossFade, 0, startTime);
}
else
{
animator.PlayInFixedTime(animHashes[animIndex], 0, startTime);
}
animator.speed = 1;
animator.Update(0.0f); // 업데이트를 돌때 속도가 0이면 시작 시간이 동작하지 않는다
animator.speed = 0f;
animatorFrame = currentFrame;
animatorTime = currentFrame * frameTime;
}
private void UpdateAnimator()
{
var currentFrame = unitView.CurrentFrame - 1;
var deltaFrame = currentFrame - animatorFrame;
var deltaTime = Time.deltaTime;
if (deltaFrame > 0 && Time.timeScale <= 0.01f)
{
animator.updateMode = AnimatorUpdateMode.UnscaledTime;
deltaTime = Time.unscaledDeltaTime;
}
else
{
animator.updateMode = AnimatorUpdateMode.Normal;
}
if (deltaTime > 0.0000001f)
{
if (deltaFrame < 1)
{
if ((Time.timeScale < 1 || currentFrame == -1) && unitView.ActionStop == false) // 프레임이 없거나 슬로우 상태
{
animatorTime += deltaTime;
animator.speed = 1.0f;
}
else
{
animator.speed = 0f;
}
}
else
{
if (currentFrame * frameTime - animatorTime >= 0)
{
animator.speed = (currentFrame * frameTime - animatorTime) / deltaTime;
animatorFrame = currentFrame;
animatorTime = currentFrame * frameTime;
}
else
{
animator.speed = 0;
animatorFrame = currentFrame;
}
}
if (unitView.Loop)
{
var time = animator.GetCurrentAnimatorStateInfo(0).normalizedTime;
if (time > 1.0f && animator.IsInTransition(0) == false)
{
animator.Play(animHashes[animIndex], 0, time - Mathf.Floor(time));
}
}
}
}
public void OnUpdate()
{
if (unitView.UseAnimation == false) return;
//UpdateCurrAnimation();
if (UpdateCurrAnimation() == false)
{
UpdateAnimator();
}
}
private void OnDisable()
{
unitView.RemoveUpdate(this);
unitView = null;
currentAnim = null;
ResetPose();
}
}