From 34990beb1aeac4244608bedb533d9249c46454b0 Mon Sep 17 00:00:00 2001 From: OLC Date: Sat, 22 Nov 2025 19:28:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20ModelSoundStopTrig?= =?UTF-8?q?ger=20=E7=BB=84=E4=BB=B6=E4=BB=A5=E5=A2=9E=E5=BC=BA=E9=9F=B3?= =?UTF-8?q?=E6=95=88=E6=8E=A7=E5=88=B6=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 `ModelSoundStopTrigger` 组件,支持在动画状态机中停止音效播放 - 支持通过指定事件名称停止音效或停止所有正在播放的音效 - 提供 Unity 编辑器自定义界面,包含条件显示和警告提示 - 更新文档以反映新功能和使用说明 - 更新版本号至 v1.8.9 --- CHANGELOG.md | 9 +++ CHANGELOG_EN.md | 9 +++ .../Animators/ModelSoundStopTrigger.cs | 61 +++++++++++++++++++ .../Animators/ModelSoundTrigger.cs | 5 +- DuckovCustomModel/Constant.cs | 2 +- .../MonoBehaviours/ModelHandler.cs | 16 ++++- README.md | 27 +++++++- README_EN.md | 27 +++++++- 8 files changed, 146 insertions(+), 10 deletions(-) create mode 100644 DuckovCustomModel.Core/MonoBehaviours/Animators/ModelSoundStopTrigger.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c391aa..9754832 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ [English](CHANGELOG_EN.md) | 中文 +## v1.8.9 + +- 新增 `ModelSoundStopTrigger` 组件,支持在动画状态机中停止音效播放 + - 支持停止指定事件名称的音效 + - 支持停止所有正在播放的音效 + - 支持使用内置事件名称(如 `idle`)或自定义触发器事件名称 + - 支持在状态进入或退出时触发停止操作 + - 提供 Unity 编辑器自定义界面,包含条件显示、警告提示和帮助信息 + ## v1.8.8-fix1 - 修正了 ModelParameterDriver 的参数记录逻辑,以解决编辑器数据锁定的问题,并提升了相关数据的简洁性 diff --git a/CHANGELOG_EN.md b/CHANGELOG_EN.md index 6389c70..9047bd8 100644 --- a/CHANGELOG_EN.md +++ b/CHANGELOG_EN.md @@ -2,6 +2,15 @@ English | [中文](CHANGELOG.md) +## v1.8.9 + +- Added `ModelSoundStopTrigger` component, supporting stopping sound playback in animation state machines + - Supports stopping sounds by specified event name + - Supports stopping all currently playing sounds + - Supports using built-in event names (e.g., `idle`) or custom trigger event names + - Supports triggering stop operation on state enter or exit + - Provides Unity editor custom interface with conditional display, warning prompts, and help information + ## v1.8.8-fix1 - Fixed ModelParameterDriver parameter recording logic to resolve editor data locking issues and improve data conciseness diff --git a/DuckovCustomModel.Core/MonoBehaviours/Animators/ModelSoundStopTrigger.cs b/DuckovCustomModel.Core/MonoBehaviours/Animators/ModelSoundStopTrigger.cs new file mode 100644 index 0000000..e77ad3e --- /dev/null +++ b/DuckovCustomModel.Core/MonoBehaviours/Animators/ModelSoundStopTrigger.cs @@ -0,0 +1,61 @@ +using System; +using UnityEngine; + +namespace DuckovCustomModel.Core.MonoBehaviours.Animators +{ + public class ModelSoundStopTrigger : StateMachineBehaviour + { + [Tooltip("If true, stop all playing sounds. If false, stop the sound specified by eventName.")] + public bool stopAllSounds; + + [Tooltip( + "If true, use built-in event name directly (e.g., 'idle') without 'CustomModelSoundTrigger:' prefix. " + + "WARNING: Only use this for built-in event names like 'idle'. For custom triggers, leave this false.")] + public bool useBuiltInEventName; + + [Tooltip("Event name of the sound to stop. If empty, a default name will be used (same as ModelSoundTrigger).")] + public string eventName = string.Empty; + + [Tooltip("If true, stop the sound when entering this state. If false, stop when exiting.")] + public bool stopOnEnter = true; + + public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) + { + if (stopOnEnter) TriggerStopSound(stopAllSounds, useBuiltInEventName, eventName, animator); + } + + public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) + { + if (!stopOnEnter) TriggerStopSound(stopAllSounds, useBuiltInEventName, eventName, animator); + } + + private static void TriggerStopSound(bool stopAll, bool useBuiltIn, string eventName, Animator animator) + { + if (stopAll) + { + OnSoundStopTriggered?.Invoke(string.Empty, animator); + } + else if (useBuiltIn) + { + if (string.IsNullOrWhiteSpace(eventName)) + { + Debug.LogWarning("ModelSoundStopTrigger: useBuiltInEventName is true but eventName is empty. " + + "Please specify a built-in event name (e.g., 'idle')."); + return; + } + + OnSoundStopTriggered?.Invoke(eventName, animator); + } + else + { + var targetEvent = string.IsNullOrWhiteSpace(eventName) + ? "CustomModelSoundTrigger" + : eventName; + var finalEventName = $"CustomModelSoundTrigger:{targetEvent}"; + OnSoundStopTriggered?.Invoke(finalEventName, animator); + } + } + + public static event Action? OnSoundStopTriggered; + } +} diff --git a/DuckovCustomModel.Core/MonoBehaviours/Animators/ModelSoundTrigger.cs b/DuckovCustomModel.Core/MonoBehaviours/Animators/ModelSoundTrigger.cs index 99a7001..874b889 100644 --- a/DuckovCustomModel.Core/MonoBehaviours/Animators/ModelSoundTrigger.cs +++ b/DuckovCustomModel.Core/MonoBehaviours/Animators/ModelSoundTrigger.cs @@ -48,10 +48,11 @@ public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo } selectedTag = selectedTag.ToLowerInvariant().Trim(); - var finalEventName = string.IsNullOrWhiteSpace(eventName) + + var targetEvent = string.IsNullOrWhiteSpace(eventName) ? "CustomModelSoundTrigger" : eventName; - + var finalEventName = $"CustomModelSoundTrigger:{targetEvent}"; OnSoundTriggered?.Invoke(selectedTag, finalEventName, playMode, animator); } diff --git a/DuckovCustomModel/Constant.cs b/DuckovCustomModel/Constant.cs index 3261625..7ac45e3 100644 --- a/DuckovCustomModel/Constant.cs +++ b/DuckovCustomModel/Constant.cs @@ -4,7 +4,7 @@ public static class Constant { public const string ModID = "DuckovCustomModel"; public const string ModName = "Duckov Custom Model"; - public const string ModVersion = "1.8.8-fix1"; + public const string ModVersion = "1.8.9"; public const string HarmonyId = "com.ritsukage.DuckovCustomModel"; } } diff --git a/DuckovCustomModel/MonoBehaviours/ModelHandler.cs b/DuckovCustomModel/MonoBehaviours/ModelHandler.cs index c10253b..0a286c6 100644 --- a/DuckovCustomModel/MonoBehaviours/ModelHandler.cs +++ b/DuckovCustomModel/MonoBehaviours/ModelHandler.cs @@ -168,6 +168,7 @@ private void LateUpdate() private void OnDestroy() { ModelSoundTrigger.OnSoundTriggered -= OnSoundTriggered; + ModelSoundStopTrigger.OnSoundStopTriggered -= OnSoundStopTriggered; Health.OnHurt -= OnGlobalHurt; Health.OnDead -= OnGlobalDead; @@ -231,6 +232,7 @@ public void Initialize(CharacterMainControl characterMainControl, ModelTarget ta Health.OnDead += OnGlobalDead; ModelSoundTrigger.OnSoundTriggered += OnSoundTriggered; + ModelSoundStopTrigger.OnSoundStopTriggered += OnSoundStopTriggered; ModLogger.Log("ModelHandler initialized successfully."); IsInitialized = true; @@ -1248,7 +1250,19 @@ private void OnSoundTriggered(string soundTag, string eventName, SoundPlayMode p var soundPath = GetRandomSoundByTag(soundTag, out var skippedByProbability); if (string.IsNullOrEmpty(soundPath) || skippedByProbability) return; - PlaySound($"CustomModelSoundTrigger:{eventName}", soundPath, playMode: playMode); + PlaySound(eventName, soundPath, playMode: playMode); + } + + private void OnSoundStopTriggered(string eventName, Animator animator) + { + if (animator == null) return; + if (CustomModelInstance == null) return; + if (animator.gameObject != CustomModelInstance) return; + + if (string.IsNullOrEmpty(eventName)) + StopAllSounds(); + else + StopSound(eventName); } private void ScheduleNextIdleAudio() diff --git a/README.md b/README.md index 24b71b1..e0dc74b 100644 --- a/README.md +++ b/README.md @@ -608,6 +608,22 @@ Animator Controller 可以使用以下参数: - `playMode`:音效播放模式(Normal、StopPrevious、SkipIfPlaying、UseTempObject) - `eventName`:事件名称,用于音效播放管理(可选,为空时使用默认名称) +#### ModelSoundStopTrigger + +在动画状态进入或退出时停止音效播放。 + +- `stopAllSounds`:是否停止所有正在播放的音效(true:停止所有,false:停止指定事件名称的音效) +- `useBuiltInEventName`:是否使用内置事件名称(true:直接使用内置事件名称如 `idle`,false:使用自定义触发器事件名称) +- `eventName`:事件名称 + - 当 `stopAllSounds` 为 false 且 `useBuiltInEventName` 为 false 时:自定义触发器事件名称(可选,为空时使用默认名称 `CustomModelSoundTrigger`) + - 当 `stopAllSounds` 为 false 且 `useBuiltInEventName` 为 true 时:内置事件名称(必需,如 `idle`) +- `stopOnEnter`:是否在状态进入时停止(true:进入时停止,false:退出时停止) + +**注意事项**: +- 当 `useBuiltInEventName` 为 true 时,必须指定 `eventName`,否则会显示警告 +- 自定义触发器的事件名称格式为 `CustomModelSoundTrigger:{eventName}`,与 `ModelSoundTrigger` 保持一致 +- 内置事件名称(如 `idle`)直接使用,不添加前缀 + #### ModelDialogueTrigger 在动画状态进入时触发对话播放。 @@ -746,9 +762,14 @@ Animator Controller 可以使用以下参数: #### 动画状态机触发 - 可以在动画状态机中使用 `ModelSoundTrigger` 组件在状态进入时触发音效 -- 支持配置多个音效标签,可选择随机或顺序播放 -- 支持配置音效播放模式,提供更精细的音效控制 -- 音效标签可以是任意自定义标签,不再限制于预定义标签 + - 支持配置多个音效标签,可选择随机或顺序播放 + - 支持配置音效播放模式,提供更精细的音效控制 + - 音效标签可以是任意自定义标签,不再限制于预定义标签 +- 可以在动画状态机中使用 `ModelSoundStopTrigger` 组件停止音效播放 + - 支持停止指定事件名称的音效(自定义触发器或内置事件名称如 `idle`) + - 支持停止所有正在播放的音效 + - 支持在状态进入或退出时触发停止操作 + - 在 Unity 编辑器中提供友好的配置界面,包含条件显示和警告提示 ### 音效文件要求 diff --git a/README_EN.md b/README_EN.md index c39fcf2..f1b6c1e 100644 --- a/README_EN.md +++ b/README_EN.md @@ -608,6 +608,22 @@ Triggers sound effect playback when animation state enters. - `playMode`: Sound playback mode (Normal, StopPrevious, SkipIfPlaying, UseTempObject) - `eventName`: Event name for sound playback management (optional, uses default name if empty) +#### ModelSoundStopTrigger + +Stops sound effect playback when animation state enters or exits. + +- `stopAllSounds`: Whether to stop all currently playing sounds (true: stop all, false: stop sound by specified event name) +- `useBuiltInEventName`: Whether to use built-in event name (true: use built-in event name like `idle` directly, false: use custom trigger event name) +- `eventName`: Event name + - When `stopAllSounds` is false and `useBuiltInEventName` is false: Custom trigger event name (optional, uses default name `CustomModelSoundTrigger` if empty) + - When `stopAllSounds` is false and `useBuiltInEventName` is true: Built-in event name (required, e.g., `idle`) +- `stopOnEnter`: Whether to stop on state enter (true: stop on enter, false: stop on exit) + +**Notes**: +- When `useBuiltInEventName` is true, `eventName` must be specified, otherwise a warning will be displayed +- Custom trigger event names use the format `CustomModelSoundTrigger:{eventName}`, consistent with `ModelSoundTrigger` +- Built-in event names (e.g., `idle`) are used directly without prefix + #### ModelDialogueTrigger Triggers dialogue playback when animation state enters. @@ -746,9 +762,14 @@ Sounds can be configured in `ModelInfo` within `bundleinfo.json`: #### Animation State Machine Trigger - Can use `ModelSoundTrigger` component in animation state machines to trigger sounds when states are entered -- Supports configuring multiple sound tags, can choose random or sequential playback -- Supports configuring sound playback modes for finer audio control -- Sound tags can be any custom tags, no longer restricted to predefined tags + - Supports configuring multiple sound tags, can choose random or sequential playback + - Supports configuring sound playback modes for finer audio control + - Sound tags can be any custom tags, no longer restricted to predefined tags +- Can use `ModelSoundStopTrigger` component in animation state machines to stop sound playback + - Supports stopping sounds by specified event name (custom triggers or built-in event names like `idle`) + - Supports stopping all currently playing sounds + - Supports triggering stop operation on state enter or exit + - Provides user-friendly configuration interface in Unity editor with conditional display and warning prompts ### Sound File Requirements