(个人)VR太极拳学习系统-创新实训第三周(1)

上次遗留的问题

上次编译错误信息显示找不到Sphinx-UE4的头文件,原因是没有在模块下的.Build.cs文件中添加依赖。
.Build.cs文件是ue4中一个模块的编译配置文件,类似于make使用的makefile,只不过epic选择使用c#自己实现一个工具,也就是Unreal Build Tool。
要解决上回的问题,需要在需要使用道Sphinx-UE4文件的模块中添加依赖,具体做法是找到构造函数中调用PublicDependencyModuleNames.AddRange()的位置,在其中添加上要依赖的模块,如下图所示:

使用c++扩展关卡蓝图类

要扩展关卡蓝图,需要继承自ALevelScriptActor类,如下所示

// SphinxLevelScriptActor.h
#pragma once

#include "SpeechRecognition.h"
#include "SpeechRecognitionActor.h"
#include "CoreMinimal.h"
#include "Engine/LevelScriptActor.h"
#include "SphinxLevelScriptActor.generated.h"

UCLASS()
class TAICHIPROJECT_API ASphinxLevelScriptActor : public ALevelScriptActor
{
    GENERATED_BODY()

public:
    ASphinxLevelScriptActor();

public:
    UPROPERTY(BlueprintReadOnly)
    class ASpeechRecognitionActor* speechActor;

    virtual void BeginPlay() override;

    virtual void EndPlay(const EEndPlayReason::Type EndPlayReason);

    UFUNCTION()
    void OnWordSpoken(FRecognisedPhrases phrases);

    UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "On Word Spoken"))
    void OnWordSpokenImpl(const FRecognisedPhrases &phrases);

    UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "Init Word List"))
    void InitWordList();

};

要使用Sphinx-UE4的功能,必须用到其提供的ASpeechRecognitionActor类,因此在BeginPlay()函数中spawn一个该Actor并使用一个指针来保存其引用,使用BlueprintReadOnly将其暴露到蓝图中使用。这里有一点需注意的是,父类的BeginPlay()函数会调用蓝图的BeginPlay事件,所以如果在BeginPlay()函数的实现中首先调用了父类的,那么蓝图的BeginPlay事件会在C++中实现的代码之前运行,如果要蓝图中的实现依赖于c++的BeginPlay()函数中初始化的某些变量,那么就会出现问题。

然后要将一个回调函数动态绑定到该actor的OnWordSpoken事件上,用来处理用户输入的命令,注意要绑定的函数必须使用UFUNCTION()宏修饰,因为动态绑定实际上使用到了我们正在实现的类对应的UCLASS实例中保存的元数据,只有使用虚幻提供的宏修饰变量和函数,UCLASS才能获得这些元数据。
还有一点比较重要,可以看到上面的代码中,声明了一个OnWordSpoken(FRecognisedPhrases)函数和一个OnWordSpokenImpl(const FRecognisedPhrases&)函数,其中前者是被绑定到ASpeechRecognitionActor的OnWordSpoken事件的,它的实现只是调用了后者。要这样实现是因为绑定到事件的函数的签名必须与事件期望的一致,所以参数必须是FRecognisedPhrases类型,但是我们希望在蓝图中才真正实现这个回调,所以需要将参数也在蓝图中显示出来,而值类型的参数在蓝图中是不会显示的,要改成引用类型才可以。另外有两种方式让蓝图重写c++类的函数,分别是使用BlueprintNativeEvent限定符和使用BlueprintImplementableEvent限定符,但是两者的区别是前者只能在普通蓝图中使用,而后者可以在普通蓝图和关卡蓝图中使用。更具体的差异可以参考官方文档给出的描述:
这里写图片描述
这里写图片描述
接下来的初始化过程与蓝图中的节点一致,但是我们可能在不同的关卡中需要使用不同的命令,因此我们可以将具体命令的初始化同样变为一个蓝图实现的函数。原本我希望的是可以在蓝图中以函数的形式重写c++函数,这样可以将具体命令的数组以返回值的形式返回到c++函数中,然后将模式的设置在c++中完成,相当于蓝图只是提供数据,这样就避免蓝图中忘记调用Set Keyword Mode节点而导致bug,可惜的是我没有找到具体的方法,似乎蓝图中只能够以事件的形式来重写c++函数,而事件是没有办法返回值的。

具体的实现代码如下:

// SphinxLevelScriptActor.cpp
#include "SphinxLevelScriptActor.h"
#define print(text) if(GEngine) GEngine->AddOnScreenDebugMessage(-1, 1.5, FColor::Cyan, text)

ASphinxLevelScriptActor::ASphinxLevelScriptActor():
    ALevelScriptActor(),
    speechActor(nullptr)
{ 
    PrimaryActorTick.bCanEverTick = true;
}

void ASphinxLevelScriptActor::BeginPlay()
{
    Super::BeginPlay();

    FVector location(0.f, 0.f, 0.f);
    FRotator rotation(0.f, 0.f, 0.f);
    FActorSpawnParameters spawnInfo;
    speechActor = GetWorld()->SpawnActor<ASpeechRecognitionActor>(location, rotation, spawnInfo);

    speechActor->OnWordsSpoken.AddDynamic(this, &ASphinxLevelScriptActor::OnWordSpoken);

    bool result = speechActor->Init(ESpeechRecognitionLanguage::VE_Chinese);
    if (result)
    {
        print("ASphinxLevelScriptActor:Init success");

        speechActor->SetConfigParam("-vad_prespeech", ESpeechRecognitionParamType::VE_INTEGER, "10");
        speechActor->SetConfigParam("-vad_postspeech", ESpeechRecognitionParamType::VE_INTEGER, "10");
        speechActor->SetConfigParam("-agc", ESpeechRecognitionParamType::VE_STRING, "noise");
        speechActor->SetConfigParam("-beam", ESpeechRecognitionParamType::VE_FLOAT, "1e-60");

        InitWordList();
    }
    else
    {
        print("ASphinxLevelScripActor:Init failure");
    }
}

void ASphinxLevelScriptActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    speechActor->Shutdown();
}

void ASphinxLevelScriptActor::OnWordSpoken(FRecognisedPhrases phrases)
{
    OnWordSpokenImpl(phrases);
}

接下来只需要在关卡蓝图中设置刚刚实现的类为父类,并补全Init Word List事件和On Word Spoken事件就可以使用语音控制功能了:
这里写图片描述
这里写图片描述
这里写图片描述

猜你喜欢

转载自blog.csdn.net/dying_isaac/article/details/80025370