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(); 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(); 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(); 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(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(); } }