using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Ichni.RhythmGame.Beatmap; using UniRx; using UniRx.Triggers; using Unity.VisualScripting; using UnityEngine; using Object = UnityEngine.Object; namespace Ichni.RhythmGame { public class TransformSubmodule : SubmoduleBase { #region [变换属性状态缓存] Transform Cached States public Vector3 originalPosition; public Vector3 originalEulerAngles; public Vector3 originalScale; public Vector3 positionOffset; public Vector3 eulerAnglesOffset; public Vector3 scaleOffset; public Vector3 currentPosition; public Vector3 currentEulerAngles; public Vector3 currentScale; public bool positionDirtyMark; public bool eulerAnglesDirtyMark; public bool scaleDirtyMark; public bool eulerAnglesOffsetLock; public IDisposable observer; #endregion #region [构造函数与初始化] Constructors & Initialization public TransformSubmodule(GameElement attachedGameElement) : base(attachedGameElement) { this.originalPosition = Vector3.zero; this.originalEulerAngles = Vector3.zero; this.originalScale = Vector3.one; positionOffset = Vector3.zero; eulerAnglesOffset = Vector3.zero; scaleOffset = Vector3.zero; currentPosition = Vector3.zero; currentEulerAngles = Vector3.zero; currentScale = Vector3.one; positionDirtyMark = true; eulerAnglesDirtyMark = true; scaleDirtyMark = true; eulerAnglesOffsetLock = false; if (!HaveSameSubmodule && attachedGameElement is IHaveTransformSubmodule host) { host.transformSubmodule = this; host.SetTransformObserver(); } } public TransformSubmodule(GameElement attachedGameElement, Vector3 originalPosition, Vector3 originalEulerAngles, Vector3 originalScale) : base(attachedGameElement) { this.originalPosition = originalPosition; this.originalEulerAngles = originalEulerAngles; this.originalScale = originalScale; positionOffset = Vector3.zero; eulerAnglesOffset = Vector3.zero; scaleOffset = Vector3.zero; currentPosition = originalPosition; currentEulerAngles = originalEulerAngles; currentScale = originalScale; positionDirtyMark = true; eulerAnglesDirtyMark = true; scaleDirtyMark = true; eulerAnglesOffsetLock = false; if (!HaveSameSubmodule && attachedGameElement is IHaveTransformSubmodule host) { host.transformSubmodule = this; host.SetTransformObserver(); } } #endregion #region [生命周期与状态刷新] Lifecycle & State Refresh public override void Refresh() { positionDirtyMark = true; eulerAnglesDirtyMark = true; scaleDirtyMark = true; } private bool HaveAnimation() => attachedGameElement.childElementList .Any(element => element is Displacement or Swirl or Scale or LookAt); public override void CheckAndRemoveObservers() { if (!HaveAnimation()) { observer?.Dispose(); } } #endregion } #region [组件接口] Component Interface public interface IHaveTransformSubmodule { TransformSubmodule transformSubmodule { get; set; } /// /// 设置物体Transform的监听,顺序为Scale -> EulerAngles -> Position /// 如果有一些特殊的物体(例如Camera,ElementFolder),需要自定义监听,可以重写这个方法 /// public void SetTransformObserver() { // 旧版的 UniRx 各自监听已淘汰,现由 GameManager 中枢在 LateUpdate 统一下发 UpdateTransform() // 如果有一些特殊物体需要极其特殊时序,可覆盖此方法或手动管理 } public void UpdateTransform(bool refreshAll = true) { GameElement attachedGameElement = transformSubmodule.attachedGameElement; bool willRefresh = false; if (transformSubmodule.scaleDirtyMark) { transformSubmodule.currentScale = transformSubmodule.originalScale + transformSubmodule.scaleOffset; attachedGameElement.transform.localScale = transformSubmodule.currentScale; transformSubmodule.scaleDirtyMark = false; willRefresh = true; transformSubmodule.scaleOffset = Vector3.zero; } if (!transformSubmodule.eulerAnglesOffsetLock && transformSubmodule.eulerAnglesDirtyMark) { transformSubmodule.currentEulerAngles = transformSubmodule.originalEulerAngles + transformSubmodule.eulerAnglesOffset; attachedGameElement.transform.localEulerAngles = transformSubmodule.currentEulerAngles; transformSubmodule.eulerAnglesDirtyMark = false; willRefresh = true; transformSubmodule.eulerAnglesOffset = Vector3.zero; } if (transformSubmodule.positionDirtyMark) { transformSubmodule.currentPosition = transformSubmodule.originalPosition + transformSubmodule.positionOffset; attachedGameElement.transform.localPosition = transformSubmodule.currentPosition; transformSubmodule.positionDirtyMark = false; willRefresh = true; transformSubmodule.positionOffset = Vector3.zero; } if(refreshAll && willRefresh) { attachedGameElement.Refresh(); } } public void UpdateLookAt(LookAt lookAt) // 处理LookAt { Transform target = lookAt.targetGameElement.transform; Transform self = transformSubmodule.attachedGameElement.transform; if (transformSubmodule.eulerAnglesOffsetLock && transformSubmodule.eulerAnglesDirtyMark) { Vector3 lookingDirection = (target.position - self.position).normalized; Vector3 eulerAnglesOffset = Quaternion.LookRotation(lookingDirection).eulerAngles; transformSubmodule.eulerAnglesOffset += eulerAnglesOffset; transformSubmodule.currentEulerAngles = transformSubmodule.originalEulerAngles + transformSubmodule.eulerAnglesOffset; self.localEulerAngles = transformSubmodule.currentEulerAngles; transformSubmodule.eulerAnglesDirtyMark = false; transformSubmodule.eulerAnglesOffsetLock = false; transformSubmodule.eulerAnglesOffset = Vector3.zero; } } } #endregion }