146 lines
5.2 KiB
C#
146 lines
5.2 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Reflection;
|
||
using Ichni.RhythmGame.Beatmap;
|
||
using UnityEngine;
|
||
|
||
namespace Ichni.RhythmGame
|
||
{
|
||
public class PropertyAnimationFloat : AnimationBase
|
||
{
|
||
#region [暴露属性字段与关联] Exposed Fields & References
|
||
public string componentName;
|
||
public string propertyName;
|
||
public FlexibleFloat propertyValue;
|
||
|
||
private Component targetComponent;
|
||
private FieldInfo targetField;
|
||
private PropertyInfo targetProperty;
|
||
|
||
// 我们尝试通过原生的 Action 来降低反射 SetValue 带来的性能损耗(装箱与GC)
|
||
private Action<float> floatSetterDelegate;
|
||
#endregion
|
||
|
||
#region [生命周期与工厂] Lifecycle & Factory
|
||
public static PropertyAnimationFloat GenerateElement(string elementName, Guid id, List<string> tags, bool isFirstGenerated,
|
||
GameElement animatedObject, string componentName, string propertyName, FlexibleFloat propertyValue)
|
||
{
|
||
PropertyAnimationFloat animation = Instantiate(GameManager.Instance.basePrefabs.emptyObject)
|
||
.AddComponent<PropertyAnimationFloat>();
|
||
|
||
animation.Initialize(elementName, id, tags, isFirstGenerated, animatedObject);
|
||
|
||
animation.animatedObject = animatedObject;
|
||
|
||
animation.componentName = componentName;
|
||
animation.propertyName = propertyName;
|
||
animation.propertyValue = propertyValue;
|
||
|
||
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($"[PropertyAnimationFloat] 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();
|
||
|
||
// 1. 尝试寻找 Property
|
||
targetProperty = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||
if (targetProperty != null && targetProperty.CanWrite)
|
||
{
|
||
MethodInfo setMethod = targetProperty.GetSetMethod(nonPublic: true);
|
||
if (setMethod != null)
|
||
{
|
||
try
|
||
{
|
||
floatSetterDelegate = (Action<float>)Delegate.CreateDelegate(typeof(Action<float>), targetComponent, setMethod);
|
||
return; // 建立快速委托成功!
|
||
}
|
||
catch { /* 回落 */ }
|
||
}
|
||
}
|
||
|
||
// 2. 尝试寻找 Field (直接包含公有/私有)
|
||
targetField = type.GetField(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||
if (targetField == null && targetProperty == null)
|
||
{
|
||
Debug.LogWarning($"[PropertyAnimationFloat] Cannot find target Property or Field '{propertyName}' strictly on '{componentName}'.");
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region [核心动画逻辑] Core Animation Logic
|
||
protected override void UpdateAnimation(float songTime, bool forceUpdate)
|
||
{
|
||
if (targetComponent == null || propertyValue == null || propertyValue.animations.Count == 0) return;
|
||
|
||
propertyValue.UpdateFlexibleFloat(songTime);
|
||
float value = propertyValue.value;
|
||
|
||
if (floatSetterDelegate != null)
|
||
{
|
||
floatSetterDelegate(value);
|
||
}
|
||
else if (targetProperty != null)
|
||
{
|
||
targetProperty.SetValue(targetComponent, value);
|
||
}
|
||
else if (targetField != null)
|
||
{
|
||
targetField.SetValue(targetComponent, value);
|
||
}
|
||
|
||
if (animatedObject is IHaveDirtyMarkSubmodule dirtyTarget)
|
||
{
|
||
dirtyTarget.dirtyMarkSubmodule.MarkDirty(propertyName);
|
||
}
|
||
}
|
||
|
||
public override void ApplyTimeOffset(float offset)
|
||
{
|
||
base.ApplyTimeOffset(offset);
|
||
if (propertyValue != null && propertyValue.animations != null)
|
||
{
|
||
foreach(var a in propertyValue.animations)
|
||
{
|
||
a.startTime += offset;
|
||
a.endTime += offset;
|
||
}
|
||
}
|
||
}
|
||
#endregion
|
||
}
|
||
}
|