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 { 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; 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 as IHaveTransformSubmodule).transformSubmodule = this; (attachedGameElement as IHaveTransformSubmodule).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 as IHaveTransformSubmodule).transformSubmodule = this; (attachedGameElement as IHaveTransformSubmodule).UpdateTransform(false); (attachedGameElement as IHaveTransformSubmodule).SetTransformObserver(); } } public override void SaveBM() { matchedBM = new TransformSubmodule_BM(attachedGameElement); } 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(); } } } public interface IHaveTransformSubmodule { TransformSubmodule transformSubmodule { get; set; } /// /// 设置物体Transform的监听,顺序为Scale -> EulerAngles -> Position /// 如果有一些特殊的物体(例如Camera,ElementFolder),需要自定义监听,可以重写这个方法 /// public void SetTransformObserver() { transformSubmodule.observer = Observable.EveryUpdate() .Where(_ => GameManager.instance.audioManager.isUpdating) .Subscribe(_ => UpdateTransform()) .AddTo(transformSubmodule.attachedGameElement); } 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.lookAtObject.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; } } } namespace Beatmap { public class TransformSubmodule_BM : Submodule_BM { public Vector3 originalPosition; public Vector3 originalEulerAngles; public Vector3 originalScale; public TransformSubmodule_BM() { } public TransformSubmodule_BM(GameElement attachedElement) : base(attachedElement) { TransformSubmodule transformSubmodule = (attachedElement as IHaveTransformSubmodule).transformSubmodule; this.originalPosition = transformSubmodule.originalPosition; this.originalEulerAngles = transformSubmodule.originalEulerAngles; this.originalScale = transformSubmodule.originalScale; } public override void ExecuteBM() { attachedElement = GameElement_BM.GetElement(attachedElementGuid); //if(attachedElement == null)Debug.Log(attachedElementGuid); (attachedElement as IHaveTransformSubmodule).transformSubmodule = new TransformSubmodule(attachedElement, originalPosition, originalEulerAngles, originalScale); } public override void DuplicateBM(GameElement attached) { (attached as IHaveTransformSubmodule).transformSubmodule = new TransformSubmodule(attached, originalPosition, originalEulerAngles, originalScale); } } } }