-
Notifications
You must be signed in to change notification settings - Fork 4
feat: 添加 ModelParameterDriver 以支持自定义参数控制并增强参数显示器 #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,4 +8,3 @@ public enum SoundPlayMode | |
| UseTempObject, | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,194 @@ | ||||||
| using System.Linq; | ||||||
| using DuckovCustomModel.Core.MonoBehaviours.Animators; | ||||||
| using UnityEngine; | ||||||
|
|
||||||
| namespace DuckovCustomModel.Core.Managers | ||||||
| { | ||||||
| public static class AnimatorParameterDriverManager | ||||||
| { | ||||||
| public static void InitializeDriver(ModelParameterDriver parameterDriver, Animator animator) | ||||||
| { | ||||||
| if (parameterDriver.Initialized) return; | ||||||
|
|
||||||
| parameterDriver.Initialized = true; | ||||||
|
|
||||||
| var enableParameters = parameterDriver.parameters | ||||||
| .Where(parameter => InitializeParameter(parameter, animator)).ToArray(); | ||||||
|
|
||||||
| parameterDriver.parameters = enableParameters; | ||||||
| parameterDriver.IsEnabled = parameterDriver.parameters.Length > 0; | ||||||
| } | ||||||
|
|
||||||
| public static void ApplyParameter(ModelParameterDriver.Parameter parameter, Animator animator) | ||||||
| { | ||||||
| switch (parameter.type) | ||||||
| { | ||||||
| case ModelParameterDriver.ChangeType.Set: | ||||||
| { | ||||||
| ApplyParameterAsSet(parameter, animator); | ||||||
| break; | ||||||
| } | ||||||
| case ModelParameterDriver.ChangeType.Add: | ||||||
| { | ||||||
| ApplyParameterAsAdd(parameter, animator); | ||||||
| break; | ||||||
| } | ||||||
| case ModelParameterDriver.ChangeType.Random: | ||||||
| { | ||||||
| ApplyParameterAsRandom(parameter, animator); | ||||||
| break; | ||||||
| } | ||||||
| case ModelParameterDriver.ChangeType.Copy: | ||||||
| { | ||||||
| ApplyParameterAsCopy(parameter, animator); | ||||||
| break; | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| private static bool InitializeParameter(ModelParameterDriver.Parameter parameter, Animator animator) | ||||||
| { | ||||||
| if (string.IsNullOrEmpty(parameter.name)) return false; | ||||||
|
|
||||||
| var parameterExists = animator.parameters | ||||||
| .Any(p => p.name == parameter.name); | ||||||
|
|
||||||
| if (!parameterExists) return false; | ||||||
|
|
||||||
| var destParam = animator.parameters | ||||||
| .FirstOrDefault(p => p.name == parameter.name); | ||||||
| if (destParam == null) return false; | ||||||
|
|
||||||
| parameter.DestParam = destParam; | ||||||
|
|
||||||
| if (parameter.type != ModelParameterDriver.ChangeType.Copy) return true; | ||||||
| { | ||||||
| if (string.IsNullOrEmpty(parameter.source)) return false; | ||||||
|
|
||||||
| var sourceParam = animator.parameters | ||||||
| .FirstOrDefault(p => p.name == parameter.source); | ||||||
| if (sourceParam == null) return false; | ||||||
|
|
||||||
| parameter.SourceParam = sourceParam; | ||||||
| } | ||||||
|
|
||||||
| return true; | ||||||
| } | ||||||
|
|
||||||
| private static void ApplyParameterAsSet(ModelParameterDriver.Parameter parameter, Animator animator) | ||||||
| { | ||||||
| if (parameter.DestParam is not AnimatorControllerParameter targetParam) | ||||||
| return; | ||||||
|
|
||||||
| switch (targetParam.type) | ||||||
| { | ||||||
| case AnimatorControllerParameterType.Float: | ||||||
| animator.SetFloat(targetParam.name, parameter.value); | ||||||
| break; | ||||||
| case AnimatorControllerParameterType.Int: | ||||||
| animator.SetInteger(targetParam.name, (int)parameter.value); | ||||||
| break; | ||||||
| case AnimatorControllerParameterType.Bool: | ||||||
| animator.SetBool(targetParam.name, parameter.value > 0f); | ||||||
| break; | ||||||
| case AnimatorControllerParameterType.Trigger: | ||||||
| animator.SetTrigger(targetParam.name); | ||||||
| break; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| private static void ApplyParameterAsAdd(ModelParameterDriver.Parameter parameter, Animator animator) | ||||||
| { | ||||||
| if (parameter.DestParam is not AnimatorControllerParameter targetParam) | ||||||
| return; | ||||||
|
|
||||||
| switch (targetParam.type) | ||||||
| { | ||||||
| case AnimatorControllerParameterType.Float: | ||||||
| { | ||||||
| var currentValue = animator.GetFloat(targetParam.name); | ||||||
| animator.SetFloat(targetParam.name, currentValue + parameter.value); | ||||||
| break; | ||||||
| } | ||||||
| case AnimatorControllerParameterType.Int: | ||||||
| { | ||||||
| var currentValue = animator.GetInteger(targetParam.name); | ||||||
| animator.SetInteger(targetParam.name, currentValue + (int)parameter.value); | ||||||
| break; | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| private static void ApplyParameterAsRandom(ModelParameterDriver.Parameter parameter, Animator animator) | ||||||
| { | ||||||
| if (parameter.DestParam is not AnimatorControllerParameter targetParam) | ||||||
| return; | ||||||
|
|
||||||
| switch (targetParam.type) | ||||||
| { | ||||||
| case AnimatorControllerParameterType.Float: | ||||||
| var randomFloat = Random.Range(parameter.valueMin, parameter.valueMax); | ||||||
| animator.SetFloat(targetParam.name, randomFloat); | ||||||
| break; | ||||||
| case AnimatorControllerParameterType.Int: | ||||||
| var randomInt = Random.Range((int)parameter.valueMin, (int)parameter.valueMax); | ||||||
| animator.SetInteger(targetParam.name, randomInt); | ||||||
| break; | ||||||
| case AnimatorControllerParameterType.Bool: | ||||||
| var randomBool = Random.value < parameter.chance; | ||||||
| animator.SetBool(targetParam.name, randomBool); | ||||||
| break; | ||||||
| case AnimatorControllerParameterType.Trigger: | ||||||
| if (Random.value < parameter.chance) | ||||||
| animator.SetTrigger(targetParam.name); | ||||||
| break; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| private static void ApplyParameterAsCopy(ModelParameterDriver.Parameter parameter, Animator animator) | ||||||
| { | ||||||
| if (parameter.DestParam is not AnimatorControllerParameter targetParam) | ||||||
| return; | ||||||
|
|
||||||
| if (parameter.SourceParam is not AnimatorControllerParameter sourceParam) | ||||||
| return; | ||||||
|
|
||||||
| if (sourceParam.type == AnimatorControllerParameterType.Trigger) | ||||||
| return; | ||||||
|
|
||||||
| var sourceValue = sourceParam.type switch | ||||||
| { | ||||||
| AnimatorControllerParameterType.Float => animator.GetFloat(sourceParam.name), | ||||||
| AnimatorControllerParameterType.Int => animator.GetInteger(sourceParam.name), | ||||||
| AnimatorControllerParameterType.Bool => animator.GetBool(sourceParam.name) ? 1f : 0f, | ||||||
| _ => 0f, | ||||||
| }; | ||||||
|
|
||||||
| var finalValue = sourceValue; | ||||||
| if (parameter.convertRange) | ||||||
| { | ||||||
| var sourceMin = parameter.sourceMin; | ||||||
| var sourceMax = parameter.sourceMax; | ||||||
| var targetMin = parameter.valueMin; | ||||||
| var targetMax = parameter.valueMax; | ||||||
|
|
||||||
| if (sourceMax - sourceMin != 0) | ||||||
|
||||||
| if (sourceMax - sourceMin != 0) | |
| if (Mathf.Abs(sourceMax - sourceMin) > Mathf.Epsilon) |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,91 @@ | ||||||||||||||||||||||||||||||||
| using System; | ||||||||||||||||||||||||||||||||
| using DuckovCustomModel.Core.Managers; | ||||||||||||||||||||||||||||||||
| using Newtonsoft.Json; | ||||||||||||||||||||||||||||||||
| using UnityEngine; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| namespace DuckovCustomModel.Core.MonoBehaviours.Animators | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| public class ModelParameterDriver : StateMachineBehaviour, ISerializationCallbackReceiver | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
|
Comment on lines
+8
to
+9
|
||||||||||||||||||||||||||||||||
| public class ModelParameterDriver : StateMachineBehaviour, ISerializationCallbackReceiver | |
| { | |
| /// <summary> | |
| /// Drives animator parameters when animation states are entered, similar to Unity's built-in Animator Parameter Driver. | |
| /// Attach this component to animation state machine states to automatically set, add, randomize, or copy animator parameters | |
| /// when the state is entered. Useful for controlling complex animation logic and parameter changes in a modular way. | |
| /// </summary> | |
| public class ModelParameterDriver : StateMachineBehaviour, ISerializationCallbackReceiver |
Copilot
AI
Nov 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fields destMin and destMax are defined but never used. The range conversion logic in AnimatorParameterDriverManager.ApplyParameterAsCopy (lines 172-173) uses valueMin and valueMax instead. Either remove these unused fields or update the range conversion logic to use them if they were intended for a different purpose than valueMin/valueMax.
| public float destMin; | |
| public float destMax; |
Copilot
AI
Nov 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Public properties DestParam and SourceParam store cached parameter references but lack documentation explaining their purpose and that they are populated during initialization. Consider adding XML documentation comments to clarify that these are runtime-only cached values used for performance optimization.
| public object? DestParam; | |
| /// <summary> | |
| /// Runtime-only cached reference to the destination parameter. | |
| /// Populated during initialization for performance optimization. | |
| /// Not intended to be set manually or serialized. | |
| /// </summary> | |
| public object? DestParam; | |
| /// <summary> | |
| /// Runtime-only cached reference to the source parameter. | |
| /// Populated during initialization for performance optimization. | |
| /// Not intended to be set manually or serialized. | |
| /// </summary> |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,18 @@ | ||||||||||||
| using System; | ||||||||||||
| using UnityEngine; | ||||||||||||
|
|
||||||||||||
| namespace DuckovCustomModel.Core.MonoBehaviours.Packages | ||||||||||||
| { | ||||||||||||
| [AddComponentMenu("Duckov Custom Model/Blueprint ID")] | ||||||||||||
| [DisallowMultipleComponent] | ||||||||||||
|
||||||||||||
| [DisallowMultipleComponent] | |
| [DisallowMultipleComponent] | |
| /// <summary> | |
| /// Assigns unique identifiers to game objects. Currently a placeholder for future functionality. | |
| /// </summary> |
Copilot
AI
Nov 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The public field id should have a tooltip attribute to explain its purpose in the Unity Inspector. Consider adding [Tooltip("Unique identifier for this game object")] above the field declaration to improve usability for users configuring this component in Unity.
| { | |
| { | |
| [Tooltip("Unique identifier for this game object")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unity's
Random.Rangefor integers is exclusive on the upper bound, meaningRandom.Range((int)parameter.valueMin, (int)parameter.valueMax)will never returnvalueMax. This differs from the float version which is inclusive. Consider documenting this behavior or adding 1 tovalueMaxif inclusive behavior is intended:Random.Range((int)parameter.valueMin, (int)parameter.valueMax + 1).