Unity如何实现可传入时长、外部直接调用的嘴部动画协程
Unity PlayerMouthSpeechController 调用简化方案
问题背景
原有PlayerMouthSpeechController脚本存在标志位冗余、Update逻辑混乱的问题,优化后已经实现了传入时长参数控制嘴部动画的功能,但调用时需要外部手动调用StartCoroutine,希望简化调用方式,无需外部处理协程启动逻辑。
实现方案
核心思路是增加一层公共包装方法,把协程启动的逻辑封装在脚本内部:
- 将原有返回
IEnumerator的协程方法改为私有,避免外部直接调用 - 新增一个公共无返回值的
AnimateMouth方法,接收时长参数,内部自行启动协程 - 额外增加运行状态判断,避免重复触发协程导致逻辑异常
修改后完整代码
using System; using System.Collections; using UnityEngine; public class PlayerMouthSpeechController : MonoBehaviour { public float duration; [Range(0, 100)] public float valueRange; private bool _isTalking = false; private SkinnedMeshRenderer _bodySkinnedMeshRenderer; void Start() { _bodySkinnedMeshRenderer = GetComponent<SkinnedMeshRenderer>(); } //Lerp between startValue and endValue over 'duration' seconds private IEnumerator LerpShape(float startValue, float endValue, float duration) { float elapsed = 0; while (elapsed < duration) { elapsed += Time.deltaTime; float value = Mathf.Lerp(startValue, endValue, elapsed / duration); _bodySkinnedMeshRenderer.SetBlendShapeWeight(0, value); yield return null; } } // 内部协程方法,处理嘴部动画逻辑 private IEnumerator AnimateMouthCoroutine(float timeToTalk) { _isTalking = true; // 启动计时协程,到时间后关闭说话状态 StartCoroutine(StopTalkingAfterDelay(timeToTalk)); while (_isTalking) { yield return StartCoroutine(LerpShape(0, valueRange, duration)); yield return StartCoroutine(LerpShape(valueRange, 0, duration)); } } // 计时协程,指定时间后关闭说话状态 private IEnumerator StopTalkingAfterDelay(float delay) { yield return new WaitForSeconds(delay); _isTalking = false; } // 对外暴露的公共调用方法,无需外部处理协程 public void AnimateMouth(float timeToTalk) { // 可选逻辑:如果当前正在说话,先打断之前的动画再重新启动 if (_isTalking) { StopAllCoroutines(); // 重置嘴部为闭合状态 _bodySkinnedMeshRenderer.SetBlendShapeWeight(0, 0); } // 内部启动协程 StartCoroutine(AnimateMouthCoroutine(timeToTalk)); } }
调用方式
其他脚本不需要任何改动,直接调用即可:
// 直接调用,无需手动写StartCoroutine blendShapeController.AnimateMouth(10f);
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

