概述
Epic Games 的 Unreal Engine*拥有一个强大的虚拟现实 (VR) 编辑器选项,但是该选项不具备在虚拟现实中编辑与放置音效的能力。调整音效后,必须不断重启编辑器,才能测试虚拟现实中的音效,这是件非常麻烦的事。因此,我们决定创建一款音效编辑器,以支持游戏开发人员与音效设计师在虚拟现实中快速放置、编辑与测试空间化音效。This will prevent the user from having to constantly enter and exit the editor.
系统要求
- Unreal Engine 4.18.1 或更高版本
- Visual Studio* 2017
- HTC Vive*
您将学到的知识
- 动作控制器交互
- 如何创建一个自定义 C++ 类
- VR UI
- 保存编辑器修改
- 音效空间化参数
下面,我们将从头到尾逐步介绍如何创建这款面向 Unreal Engine 的自定义音频编辑器工具:
开始前,您需要做几件事。下载并解压项目文件夹。您还需要确保已安装 Unreal Engine 4.18.1 或更高版本。
下载并解压文件夹后,右击 Intel_VR_Audio_Tools.uproject 并选择“生成 Visual Studio 项目文件”(Generate Visual Studio project files)。完成后,打开项目。将出现“缺少 Intel_VR_Audio_Tools 模块”的弹出消息。单击“是”开始重新创建;应该耗时不到 20 秒。鉴于您动态查找添加至项目的 .wav 文件的方式,这是必要的,我们将在自定义 C++ 类章节中予以介绍。
设置虚拟现实播放器
我们首先从 Unreal 虚拟现实模板开始,选择 MotionControllerPawn 作为我们的 pawn,它的动作控制已设置,并支持通过远距传动移动。
动作控制器交互
动作控制器与 3D 小部件交互前,需要将 WidgetInteraction 组件添加至 VirtualRealityBP 文件夹中的 BP_MotionController。我们还需要为音效选择器小部件添加一个名为 soundScene 的场景组件。
右触发器被启动时,Press 和 Release Pointer key 连接调用的事件。我们需要增加 MotionControllerPawn,它也位于 VirtualRealityBP 文件夹中。
自定义 C++ 类
制作教程时,了解音效的名称和位置以及动态更新小部件以匹配所有文件是一个难以解决的问题,因此,需要重建项目。幸运的是,Unreal Engine 为我们提供了一些帮助。
IntelSoundComponent 是一个可以添加至任意蓝图的 C++ 类,用于动态定位与加载 .wav 文件至 USoundWave,Unreal 也是通过这种方式加载音效文件。
首先,右击内容浏览器并创建一个名为 IntelSoundComponent 的新 C++ 类。该操作创建了一个 IntelSoundComponent.cpp 文件和 IntelSoundComponent.h 文件。
接下来,我们添加定位与管理文件所需的 include。
在 IntelSoundComponent.cpp 中添加了 Paths.h, FileManager.h 和 Runtime/Engine/Classes/Sound/SoundWave.h(因为某些原因,SoundWave.h 前面的部分必不可少)两种 Include。
#include "IntelSoundComponent.h" #include "Paths.h" #include "FileManager.h" #include "Runtime/Engine/Classes/Sound/SoundWave.h" bool exists; FString dir, soundDir; TArray<FString> soundFiles; // Sets default values for this component's properties UIntelSoundComponent::UIntelSoundComponent() { // 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; //Empty soundFiles TArray.Easiest way if new wave files are added. soundFiles.Empty(); //the way Unreal Engine calls the project's root directory dir = FPaths::ProjectDir(); //Combining Root with the folder location for the sounds. //This could probably be an external folder if needed with the help of ( IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); ) soundDir = dir + "Content/Sounds"; //UE4 returns a bool if the directory exists or not. exists = FPaths::DirectoryExists(soundDir); }
代码块。IntelSoundComponent.cpp
现在,我们创建了一个名为 exists 的 bool、两个名为 dir 和 SoundDir 的 FString 变量和一个名为 soundFiles 的 FString TArray。由于 soundFiles 是一个 TArray,我们可以通过调用 soundFiles.Empty() 来清空 TArray。我们相信如果添加了新波形文件,这也是最快的方法。然后,我们将 FString dir 设置为 FPaths::ProjectDir();(提供了项目的根位置)。接下来,我们将 FString soundDir 设置为 dir + "Content/Sounds”,因为它是我们放置 .wav 文件的文件夹。FPaths 可以通过其他方法来检查目录是否存在,因此,我们将 bool 设置为 exists = FPaths::DirectoryExists(soundDir);。
// Called when the game starts void UIntelSoundComponent::BeginPlay() { Super::BeginPlay(); //UE4 way of managing files IFileManager &fileManager = IFileManager::Get(); //UE_LOG(LogTemp, Warning, TEXT("%s"), &fileManager); if (exists == true){ //Extensions to sound files.Was using .wav, but .uasset seems to work when there is and isn't an editor. FString ext = "/*.wav"; FString ext2 = "/*.uasset"; //path = FPaths::ProjectDir() + Content/Sounds + /*.uasset FString path = soundDir + ext2; //This finds file in the given array, with the given path //the true bool is saying to look for files while false bool is saying to not look for directories fileManager.FindFiles(soundFiles, *path, true, false);
代码块。IntelSoundComponent.cpp
在 BeginPlay() 上,我们首先使用 IFileManager &fileManager = IFileManager::Get(); 对 IFileManager 进行实例化。我们的目标是调试与测试 .wav 文件是否能被 fileManager.FindFiles 发现,后者搜索 .uassets,而不是我们之前使用的 .wav 文件,因为 .uassets 在共享项目时更可靠。
//Setting soundFileArray to soundFiles to pass into blueprint. void UIntelSoundComponent::soundArray(TArray<FString> &soundFileArray) { soundFileArray = soundFiles; } //loading a wav file as a USoundWave so Unreal can set the sound chosen with LoadObject<USoundWave> for blueprint USoundWave* UIntelSoundComponent::setWavToSoundWave(const FString &fileName) { USoundWave* swRef; FString name = fileName; swRef = LoadObject<USoundWave>(nullptr, *name); return swRef; }
代码块。IntelSoundComponent.cpp
最后,我们在 .cpp 中创建了显示为蓝图节点的两个函数。它们是 SoundArray(将 soundFiles TArray 传输至蓝图)和 setWavToSoundWave(我们花了一些时间才弄清楚,因为我们需要找到一个动态引用 .wav 文件的方法,这个方法必须为 Unreal 所理解,它是一个 USoundWave)。为了解决该问题,我们发现了 LoadObject。如果条件允许的话,该函数在运行时将对象加载至我们设置的任何类型。对我们而言,它是 LoadObject(nullptr, *name);—*name 是虚拟现实玩家选择的音效。
我们在 IntelSoundComponent.h 中创建了两个 UFUNCTION,以调用 .cpp 蓝图中的两个函数。
#include "CoreMinimal.h" #include "Components/SceneComponent.h" #include "Runtime/Engine/Classes/Sound/SoundWave.h" #include "IntelSoundComponent.generated.h" UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) class INTEL_VR_AUDIO_TOOLS_API UIntelSoundComponent : public USceneComponent { GENERATED_BODY() public: // Sets default values for this component's properties UIntelSoundComponent(); //Blueprint function to expose soundFiles into blueprint. UFUNCTION(BlueprintCallable, Category = IntelAudio) void soundArray(TArray<FString> &soundFileArray); //Blueprint function passing a wav converted in USoundwave into blueprint. UFUNCTION(Category = IntelAudio, BlueprintCallable) USoundWave* setWavToSoundWave(const FString &fileName);
代码块。IntelSoundComponent.h
向蓝图展示音效文件的蓝图函数。
传输 .wav 文件的蓝图函数在 USoundWave 中被转换为蓝图。
设置 UI
我们需要设置 3 个 UMG 小部件。
我们创建了用于管理这些 UMG 小部件的蓝图。
我们为该项目配备了若干个小部件。选择音效后,AudioParamsSliderWidget 小部件将弹出。soundButtonWidgetBP 只是面向 Content/Sound 文件夹中音效的按钮部件。我们将 soundSelectorWidgetBP 小部件放在关卡中,具体方法创建一个名为 IntelSoundWidgetBP 的 actorBP,并使其获取来自 SoundArray C++ 节点的音效,然后使用 soundButtonWidgetBP 来填充 soundSelectorWidgetBP。(我们可以动态地完成,但是每次开始播放时,我们都会获得新生成的 actor 的引用。)这一切都发生在 IntelSoundManagerBP 中,我们从一开始也把它放在关卡中。
IntelSoundManagerBP
我们从上图中获取了 FString 的 soundFiles TArray,与此同时,分割.wav 的名称(音效名称)。我们将该字符串发送至 IntelSoundWidget 的字符串列,以命名动态填充的按钮。
IntelSoundWidgetBP
我们在 IntelSoundWidgetBP 中生成 soundUI,
添加音效,
如果我们不使用 Set Widget 节点,将生成小部件,但是它在游戏中不可见。
音效参数
玩家从小部件中选择音效后,将生成一个 IntelSoundAudioActorBP actor。我们在 actor 中看到了 AudioParamsSliderWidgetBP,如果单击 Spatialize?,将显示 3 个衰减设置,可以通过小部件对它们进行修改。
声音衰减本质上是指随着玩家的离开,音量减弱的能力。
![graph](http://software.intel.com//sites/default/files/managed/16/4c/webops-8974-fig13.1-attenuation-inverse.png)
![graph](http://software.intel.com//sites/default/files/managed/60/71/webops-8974-fig13.2-attenuation-log-reverse.png)
显示的 3 个设置为衰减函数、衰减形状和衰减距离。
如果时间充足,可以显示更多设置。Unreal 中存在衰减设置结构的映像。
我们相信我们选择的 3 个设置是最基本、最需要的设置。我们现在正在研究如何在改变设置时显示调试行。我们正在尝试使用衰减设置调试行 Unreal 用例来显示游戏编辑器中的衰减,但是还未找到答案。因此,我们可能得到所选的衰减形状和函数的形状范围,并使用 Unreal 内置绘制调试行节点。
退出时保存
当我们退出游戏,生成了音效,移动它们并且运行了音频参数时,我们通过 IntelSoundAudioActorBP,使用 IntelSaveGameBP 保存所有重要的变量。
IntelSaveGameBP
IntelSoundManagerBP
IntelSoundAudioActorBP
如果一切正常运行,我们应该能够在虚拟现实中编辑文件夹中的任何音效。