zoukankan      html  css  js  c++  java
  • ue4音效、动画结合实例

    在游戏中,许多音效需要在动画恰当的时机出现,例如行走、奔跑,就需要恰好在足部落地瞬间播放。

    而AnimNotify就能非常方便地处理此类问题。

    AnimNotify,顾名思义就是动画通知,能在特定的动画片段播放到特定进度时“发出消息”。

    目前我们的工程有前、后、左、右、左前、右前、左后、右后八向的跑动动画。

    先以向前跑为例,用右键添加通知的方式,分别在右脚、左脚落地时添加了lfoot_touchground与rfoot_touchground的两个自定义通知

    当然直接添加playsound通知也是可以的,能很方便地直接设置声音,但为了功能的可扩展性我们还是使用自定义通知。

    然而我们如何才能获取这些通知呢?

    if (mesh) {
            UAnimInstance* anim=mesh->GetAnimInstance();
            if (anim) {
                TArray<const struct FAnimNotifyEvent*> AnimNotifies=anim->NotifyQueue.AnimNotifies;
                range(i,0,AnimNotifies.Num()) {
                    FString NotifyName=AnimNotifies[i]->NotifyName.ToString();
                    /*GEngine->A/ddOnScreenDebugMessage
                    (-1, 5.f, FColor::Red, FString::FromInt(i)+" "+ NotifyName);*/
                    if (NotifyName=="lfoot_touchground") {
                        audio_lfoot->SetSound(sb_walks[0]);
                        audio_lfoot->Play();
                    }
                    else if (NotifyName == "rfoot_touchground") {
                        audio_rfoot->SetSound(sb_walks[1]);
                        audio_rfoot->Play();
                    }
                    else {
    
                    }
                }
            }
        }

    没错,就是这么简单anim->NotifyQueue即为动画通知队列,AnimNotifies就能获取当前所有通知事件,上述代码去掉注释即可打印当前帧所有动画通知名称。

    为了实现播放音效,我们还需要绑定在左右脚的AudioComponent,如果当前通知队列中有对应的通知,就先SetSound设置将要播放的声音资源后再Play。

    我把所有需要绑定在Chracter的Mesh上的AudioComponent“打包装入“了一个叫AudioController的类,在Character的构造函数中进行了AudioController的构造,并将AudioController中的各音效组件绑定到对应的socket上。

    AudioController.h文件

    // Fill out your copyright notice in the Description page of Project Settings.
    
    #pragma once
    
    #include "Components/ActorComponent.h"
    #include "Components/AudioComponent.h"
    #include "Components/SkeletalMeshComponent.h"
    #include "AudioController.generated.h"
    
    
    UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
    class MYSOUNDANDEFFECTS_API UAudioController : public UActorComponent
    {
        GENERATED_BODY()
    
    public:    
        // Sets default values for this component's properties
        UAudioController();
        //UAudioController(USkeletalMeshComponent* mesh_);
    
    protected:
        // Called when the game starts
        virtual void BeginPlay() override;
    
    public:    
        // Called every frame
        virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
    
        UPROPERTY(Category = Audio, VisibleDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
            UAudioComponent* audio_lfoot = NULL;
        UPROPERTY(Category = Audio, EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
            FString bonename_lfoot = "Bip01-L-Foot";
    
        UPROPERTY(Category = Audio, VisibleDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
            UAudioComponent* audio_rfoot = NULL;
        UPROPERTY(Category = Audio, EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
            FString bonename_rfoot = "Bip01-R-Foot";
    
        USkeletalMeshComponent* mesh = NULL;
    
        void my_init(USkeletalMeshComponent* mesh_);
    
        UPROPERTY(Category = Audio, EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
            TArray<class USoundBase*> sb_walks;
        
        
    };

    AudioController.cpp文件

    // Fill out your copyright notice in the Description page of Project Settings.
    
    #include "MySoundAndEffects.h"
    #include "Animation/AnimInstance.h"
    #include "AudioController.h"
    
    
    // Sets default values for this component's properties
    UAudioController::UAudioController()
    {
        // Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
        // off to improve performance if you don't need them.
        PrimaryComponentTick.bCanEverTick = true;
        // ...
        audio_lfoot = CreateDefaultSubobject<UAudioComponent>("audio_lfoot");
        audio_rfoot = CreateDefaultSubobject<UAudioComponent>("audio_rfoot");
    
    }
    
    //UAudioController::UAudioController(USkeletalMeshComponent* mesh_)
    //{
    //    // Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
    //    // off to improve performance if you don't need them.
    //    PrimaryComponentTick.bCanEverTick = true;
    //
    //    // ...
    //    
    //
    //    
    //}
    
    
    void UAudioController::my_init(USkeletalMeshComponent* mesh_) {
        this->mesh = mesh_;
        //UAudioController();
        audio_lfoot->SetupAttachment(mesh_, FName(*bonename_lfoot));
        audio_rfoot->SetupAttachment(mesh_, FName(*bonename_rfoot));
    }
    
    
    // Called when the game starts
    void UAudioController::BeginPlay()
    {
        Super::BeginPlay();
    
        // ...
        
    }
    
    
    // Called every frame
    void UAudioController::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
    {
        Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
    
        // ...
        if (mesh) {
            UAnimInstance* anim=mesh->GetAnimInstance();
            if (anim) {
                TArray<const struct FAnimNotifyEvent*> AnimNotifies=anim->NotifyQueue.AnimNotifies;
                range(i,0,AnimNotifies.Num()) {
                    FString NotifyName=AnimNotifies[i]->NotifyName.ToString();
                    /*GEngine->A/ddOnScreenDebugMessage
                    (-1, 5.f, FColor::Red, FString::FromInt(i)+" "+ NotifyName);*/
                    if (NotifyName=="lfoot_touchground") {
                        audio_lfoot->SetSound(sb_walks[0]);
                        audio_lfoot->Play();
                    }
                    else if (NotifyName == "rfoot_touchground") {
                        audio_rfoot->SetSound(sb_walks[1]);
                        audio_rfoot->Play();
                    }
                    else {
    
                    }
                }
            }
        }
        else {
            throw std::exception("mesh not exist!!");
        }
    
    }

    还有character类中audiocontroller的定义和创建:

    AAimPraticeHuman::AAimPraticeHuman()
    {
    #ifdef AudioController_h
    	audiocontroller = CreateDefaultSubobject<UAudioController>("audiocontroller");
    	audiocontroller->my_init(GetMesh());
    #endif
    }
    

     

    UPROPERTY(Category = Audio, VisibleDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
      class UAudioController* audiocontroller = NULL;

    最后就是音效导入,看似简单其实有很多细节。

    建议直接拖入wav格式文件,如果导入失败应该是wav文件具体参数的问题。

    (我用Audition把一长串的跑步音效分割出了两声脚碰地的声音,直接导入ue4报错,然而先用格式工厂转一下就ok了)

    当然现在的音效还不能直接用于游戏脚步,还需要先设置并发和立体效果。

    Concurrency项勾选override即可,使用”先远后旧“的并发停止策略问题也不大。

    Attenuation项我新建了一个叫SceneSoundAttenuation蓝图,设置如下:

    这些选项看字面都比较好理解,3D Stereo Spread的意思其实就是左右耳间距。

    最后别忘了设置character蓝图的sb_walks数组的值,以及左右脚的socketname

    大功告成啦!

    具体效果。。。反正你们也听不到。。。

    ------------------------------------------------

    下期预告:美妙的IK(反向动力学)

  • 相关阅读:
    English 2
    速算24点
    心理学1
    从微服务到函数式编程
    034 01 Android 零基础入门 01 Java基础语法 04 Java流程控制之选择结构 01 流程控制概述
    033 01 Android 零基础入门 01 Java基础语法 03 Java运算符 13 运算符和表达式知识点总结
    032 01 Android 零基础入门 01 Java基础语法 03 Java运算符 12 运算符和if-else条件语句的综合案例——闰年问题
    031 01 Android 零基础入门 01 Java基础语法 03 Java运算符 11 运算符的优先级
    030 01 Android 零基础入门 01 Java基础语法 03 Java运算符 10 条件运算符
    029 01 Android 零基础入门 01 Java基础语法 03 Java运算符 09 逻辑“非”运算符
  • 原文地址:https://www.cnblogs.com/wzj998/p/7512799.html
Copyright © 2011-2022 走看看