using System; using System.Collections.Generic; using System.Reflection; using Ichni.RhythmGame.Beatmap; using UnityEngine; namespace Ichni.RhythmGame { public class PropertyAnimationVector3 : AnimationBase { #region [暴露属性字段与关联] Exposed Fields & References public string componentName; public string propertyName; public FlexibleFloat propertyValueX, propertyValueY, propertyValueZ; private Component targetComponent; private FieldInfo targetField; private PropertyInfo targetProperty; // 高性能赋值委托 private Action vectorSetterDelegate; #endregion #region [生命周期与工厂] Lifecycle & Factory public static PropertyAnimationVector3 GenerateElement(string elementName, Guid id, List tags, bool isFirstGenerated, GameElement animatedObject, string componentName, string propertyName, FlexibleFloat propertyValueX, FlexibleFloat propertyValueY, FlexibleFloat propertyValueZ) { PropertyAnimationVector3 animation = Instantiate(GameManager.Instance.basePrefabs.emptyObject) .AddComponent(); animation.Initialize(elementName, id, tags, isFirstGenerated, animatedObject); animation.animatedObject = animatedObject; animation.componentName = componentName; animation.propertyName = propertyName; animation.propertyValueX = propertyValueX; animation.propertyValueY = propertyValueY; animation.propertyValueZ = propertyValueZ; return animation; } public override void AfterInitialize() { if (animatedObject == null || string.IsNullOrEmpty(componentName)) return; Type componentType = GetTypeFromAllAssemblies(componentName); if (componentType != null) { targetComponent = animatedObject.GetComponentInChildren(componentType); } if (targetComponent != null) { InitializeReflection(); } else { Debug.LogWarning($"[PropertyAnimationVector3] Cannot find Component '{componentName}' strictly on '{animatedObject.name}'."); } base.AfterInitialize(); } private Type GetTypeFromAllAssemblies(string typeName) { foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { Type type = assembly.GetType(typeName); if (type != null) return type; } return null; } private void InitializeReflection() { Type type = targetComponent.GetType(); targetProperty = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (targetProperty != null && targetProperty.CanWrite) { MethodInfo setMethod = targetProperty.GetSetMethod(nonPublic: true); if (setMethod != null && targetProperty.PropertyType == typeof(Vector3)) { try { vectorSetterDelegate = (Action)Delegate.CreateDelegate(typeof(Action), targetComponent, setMethod); return; } catch { } } } targetField = type.GetField(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (targetField == null && targetProperty == null) { Debug.LogWarning($"[PropertyAnimationVector3] Cannot find target Property or Field '{propertyName}' strictly on '{componentName}'."); } } #endregion #region [核心动画逻辑] Core Animation Logic protected override void UpdateAnimation(float songTime) { if (targetComponent == null) return; propertyValueX?.UpdateFlexibleFloat(songTime); propertyValueY?.UpdateFlexibleFloat(songTime); propertyValueZ?.UpdateFlexibleFloat(songTime); float x = propertyValueX != null ? propertyValueX.value : 0f; float y = propertyValueY != null ? propertyValueY.value : 0f; float z = propertyValueZ != null ? propertyValueZ.value : 0f; Vector3 targetVector = new Vector3(x, y, z); if (vectorSetterDelegate != null) { vectorSetterDelegate(targetVector); } else if (targetProperty != null) { targetProperty.SetValue(targetComponent, targetVector); } else if (targetField != null) { targetField.SetValue(targetComponent, targetVector); } if (animatedObject is IHaveDirtyMarkSubmodule dirtyTarget) { dirtyTarget.dirtyMarkSubmodule.MarkDirty(propertyName); } } public override void ApplyTimeOffset(float offset) { base.ApplyTimeOffset(offset); void ApplyOffset(FlexibleFloat ff) { if (ff == null || ff.animations == null) return; foreach(var a in ff.animations) { a.startTime += offset; a.endTime += offset; } } ApplyOffset(propertyValueX); ApplyOffset(propertyValueY); ApplyOffset(propertyValueZ); } #endregion } }