using System; using System.Collections; using System.Collections.Generic; using Ichni.RhythmGame.Beatmap; using Unity.VisualScripting; using UnityEngine; namespace Ichni.RhythmGame { public partial class Displacement : AnimationBase { #region [暴露属性字段与关联] Exposed Fields & References private TransformSubmodule targetTransformSubmodule; public FlexibleFloat positionX, positionY, positionZ; public static HashSet ActiveDisplacements = new HashSet(); #endregion protected void OnEnable() { ActiveDisplacements.Add(this); } protected void OnDisable() { ActiveDisplacements.Remove(this); } #region [生命周期与工厂] Lifecycle & Factory public static Displacement GenerateElement(string elementName, Guid id, List tags, bool isFirstGenerated, GameElement animatedObject, FlexibleFloat positionX, FlexibleFloat positionY, FlexibleFloat positionZ) { Displacement displacement = Instantiate(GameManager.Instance.basePrefabs.emptyObject).AddComponent(); displacement.Initialize(elementName, id, tags, isFirstGenerated, animatedObject); displacement.animatedObject = animatedObject; displacement.positionX = positionX; displacement.positionY = positionY; displacement.positionZ = positionZ; displacement.animationReturnType = FlexibleReturnType.Before; displacement.targetTransformSubmodule = (animatedObject as IHaveTransformSubmodule).transformSubmodule; //displacement.timeDurationSubmodule.SetDuration(positionX, positionY, positionZ); return displacement; } #endregion #region [核心动画逻辑] Core Animation Logic protected override void UpdateAnimation(float songTime, bool forceUpdate) { positionX.UpdateFlexibleFloat(songTime); positionY.UpdateFlexibleFloat(songTime); positionZ.UpdateFlexibleFloat(songTime); bool isMiddleExecuting = positionX.returnType is FlexibleReturnType.MiddleExecuting || positionY.returnType is FlexibleReturnType.MiddleExecuting || positionZ.returnType is FlexibleReturnType.MiddleExecuting; bool isSwitching = positionX.isSwitchingReturnType || positionY.isSwitchingReturnType || positionZ.isSwitchingReturnType; if (forceUpdate || isMiddleExecuting) { if(!forceUpdate) animationReturnType = FlexibleReturnType.MiddleExecuting; // 首帧合并退让保护 bool shouldSkipFirstFrame = false; if (!forceUpdate && isSwitching) { foreach (var d in ActiveDisplacements) { if (d != this && d.targetTransformSubmodule == this.targetTransformSubmodule) { if (d.positionX.isSwitchingReturnType || d.positionY.isSwitchingReturnType || d.positionZ.isSwitchingReturnType) { shouldSkipFirstFrame = true; break; } } } } if (!shouldSkipFirstFrame) { Vector3 currentPosition = new Vector3(positionX.value, positionY.value, positionZ.value); targetTransformSubmodule.positionOffset += currentPosition; targetTransformSubmodule.positionDirtyMark = true; } } else if (isSwitching) { // 最终帧补偿机制 bool isAnyOtherExecuting = false; foreach (var d in ActiveDisplacements) { if (d != this && d.targetTransformSubmodule == this.targetTransformSubmodule) { if (d.positionX.returnType is FlexibleReturnType.MiddleExecuting || d.positionY.returnType is FlexibleReturnType.MiddleExecuting || d.positionZ.returnType is FlexibleReturnType.MiddleExecuting) { isAnyOtherExecuting = true; break; } } } if (!isAnyOtherExecuting) { animationReturnType = FlexibleReturnType.MiddleExecuting; Vector3 currentPosition = new Vector3(positionX.value, positionY.value, positionZ.value); targetTransformSubmodule.positionOffset += currentPosition; targetTransformSubmodule.positionDirtyMark = true; } } else { animationReturnType = FlexibleReturnType.MiddleInterval; } } public override void ApplyTimeOffset(float offset) { base.ApplyTimeOffset(offset); positionX.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); positionY.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); positionZ.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); } #endregion } }