如何在220帧动画模型的第70帧触发音频播放事件?
动画第70帧触发音频播放的实现方案
核心逻辑是实时监听动画播放的帧进度,当到达目标帧时触发音频播放,同时避免重复触发。以下是不同引擎的具体实现方式:
通用注意事项
- 确保动画资源的帧率设置准确,否则帧计算会出现偏差
- 若动画为循环播放,需在动画结束后重置触发标记,保证每次循环都能正常触发
- 可将判断条件从
等于70帧改为大于等于70帧,避免因帧率过高或更新间隔导致的帧跳过问题
Unity 实现
方式1:使用 Animator 组件
using UnityEngine; public class FrameAudioTrigger : MonoBehaviour { private Animator _animator; private AudioSource _audioSource; private bool _hasTriggered; [SerializeField] private int targetFrame = 70; void Start() { _animator = GetComponent<Animator>(); _audioSource = GetComponent<AudioSource>(); _audioSource.playOnAwake = false; } void Update() { if (_animator.isPlaying && !_hasTriggered) { AnimatorStateInfo stateInfo = _animator.GetCurrentAnimatorStateInfo(0); AnimationClip clip = _animator.runtimeAnimatorController.animationClips[0]; // 计算当前播放帧:动画时长 * 帧率 * 播放进度 int currentFrame = Mathf.RoundToInt(stateInfo.length * clip.frameRate * stateInfo.normalizedTime); if (currentFrame >= targetFrame) { _audioSource.Play(); _hasTriggered = true; } } // 动画结束后重置触发标记 if (!_animator.isPlaying) { _hasTriggered = false; } } }
方式2:使用 Animation 组件
using UnityEngine; public class LegacyAnimationTrigger : MonoBehaviour { private Animation _animation; private AudioSource _audioSource; private bool _triggered; public int targetFrame = 70; void Start() { _animation = GetComponent<Animation>(); _audioSource = GetComponent<AudioSource>(); _audioSource.playOnAwake = false; } void Update() { if (_animation.isPlaying && !_triggered) { AnimationClip clip = _animation.clip; // 计算当前播放帧:播放时间 / 单帧时长 int currentFrame = Mathf.RoundToInt(_animation[clip.name].time / (1f / clip.frameRate)); if (currentFrame >= targetFrame) { _audioSource.Play(); _triggered = true; } } if (!_animation.isPlaying) { _triggered = false; } } }
Unreal Engine 实现
方式1:蓝图实现
- 在动画蓝图的动画图表中,添加
Get Animation Current Frame节点获取当前播放帧 - 添加布尔变量
bHasTriggered,用于标记是否已触发音频 - 使用分支节点判断:
当前帧 >= 70且bHasTriggered == false - 满足条件时,调用
Play Sound 2D或Play Sound at Location播放音频,同时将bHasTriggered设置为true - 在动画结束时(通过
Is Animation Playing节点判断),将bHasTriggered重置为false
方式2:C++ 实现
#include "Animation/AnimInstance.h" #include "Audio/AudioComponent.h" #include "CoreMinimal.h" UCLASS() class YOURPROJECT_API UAnimFrameAudioTrigger : public UAnimInstance { GENERATED_BODY() public: UPROPERTY(EditAnywhere, Category = "Trigger") int32 TargetFrame = 70; UPROPERTY(EditAnywhere, Category = "Audio") USoundBase* TargetAudio; private: bool bHasTriggered = false; UAudioComponent* AudioComp; virtual void NativeInitializeAnimation() override { Super::NativeInitializeAnimation(); AActor* Owner = GetOwningActor(); if (!Owner) return; AudioComp = Owner->FindComponentByClass<UAudioComponent>(); if (!AudioComp) { AudioComp = NewObject<UAudioComponent>(Owner); AudioComp->SetupAttachment(Owner->GetRootComponent()); AudioComp->RegisterComponent(); } } virtual void NativeUpdateAnimation(float DeltaSeconds) override { Super::NativeUpdateAnimation(DeltaSeconds); if (!AudioComp || !TargetAudio || bHasTriggered) return; UAnimMontage* CurrentMontage = GetCurrentActiveMontage(); if (!CurrentMontage) { bHasTriggered = false; return; } float CurrentTime = GetMontagePosition(CurrentMontage); int32 CurrentFrame = FMath::RoundToInt(CurrentTime * CurrentMontage->GetFrameRate()); if (CurrentFrame >= TargetFrame) { AudioComp->SetSound(TargetAudio); AudioComp->Play(); bHasTriggered = true; } } };
内容的提问来源于stack exchange,提问作者fran35
相关产品推荐
相关产品推荐

