本篇文章搬运自我自己的博客,原文链接: UE集成WWise:概念与代码分析 作者: ZhaLiPeng
WWise是Audiokinetic的跨平台音频引擎,可以与游戏引擎很好地进行交互,负责音频的同事可以只在WWise中处理音频,把游戏业务和音频的制作与管理分离,提供事件和参数给游戏引擎使用,实现与业务的解耦和对音频更精确的控制。 本篇文章主要介绍WWise与UE4的集成、远程构建、资源分析、文档收录,WWise与UE的控制交互以及Bank生成的代码分析。
集成至UE4
WWise是全平台支持的,对Linux/Lumin/PS4/Switch/XboxOne/Windows/Android/iOS/Mac都支持。 但是多数游戏不需要支持这么多平台,WWise链接库很大,所以我在官方版本支持的多平台基础上做了裁剪,去掉了以下平台的支持:
- Linux
- Lumin
- PS4
- Switch
- XboxOne
对Android和Windows平台做了以下裁剪:
- 移除arm64-v8a和android_x86/x86_64的链接库支持
- 移除Win32的所有链接库/移除vc140/vc150的支持
对iOS做了以下裁剪:
- 移除所有的iphonesimulator,节省空间2.31G
对Mac的支持:
目前的项目是不需要支持Mac的(使用iOS远程出包),但是为了避免想要在Mac上跑工程编译不过的问题,保留了Mac链接库和模块支持,保留它不会对Android/iOS的打包有任何影响。
链接库
我在裁剪版本中支持以下平台:
- Android_armabi_v7a
- iOS
- Mac
- Win vc160
每个平台均支持Debug
/Profile
/Release
的支持,分别对应UE的Debug
/Development
/Shipping
的Configuration配置。
在打包Android Development的配置下,包含WWise的链接库,APK增大约30M.
WWise版本的问题
Wwise版本为Wwise 2019.1.9.7221
在AkAudio_Android.build.cs
中对Android的的链接库支持在UE_4_25_OR_LATER
下路径错误。
原始路径:
Path.Combine(ThirdPartyFolder, "Android", "armeabi-v7a", akConfigurationDir, "lib")
实际的路径:
Path.Combine(ThirdPartyFolder, "Android_armeabi-v7a", akConfigurationDir, "lib")
远程构建iOS
在我之前的笔记中写到过,远程构建iOS实际就是要把文件上传的Mac上执行编译,但是这就有一个问题,如果需要参与编译的文件没有被上传到Mac上,就会出现错误,很不巧在WWise中就会出现这个问题,解决的办法自然是要把编译WWise依赖的文件给上传到Mac上。
因为UE使用RSync
来同步构建机和本地的文件传输,在我之前的文章UE4开发笔记:Mac/iOS篇#配置远程构建有讲到,可以创建<ProjectDir>/Build/Rsync/RsyncProject.txt
文件,来写入RSync的文件同步规则,把需要的文件上传到Mac中。
WWise需要的规则如下:
+ /Plugins/Wwise/ThirdParty/include/**
+ /Plugins/Wwise/ThirdParty/iOS/**
其实就是指定WWise的链接库和Include目录全上传到Mac上。
WWise资源
WWise在UE中有两种资源格式,一种是UAkAudioEvent
用来执行WWise中指定的Event,还有一种是UAkAudioBank
,用来记录Bank中包含哪些Event,用在生成时标记把属于相同Bank的Event打包到一起。
UAkAudioEvent
- AudioEvent
UAkAudioEvent
:主要作用是指定当前Event的Bank,它唯一的函数就是LoadBank
来加载当前Event所指定的Bank(看到有用到的地方就是获取它的名字),WWise集成到UE的插件也是通过拿到UAkAudioEvent
对象的名字来与UAkAudioBank
做绑定之后去WWise
端生成bnk等文件的,这也是要求UE中资源的命名要和WWise中Event的命名完全一致的原因。
UCLASS(meta=(BlueprintSpawnableComponent))
class AKAUDIO_API UAkAudioEvent : public UObject
{
GENERATED_UCLASS_BODY()
public:
/** Bank to which this event should be added. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bank")
class UAkAudioBank * RequiredBank;
/** Maximum attenuation radius for this event */
UPROPERTY(BlueprintReadOnly, Category="AkAudioEvent")
float MaxAttenuationRadius;
/** Whether this event is infinite (looping) or finite (duration parameters are valid) */
UPROPERTY(BlueprintReadOnly, Category = "AkAudioEvent")
bool IsInfinite;
/** Minimum duration */
UPROPERTY(BlueprintReadOnly, Category = "AkAudioEvent")
float MinimumDuration;
/** Maximum duration */
UPROPERTY(BlueprintReadOnly, Category = "AkAudioEvent")
float MaximumDuration;
#if CPP
/**
* Load the required bank.
*
* @return true if the bank was loaded, otherwise false
*/
bool LoadBank();
#endif
};
通过获取Event的名字再传递给更深层次的PostEvent:
int32 UAkGameplayStatics::PostEvent(
class UAkAudioEvent* AkEvent
, class AActor* Actor
, int32 CallbackMask
, const FOnAkPostEventCallback& PostEventCallback
, const TArray<FAkExternalSourceInfo>& ExternalSources
, bool bStopWhenAttachedToDestroyed
, FString EventName
)
{
if (AkEvent == NULL && EventName.IsEmpty())
{
UE_LOG(LogScript, Warning, TEXT("UAkGameplayStatics::PostEvent: No Event specified!"));
return AK_INVALID_PLAYING_ID;
}
if (Actor == NULL)
{
UE_LOG(LogScript, Warning, TEXT("UAkGameplayStatics::PostEvent: NULL Actor specified!"));
return AK_INVALID_PLAYING_ID;
}
AkDeviceAndWorld DeviceAndWorld(Actor);
if (DeviceAndWorld.IsValid())
{
AkCallbackType AkCallbackMask = AkCallbackTypeHelpers::GetCallbackMaskFromBlueprintMask(CallbackMask);
if (ExternalSources.Num() > 0)
{
FAkSDKExtrernalSourceArray SDKExternalSrcInfo(ExternalSources);
return DeviceAndWorld.AkAudioDevice->PostEvent(GET_AK_EVENT_NAME(AkEvent, EventName), Actor, PostEventCallback, AkCallbackMask, false, SDKExternalSrcInfo.ExternalSourceArray);
}
else
{
return DeviceAndWorld.AkAudioDevice->PostEvent(GET_AK_EVENT_NAME(AkEvent, EventName), Actor, PostEventCallback, AkCallbackMask);
}
}
return AK_INVALID_PLAYING_ID;
}
UAkAudioBank
UAkAudioBank
的作用是用来调用AkAudioDevice
来加载Bank,如果在UE中开启了AutoLoad,则在UObejct的PostLoad
中就会去执行加载。
官方的对于Sound