Last active
September 10, 2024 12:57
-
-
Save JavierZumer/9499bbb005f988a6a0b3948c7d92c754 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// HEADER | |
#pragma once | |
#include "CoreMinimal.h" | |
#include "Engine/DataAsset.h" | |
#include "EnemyData.generated.h" | |
////////////////////////////////////////////////////////////////////////// | |
// Enum To String (Source: https://forums.unrealengine.com/t/conversion-of-enum-to-string/337869/24) | |
// Usage Example: | |
// FString EnumString = EnumToString( EnumValue ); | |
////////////////////////////////////////////////////////////////////////// | |
template< typename T > | |
FString EnumToString(T EnumValue) | |
{ | |
static_assert(TIsUEnumClass< T >::Value, "'T' template parameter to EnumToString must be a valid UEnum"); | |
return StaticEnum< T >()->GetNameStringByValue((int64)EnumValue); | |
} | |
UENUM(BlueprintType) //Unused | |
enum class EnemyTypeEnum : uint8 | |
{ | |
Fire, | |
Ice, | |
Shadow, | |
Last UMETA(Hidden) | |
}; | |
UENUM(BlueprintType) //Unused | |
enum class EnemyRarityEnum : uint8 | |
{ | |
Common, | |
Rare, | |
Epic, | |
Last UMETA(Hidden) | |
}; | |
UENUM(BlueprintType) | |
enum class WwiseNumericalPropertyEnum : uint8 | |
{ | |
Volume, | |
Lowpass, | |
Highpass, | |
InitialDelay, | |
GameAuxSendVolume, | |
Pitch | |
}; | |
UENUM(BlueprintType) | |
enum class WwisePropertySetModeEnum : uint8 | |
{ | |
Override, | |
Additive | |
}; | |
UENUM(BlueprintType) | |
enum class RTPCTraits : uint8 | |
{ | |
Level, | |
Health, | |
Curse | |
}; | |
USTRUCT(BlueprintType) | |
struct FAddRTPCToObject //Unused | |
{ | |
GENERATED_BODY() | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (DisplayPriority = 0)) | |
RTPCTraits EnemyTrait = RTPCTraits::Level; | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (DisplayPriority = 2)) | |
WwiseNumericalPropertyEnum Property = WwiseNumericalPropertyEnum::Volume; | |
}; | |
USTRUCT(BlueprintType) | |
struct FWwiseNumericalProperty | |
{ | |
GENERATED_BODY() | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (DisplayPriority = 0)) | |
WwiseNumericalPropertyEnum Name = WwiseNumericalPropertyEnum::Volume; | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (DisplayPriority = 1)) | |
float Value = 0.0f; | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (DisplayPriority = 2)) | |
WwisePropertySetModeEnum Mode = WwisePropertySetModeEnum::Additive; | |
}; | |
USTRUCT(BlueprintType) | |
struct FEnemyDataAbstract | |
{ | |
GENERATED_BODY() | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (DisplayPriority = 0)) | |
FString Name = ""; | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (DisplayPriority = 1)) | |
bool UpdateWwiseStructure = true; | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (DisplayPriority = 2, EditCondition = "UpdateWwiseStructure", EditConditionHides)) | |
TArray<FWwiseNumericalProperty> WwiseOptionalProperties; | |
//UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (DisplayPriority = 2, EditCondition = "UpdateWwiseStructure", EditConditionHides, DisplayName = "Add RTPCs To Object")) | |
//TArray<FAddRTPCToObject> AddRTPCToObject; | |
}; | |
USTRUCT(BlueprintType) | |
struct FEnemyTypeStruct : public FEnemyDataAbstract | |
{ | |
GENERATED_BODY() | |
}; | |
USTRUCT(BlueprintType) | |
struct FEnemyRarityStruct : public FEnemyDataAbstract | |
{ | |
GENERATED_BODY() | |
UPROPERTY(EditAnywhere, BlueprintReadWrite) | |
uint8 BaseTreasureValue = 0; | |
}; | |
USTRUCT(BlueprintType) | |
struct FEnemyActionStruct : public FEnemyDataAbstract | |
{ | |
GENERATED_BODY() | |
}; | |
UCLASS() | |
class TESTINGWAAPI_API UEnemyData : public UDataAsset | |
{ | |
GENERATED_BODY() | |
public: | |
UFUNCTION(CallInEditor, Category = "WwiseData", meta = (FullyExpand = true)) | |
void UpdateWwiseData(); | |
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "WwiseData|Game Syncs", meta = (FullyExpand = true)) | |
FString GameSyncsSwitchesParentPath = "\\Switches\\Enemies"; | |
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "WwiseData|Game Syncs") | |
FString GameSyncsRTPC_ParentPath = "\\Game Parameters\\Default Work Unit\\Enemies"; | |
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "WwiseData|Main Switch Container") | |
FString MainSwitchContainerParentPath = "\\Actor-Mixer Hierarchy\\EnemySFXs"; | |
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "WwiseData|Main Switch Container") | |
FString MainSwitchContainerName = "Enemy_SFX"; | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "WwiseData|Main Switch Container") | |
TArray<FWwiseNumericalProperty> MainSwitchContainerOptionalProperties; | |
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "WwiseData|Audio Files") | |
FString AudioFilesSourceFolder = "C:\\Users\\dunbe\\Desktop\\Enemy_SFX_Renders"; | |
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "WwiseData|Audio Files") | |
FString AudioFilesOriginalsFolder = "EnemyAudio"; | |
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "WwiseData|Options", meta = (TitleProperty = "Name")) | |
bool SetEnemyLevelAsRTPC; | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "WwiseData|Options") | |
bool ClearPropertiesAfterSettingValues = true; | |
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "EnemyData", meta = (TitleProperty = "Name", DisplayName= "Enemy Elemental Type")) | |
TArray<FEnemyTypeStruct> EnemyTypesArray; | |
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "EnemyData", meta = (TitleProperty = "Name", DisplayName = "Enemy Rarity")) | |
TArray<FEnemyRarityStruct> EnemyRaritiesArray; | |
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "EnemyData", meta = (TitleProperty = "Name", DisplayName = "Enemy Actions")) | |
TArray<FEnemyActionStruct> EnemyActionsArray; | |
private: | |
FString EnemyTypeSwitchGroupPath; | |
TArray<FString> EnemyTypeSwitchesPaths; | |
FString EnemyRaritySwitchGroupPath; | |
TArray<FString> EnemyRaritySwitchesPaths; | |
FString EnemyActionsSwitchGroupPath; | |
TArray<FString> EnemyActionsSwitchesPaths; | |
void UpdateEnemyTypeSwitch(); | |
template<typename T> | |
TArray<FString> GetEnemyDataMembers(T structToProcess); | |
void CreateSwitchContainerStructure(); | |
TSharedRef<FJsonObject> CreateSwitchContainerArgument(FString parentPath, FString containerName); | |
FString CreateSwitchContainer(FString ParentPath, FString ContainerName, FString SwitchGroup, FString Default, FString& OutID); | |
FString CreateSoundSFX(FString ParentPath, FString SoundSFXName, FString& OutSoundFXID); | |
void ImportAudio(FString ParentSoundSFX, FString FileLocation, FString OriginalsLocation); | |
void CreateSwitchGroup(FString switchGroupName, TArray<FString> switchNames, | |
FString& OutSwitchGroupPath, TArray<FString>& OutSwitchesPathsArray); | |
TSharedRef<FJsonValueObject> CreateSwitchJSONObject(FString switchName); | |
void AddSwitchAssignment(FString SwitchContainerToCheck, FString SwitchContainerChildID, FString SwitchContainerToAssignPath, FString Switch); | |
void ApplyPropertiesToWwiseObjects(FWwiseNumericalProperty Property, FString ObjectPath); | |
FString GetWwisePropertyName(WwiseNumericalPropertyEnum Property); | |
void SetRTPC(FString TargetObject, FString RTPCPath, FString Property); | |
FString CreateRTPC(FString ParentPath, FString RTPCName, int Min, int Max, int Default); | |
float GetPropertyValue(FString ObjectPath, FString PropertyName); | |
bool SaveToAsset(UObject* ObjectToSave); | |
void ClearWwiseProperties(); | |
}; | |
//CPP | |
#include "EnemyData.h" | |
#include "../Plugins/Wwise/Source/AkAudio/Public/AkWaapiClient.h" | |
#include <AkWaapiUtils.h> | |
#include "UObject/SavePackage.h" | |
void UEnemyData::UpdateWwiseData() | |
{ | |
{ | |
//Begin undo group | |
TSharedRef<FJsonObject> args = MakeShared<FJsonObject>(); | |
TSharedRef<FJsonObject> options = MakeShared<FJsonObject>(); | |
TSharedPtr<FJsonObject> result; | |
FAkWaapiClient::Get()->Call(ak::wwise::core::undo::beginGroup, args, options, result); | |
} | |
//Create Switch GameSyncs | |
CreateSwitchGroup("EnemyType", GetEnemyDataMembers(EnemyTypesArray), EnemyTypeSwitchGroupPath, EnemyTypeSwitchesPaths); | |
CreateSwitchGroup("EnemyRarity", GetEnemyDataMembers(EnemyRaritiesArray), EnemyRaritySwitchGroupPath, EnemyRaritySwitchesPaths); | |
CreateSwitchGroup("EnemyAction", GetEnemyDataMembers(EnemyActionsArray), EnemyActionsSwitchGroupPath, EnemyActionsSwitchesPaths); | |
//Create Switch Containers Hierarchy | |
CreateSwitchContainerStructure(); | |
{ | |
//End undo group | |
TSharedRef<FJsonObject> args = MakeShared<FJsonObject>(); | |
args->SetStringField("displayName", "Create Switch Hierarchy and necessary game syncs"); | |
TSharedRef<FJsonObject> options = MakeShared<FJsonObject>(); | |
TSharedPtr<FJsonObject> result; | |
FAkWaapiClient::Get()->Call(ak::wwise::core::undo::endGroup, args, options, result); | |
} | |
if (ClearPropertiesAfterSettingValues) | |
{ | |
ClearWwiseProperties(); | |
} | |
//TODO: Is this added to source control? | |
SaveToAsset(this); | |
} | |
bool UEnemyData::SaveToAsset(UObject* ObjectToSave) | |
{ | |
UPackage* Package = ObjectToSave->GetPackage(); | |
const FString PackageName = Package->GetName(); | |
const FString PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension()); | |
FSavePackageArgs SaveArgs; | |
// This is specified just for example | |
{ | |
SaveArgs.TopLevelFlags = RF_Public | RF_Standalone; | |
SaveArgs.SaveFlags = SAVE_NoError; | |
} | |
const bool bSucceeded = UPackage::SavePackage(Package, nullptr, *PackageFileName, SaveArgs); | |
if (!bSucceeded) | |
{ | |
UE_LOG(LogTemp, Error, TEXT("Package '%s' wasn't saved!"), *PackageName) | |
return false; | |
} | |
UE_LOG(LogTemp, Warning, TEXT("Package '%s' was successfully saved"), *PackageName) | |
return true; | |
} | |
void UEnemyData::ClearWwiseProperties() | |
{ | |
MainSwitchContainerOptionalProperties.Empty(); | |
for (int i = 0; i < EnemyTypesArray.Num(); i++) | |
{ | |
EnemyTypesArray[i].WwiseOptionalProperties.Empty(); | |
} | |
for (int i = 0; i < EnemyRaritiesArray.Num(); i++) | |
{ | |
EnemyRaritiesArray[i].WwiseOptionalProperties.Empty(); | |
} | |
for (int i = 0; i < EnemyActionsArray.Num(); i++) | |
{ | |
EnemyActionsArray[i].WwiseOptionalProperties.Empty(); | |
} | |
/*for (auto enemyType : EnemyTypesArray) | |
{ | |
enemyType.WwiseOptionalProperties.Empty(); | |
} | |
for (auto enemyRarity : EnemyRaritiesArray) | |
{ | |
enemyRarity.WwiseOptionalProperties.Empty(); | |
} | |
for (auto enemyAction : EnemyActionsArray) | |
{ | |
enemyAction.WwiseOptionalProperties.Empty(); | |
}*/ | |
} | |
void UEnemyData::UpdateEnemyTypeSwitch() //UNUSED ATM | |
{ | |
//Create or Update switches for EnemyType | |
FString enumName = UEnum::GetValueAsString(EnemyTypeEnum::Last); | |
enumName = enumName.Left(enumName.Find("::")); | |
TArray<FString> switches; | |
for (int i = 0; i < (int)EnemyTypeEnum::Last; i++) | |
{ | |
EnemyTypeEnum type = (EnemyTypeEnum)i; | |
switches.Emplace(EnumToString(type)); | |
} | |
//CreateSwitchGroup(enumName, switches); | |
} | |
template<typename T> | |
TArray <FString> UEnemyData::GetEnemyDataMembers(T structToProcess) | |
{ | |
TArray<FString> SwitchNames; | |
for (auto type : structToProcess) | |
{ | |
if (!type.UpdateWwiseStructure) | |
{ | |
continue; | |
} | |
SwitchNames.Emplace(type.Name); | |
} | |
return SwitchNames; | |
} | |
void UEnemyData::CreateSwitchContainerStructure() | |
{ | |
FString SwitchContainerPath = ""; | |
//Create Parent Switch Container. I'm just passing the first switch member as the default but maybe we could ask the user somehow. | |
FString SwitchContainerID; | |
SwitchContainerPath = CreateSwitchContainer(MainSwitchContainerParentPath, MainSwitchContainerName, EnemyTypeSwitchGroupPath, EnemyTypeSwitchesPaths[0], SwitchContainerID); | |
if (MainSwitchContainerOptionalProperties.Num() > 0) | |
{ | |
for (auto property : MainSwitchContainerOptionalProperties) | |
{ | |
ApplyPropertiesToWwiseObjects(property, SwitchContainerPath); | |
} | |
} | |
if (SetEnemyLevelAsRTPC) | |
{ | |
auto rtpcPath = CreateRTPC(GameSyncsRTPC_ParentPath, "EnemyXPLevel", 1, 20, 1); | |
SetRTPC(SwitchContainerPath, rtpcPath, "Volume"); | |
} | |
{ | |
//Create children switch containers. We assume at least one enemy type. | |
for (int i = 0; i < EnemyTypesArray.Num(); i++) | |
{ | |
if (!EnemyTypesArray[i].UpdateWwiseStructure) | |
{ | |
continue; | |
} | |
//We create a switch container for each enemy type. | |
FString switchTypeID; | |
auto switchTypePath = CreateSwitchContainer(SwitchContainerPath, EnemyTypesArray[i].Name, EnemyRaritySwitchGroupPath, EnemyRaritySwitchesPaths[0], switchTypeID); | |
if (EnemyTypesArray[i].WwiseOptionalProperties.Num() > 0) | |
{ | |
for (auto property : EnemyTypesArray[i].WwiseOptionalProperties) | |
{ | |
ApplyPropertiesToWwiseObjects(property, switchTypePath); | |
} | |
} | |
auto AudioFileSufixType = MainSwitchContainerName; | |
//Add switch assignments based on enemy type | |
AddSwitchAssignment(SwitchContainerPath, switchTypeID, switchTypePath, EnemyTypeSwitchesPaths[i]); | |
AudioFileSufixType = MainSwitchContainerName + "_" + EnemyTypesArray[i].Name; | |
for (int j = 0; j < EnemyRaritiesArray.Num(); j++) | |
{ | |
if (!EnemyRaritiesArray[j].UpdateWwiseStructure) | |
{ | |
continue; | |
} | |
auto AudioFileSufixRarity = AudioFileSufixType; | |
//We create a switch container for each enemy rarity | |
FString switchRarityID; | |
auto switchRarityPath = CreateSwitchContainer(switchTypePath, EnemyRaritiesArray[j].Name, EnemyActionsSwitchGroupPath, EnemyActionsSwitchesPaths[0], switchRarityID); | |
if (EnemyRaritiesArray[j].WwiseOptionalProperties.Num() > 0) | |
{ | |
for (auto property : EnemyRaritiesArray[j].WwiseOptionalProperties) | |
{ | |
ApplyPropertiesToWwiseObjects(property, switchRarityPath); | |
} | |
} | |
//Add switch assignments based on enemy rarity | |
AddSwitchAssignment(switchTypePath, switchRarityID, switchRarityPath, EnemyRaritySwitchesPaths[j]); | |
AudioFileSufixRarity = AudioFileSufixType + "_" + EnemyRaritiesArray[j].Name; | |
for (int k = 0; k < EnemyActionsArray.Num(); k++) | |
{ | |
if (!EnemyActionsArray[k].UpdateWwiseStructure) | |
{ | |
continue; | |
} | |
{ | |
//We create the Sound SFX Objects now: | |
FString SoundFXObjectID; | |
auto SoundSFXObjectPath = CreateSoundSFX(switchRarityPath, EnemyActionsArray[k].Name, SoundFXObjectID); | |
if (EnemyActionsArray[k].WwiseOptionalProperties.Num() > 0) | |
{ | |
for (auto property : EnemyActionsArray[k].WwiseOptionalProperties) | |
{ | |
ApplyPropertiesToWwiseObjects(property, SoundSFXObjectPath); | |
} | |
} | |
//Add assignments to Sound SFXs based on Actions | |
AddSwitchAssignment(switchRarityPath, SoundFXObjectID, SoundSFXObjectPath, EnemyActionsSwitchesPaths[k]); | |
auto AudioFileSufixAction = AudioFileSufixRarity + "_" + EnemyActionsArray[k].Name; | |
ImportAudio(SoundSFXObjectPath, AudioFilesSourceFolder + "\\" + AudioFileSufixAction + ".wav", AudioFilesOriginalsFolder); | |
} | |
} | |
} | |
} | |
} | |
} | |
FString UEnemyData::CreateSoundSFX(FString ParentPath, FString SoundSFXName, FString& OutSoundFXID) | |
{ | |
FString SoundSFXPath = ""; | |
{ | |
TSharedRef<FJsonObject> args = MakeShared<FJsonObject>(); | |
//Parent Object | |
TSharedRef<FJsonObject> parentObj = MakeShareable(new FJsonObject()); | |
parentObj->SetStringField("object", ParentPath); | |
//Sound SFX Settings | |
TSharedRef<FJsonObject> soundSFX = MakeShareable(new FJsonObject()); | |
soundSFX->SetStringField("type", "Sound"); | |
soundSFX->SetStringField("name", SoundSFXName); | |
TSharedRef<FJsonValueObject> soundSFXValueObject = MakeShareable(new FJsonValueObject(soundSFX)); | |
//Sound SFXs Container Array | |
TArray<TSharedPtr<FJsonValue>> soundSFXContainerArray; | |
soundSFXContainerArray.Add(soundSFXValueObject); | |
parentObj->SetArrayField("children", soundSFXContainerArray); | |
//Workunits array | |
TSharedRef<FJsonValueObject> ParentObjectValues = MakeShareable(new FJsonValueObject(parentObj)); | |
TArray<TSharedPtr<FJsonValue>> argsArray; | |
argsArray.Add(ParentObjectValues); | |
args->SetArrayField("objects", argsArray); | |
args->SetStringField("onNameConflict", "merge"); | |
args->SetStringField("listMode", "append"); | |
TSharedRef<FJsonObject> options = MakeShared<FJsonObject>(); | |
TArray<TSharedPtr<FJsonValue>> optionsArray {MakeShareable(new FJsonValueString("path")), MakeShareable(new FJsonValueString("id"))}; | |
options->SetArrayField("return", optionsArray); | |
TSharedPtr<FJsonObject> result; | |
if (FAkWaapiClient::Get()->Call(ak::wwise::core::object::set, args, options, result)) | |
{ | |
auto resultObjs = result->GetArrayField("objects"); | |
auto childrenObj = resultObjs[0]->AsObject()->GetArrayField("children"); | |
SoundSFXPath = childrenObj[0]->AsObject()->GetStringField("path"); | |
OutSoundFXID = childrenObj[0]->AsObject()->GetStringField("id"); | |
} | |
} | |
return SoundSFXPath; | |
} | |
void UEnemyData::ImportAudio(FString ParentSoundSFX, FString FileLocation, FString OriginalsLocation) | |
{ | |
TSharedRef<FJsonObject> args = MakeShared<FJsonObject>(); | |
//Sound SFX | |
TSharedPtr<FJsonObject> SoundSFX = MakeShared<FJsonObject>(); | |
SoundSFX->SetStringField("object", ParentSoundSFX); | |
//AudioFileSource Object | |
TSharedPtr<FJsonObject> AudioFileSource = MakeShared<FJsonObject>(); | |
AudioFileSource->SetStringField("type", "AudioFileSource"); | |
AudioFileSource->SetStringField("name", "mySourceOne"); //How should I call this? Does it matter? How can I call it like the audio file name? | |
//Files to import | |
TSharedPtr<FJsonObject> files = MakeShared<FJsonObject>(); | |
//File One | |
TSharedPtr<FJsonObject> fileOne = MakeShared<FJsonObject>(); | |
fileOne->SetStringField("audioFile", FileLocation); | |
fileOne->SetStringField("originalsSubFolder", OriginalsLocation); | |
TSharedRef<FJsonValueObject> fileOneValueObject = MakeShareable(new FJsonValueObject(fileOne)); | |
//Make Files Array | |
TArray<TSharedPtr<FJsonValue>> filesArray; | |
filesArray.Add(fileOneValueObject); | |
files->SetArrayField("files", filesArray); | |
AudioFileSource->SetObjectField("import", files); | |
TSharedRef<FJsonValueObject> sourceValueObject = MakeShareable(new FJsonValueObject(AudioFileSource)); | |
//Make Sources Array | |
TArray<TSharedPtr<FJsonValue>> sourcesArray; | |
sourcesArray.Add(sourceValueObject); | |
SoundSFX->SetArrayField("children", sourcesArray); | |
TSharedRef<FJsonValueObject> soundValueObject = MakeShareable(new FJsonValueObject(SoundSFX)); | |
TArray<TSharedPtr<FJsonValue>> argsArray; | |
argsArray.Add(soundValueObject); | |
args->SetArrayField("objects", argsArray); | |
args->SetStringField("onNameConflict", "merge"); | |
args->SetStringField("listMode", "append"); | |
TSharedRef<FJsonObject> options = MakeShared<FJsonObject>(); | |
TSharedPtr<FJsonObject> result; | |
FAkWaapiClient::Get()->Call(ak::wwise::core::object::set, args, options, result); | |
} | |
FString UEnemyData::CreateSwitchContainer(FString ParentPath, FString ContainerName, FString SwitchGroup, FString Default, FString& OutID) | |
{ | |
FString SwitchContainerPath = ""; | |
{ | |
//Create Switch Container | |
auto args = CreateSwitchContainerArgument(ParentPath, ContainerName); | |
args->SetStringField("onNameConflict", "merge"); | |
args->SetStringField("listMode", "append"); | |
TSharedRef<FJsonObject> options = MakeShared<FJsonObject>(); | |
TArray<TSharedPtr<FJsonValue>> optionsArray {MakeShareable(new FJsonValueString("path")), MakeShareable(new FJsonValueString("id"))}; | |
options->SetArrayField("return", optionsArray); | |
TSharedPtr<FJsonObject> result; | |
if (FAkWaapiClient::Get()->Call(ak::wwise::core::object::set, args, options, result)) | |
{ | |
auto resultObjs = result->GetArrayField("objects"); | |
auto childrenObj = resultObjs[0]->AsObject()->GetArrayField("children"); | |
SwitchContainerPath = childrenObj[0]->AsObject()->GetStringField("path"); | |
OutID = childrenObj[0]->AsObject()->GetStringField("id"); | |
} | |
} | |
{ | |
//Set Switch Group Reference | |
TSharedRef<FJsonObject> args = MakeShared<FJsonObject>(); | |
args->SetStringField("object", SwitchContainerPath); | |
args->SetStringField("reference", "SwitchGroupOrStateGroup"); | |
args->SetStringField("value", SwitchGroup); | |
TSharedRef<FJsonObject> options = MakeShareable(new FJsonObject()); | |
TSharedPtr<FJsonObject> result; | |
FAkWaapiClient::Get()->Call(ak::wwise::core::object::setReference, args, options, result); | |
} | |
{ | |
TSharedRef<FJsonObject> args = MakeShared<FJsonObject>(); | |
args->SetStringField("object", SwitchContainerPath); | |
args->SetStringField("reference", "DefaultSwitchOrState"); | |
args->SetStringField("value", Default); | |
TSharedRef<FJsonObject> options = MakeShareable(new FJsonObject()); | |
TSharedPtr<FJsonObject> result; | |
FAkWaapiClient::Get()->Call(ak::wwise::core::object::setReference, args, options, result); | |
} | |
return SwitchContainerPath; | |
} | |
TSharedRef<FJsonObject> UEnemyData::CreateSwitchContainerArgument(FString parentPath, FString containerName) | |
{ | |
TSharedRef<FJsonObject> args = MakeShared<FJsonObject>(); | |
//Parent WorkUnit | |
TSharedRef<FJsonObject> workUnit = MakeShareable(new FJsonObject()); | |
workUnit->SetStringField("object", parentPath); | |
//Switch Container Settings | |
TSharedRef<FJsonObject> switchContainer = MakeShareable(new FJsonObject()); | |
switchContainer->SetStringField("type", "SwitchContainer"); | |
switchContainer->SetStringField("name", containerName); | |
TSharedRef<FJsonValueObject> switchContainerValueObject = MakeShareable(new FJsonValueObject(switchContainer)); | |
//Switch Container Array | |
TArray<TSharedPtr<FJsonValue>> switchContainerArray; | |
switchContainerArray.Add(switchContainerValueObject); | |
workUnit->SetArrayField("children", switchContainerArray); | |
//Workunits array | |
TSharedRef<FJsonValueObject> ObjectValues = MakeShareable(new FJsonValueObject(workUnit)); | |
TArray<TSharedPtr<FJsonValue>> argsArray; | |
argsArray.Add(ObjectValues); | |
args->SetArrayField("objects", argsArray); | |
return args; | |
} | |
void UEnemyData::CreateSwitchGroup(FString switchGroupName, TArray<FString> switchNames, | |
FString& OutSwitchGroupPath, TArray<FString>& OutSwitchesPathsArray) | |
{ | |
TSharedRef<FJsonObject> args = MakeShared<FJsonObject>(); | |
//Enemy Switches Workunit | |
TSharedRef<FJsonObject> workUnit = MakeShareable(new FJsonObject()); | |
workUnit->SetStringField("object", GameSyncsSwitchesParentPath); | |
//Switch Group | |
TSharedRef<FJsonObject> switchGroup = MakeShareable(new FJsonObject()); | |
switchGroup->SetStringField("type", "SwitchGroup"); | |
switchGroup->SetStringField("name", switchGroupName); | |
//Switch Group Children Array | |
TArray<TSharedPtr<FJsonValue>> switchGroupChildrenArray; | |
for (auto switchName : switchNames) | |
{ | |
switchGroupChildrenArray.Add(CreateSwitchJSONObject(switchName)); | |
} | |
switchGroup->SetArrayField("children", switchGroupChildrenArray); | |
TSharedRef<FJsonValueObject> switchGroupValueObject = MakeShareable(new FJsonValueObject(switchGroup)); | |
//Switch Group Array | |
TArray<TSharedPtr<FJsonValue>> switchGroupArray; | |
switchGroupArray.Add(switchGroupValueObject); | |
workUnit->SetArrayField("children", switchGroupArray); | |
TSharedRef<FJsonValueObject> ObjectValues = MakeShareable(new FJsonValueObject(workUnit)); | |
TArray<TSharedPtr<FJsonValue>> argsArray; | |
argsArray.Add(ObjectValues); | |
args->SetArrayField("objects", argsArray); | |
args->SetStringField("onNameConflict", "merge"); | |
//Since the above is "merge" it doesn't destroy switches that are already there which is necessary | |
//so we don't lose switch association that were already there. The downside is that if we remove | |
//members from an enum, these won't be remove from the corresponding Wwise switch but I can live | |
//with that. | |
args->SetStringField("listMode", "append"); | |
TSharedRef<FJsonObject> options = MakeShared<FJsonObject>(); | |
TArray<TSharedPtr<FJsonValue>> optionsArray {MakeShareable(new FJsonValueString("path"))}; | |
options->SetArrayField("return", optionsArray); | |
TSharedPtr<FJsonObject> result; | |
FAkWaapiClient::Get()->Call(ak::wwise::core::object::set, args, options, result); | |
if (result) | |
{ | |
auto resultObjs = result->GetArrayField("objects"); | |
auto switchContainer = resultObjs[0]->AsObject()->GetArrayField("children"); //Always use first object in array because we only modified one object (the work unit) | |
OutSwitchGroupPath = switchContainer[0]->AsObject()->GetStringField("path"); //Always use first object in array because we only created one switch container. | |
auto switches = switchContainer[0]->AsObject()->GetArrayField("children"); | |
for (auto switchChildOfMine : switches) | |
{ | |
OutSwitchesPathsArray.Emplace(switchChildOfMine->AsObject()->GetStringField("path")); | |
} | |
} | |
} | |
TSharedRef<FJsonValueObject> UEnemyData::CreateSwitchJSONObject(FString switchName) | |
{ | |
TSharedRef<FJsonObject> switchGroupChild = MakeShareable(new FJsonObject()); | |
switchGroupChild->SetStringField(WwiseWaapiHelper::TYPE, "Switch"); | |
switchGroupChild->SetStringField(WwiseWaapiHelper::NAME, switchName); | |
return MakeShareable(new FJsonValueObject(switchGroupChild)); | |
} | |
void UEnemyData::AddSwitchAssignment(FString SwitchContainerToCheck, FString SwitchContainerChildID, FString SwitchContainerToAssignPath, FString Switch) | |
{ | |
bool MakeAssignments = true; | |
{ | |
TSharedRef<FJsonObject> args = MakeShared<FJsonObject>(); | |
args->SetStringField("id", SwitchContainerToCheck); | |
TSharedRef<FJsonObject> options = MakeShared<FJsonObject>(); | |
TSharedPtr<FJsonObject> result; | |
FAkWaapiClient::Get()->Call(ak::wwise::core::switchContainer::getAssignments, args, options, result); | |
if (result) | |
{ | |
auto assignments = result->GetArrayField("return"); | |
for (auto assign : assignments) | |
{ | |
if (assign->AsObject()->GetStringField("child") == SwitchContainerChildID) | |
{ | |
MakeAssignments = false; | |
break; | |
} | |
} | |
} | |
} | |
if (MakeAssignments) | |
{ | |
TSharedRef<FJsonObject> args = MakeShared<FJsonObject>(); | |
args->SetStringField("child", SwitchContainerToAssignPath); | |
args->SetStringField("stateOrSwitch", Switch); | |
TSharedRef<FJsonObject> options = MakeShared<FJsonObject>(); | |
TSharedPtr<FJsonObject> result; | |
FAkWaapiClient::Get()->Call(ak::wwise::core::switchContainer::addAssignment, args, options, result); | |
} | |
} | |
void UEnemyData::ApplyPropertiesToWwiseObjects(FWwiseNumericalProperty Property, FString ObjectPath) | |
{ | |
TSharedRef<FJsonObject> args = MakeShared<FJsonObject>(); | |
args->SetStringField(WwiseWaapiHelper::OBJECT, ObjectPath); | |
auto propertyName = GetWwisePropertyName(Property.Name); | |
if (propertyName == "Invalid") | |
{ | |
return; //Nothing to set. | |
} | |
auto propertyTargerValue = 0.0f; | |
if (Property.Mode == WwisePropertySetModeEnum::Additive) | |
{ | |
propertyTargerValue = GetPropertyValue(ObjectPath, propertyName) + Property.Value; | |
} | |
else | |
{ | |
propertyTargerValue = Property.Value; | |
} | |
args->SetStringField(WwiseWaapiHelper::PROPERTY, propertyName); | |
if (Property.Name == WwiseNumericalPropertyEnum::Lowpass || | |
Property.Name == WwiseNumericalPropertyEnum::Highpass || | |
Property.Name == WwiseNumericalPropertyEnum::Pitch) | |
{ | |
args->SetNumberField(WwiseWaapiHelper::VALUE, (int)propertyTargerValue); | |
} | |
else | |
{ | |
args->SetNumberField(WwiseWaapiHelper::VALUE, propertyTargerValue); | |
} | |
TSharedRef<FJsonObject> options = MakeShared<FJsonObject>(); | |
TSharedPtr<FJsonObject> result; | |
FAkWaapiClient::Get()->Call(ak::wwise::core::object::setProperty, args, options, result); | |
} | |
FString UEnemyData::GetWwisePropertyName(WwiseNumericalPropertyEnum PropertyName) | |
{ | |
switch (PropertyName) | |
{ | |
case WwiseNumericalPropertyEnum::Volume: | |
return "Volume"; | |
case WwiseNumericalPropertyEnum::Lowpass: | |
return "Lowpass"; | |
case WwiseNumericalPropertyEnum::Highpass: | |
return "Highpass"; | |
case WwiseNumericalPropertyEnum::InitialDelay: | |
return "InitialDelay"; | |
case WwiseNumericalPropertyEnum::GameAuxSendVolume: | |
return "GameAuxSendVolume"; | |
case WwiseNumericalPropertyEnum::Pitch: | |
return "Pitch"; | |
default: | |
return "Invalid"; | |
} | |
} | |
void UEnemyData::SetRTPC(FString TargetObject, FString RTPCPath, FString Property) | |
{ | |
{ | |
//Check if any of the current RTPCs matches the one we are trying to set. | |
FString WAQLPath = "\"" + TargetObject + "\""; | |
TSharedRef<FJsonObject> args = MakeShared<FJsonObject>(); | |
args->SetStringField("waql", WAQLPath); | |
TSharedRef<FJsonObject> options = MakeShared<FJsonObject>(); | |
TArray<TSharedPtr<FJsonValue>> optionsArray {MakeShareable(new FJsonValueString("@RTPC.ControlInput.path")), MakeShareable(new FJsonValueString("@RTPC.PropertyName"))}; //Tricky | |
options->SetArrayField("return", optionsArray); | |
TSharedPtr<FJsonObject> result; | |
FAkWaapiClient::Get()->Call(ak::wwise::core::object::get, args, options, result); | |
if (result) | |
{ | |
TArray<TSharedPtr<FJsonValue>> resultArray = result->GetArrayField("return"); | |
auto rtpcs = resultArray[0]->AsObject()->GetArrayField("@RTPC.ControlInput.path"); | |
for (int i = 0; i < rtpcs.Num(); i++) | |
{ | |
FString rtpcName; | |
FString PropertyName; | |
rtpcs[i]->TryGetString(rtpcName); | |
if (rtpcName == RTPCPath) //We already have an RTPC using this game parameter so let's check if it uses the same property. | |
{ | |
auto PropertyNames = resultArray[0]->AsObject()->GetArrayField("@RTPC.PropertyName"); | |
PropertyNames[i]->TryGetString(PropertyName); | |
if (PropertyName == Property) | |
{ | |
return; //An RTPC that uses the same game parameter and propery already exists on this object so let's not set it again. | |
} | |
} | |
} | |
} | |
} | |
{ | |
//Set the RTPC on the target object | |
TSharedRef<FJsonObject> args = MakeShared<FJsonObject>(); | |
//Switch Container Object | |
TSharedRef<FJsonObject> SwitchContainer = MakeShareable(new FJsonObject()); | |
SwitchContainer->SetStringField("object", TargetObject); | |
//RTPC Object | |
TSharedRef<FJsonObject> RTPCObject = MakeShareable(new FJsonObject()); | |
RTPCObject->SetStringField("type", "RTPC"); | |
RTPCObject->SetStringField("name", ""); | |
//RTPC Curve Object | |
TSharedRef<FJsonObject> RTPCCurveObject = MakeShareable(new FJsonObject()); | |
RTPCCurveObject->SetStringField("type", "Curve"); | |
//RTPC Curve Points Objects | |
TSharedRef<FJsonObject> Point1 = MakeShareable(new FJsonObject()); | |
Point1->SetNumberField("x", 1); | |
Point1->SetNumberField("y", 0); | |
Point1->SetStringField("shape", "Linear"); | |
TSharedRef<FJsonValueObject> Point1ValueObject = MakeShareable(new FJsonValueObject(Point1)); | |
TSharedRef<FJsonObject> Point2 = MakeShareable(new FJsonObject()); | |
Point2->SetNumberField("x", 20); | |
Point2->SetNumberField("y", 6); | |
Point2->SetStringField("shape", "Linear"); | |
TSharedRef<FJsonValueObject> Point2ValueObject = MakeShareable(new FJsonValueObject(Point2)); | |
//RTPC Curve Points Array | |
TArray<TSharedPtr<FJsonValue>> RTPCCurvePoints; | |
RTPCCurvePoints.Add(Point1ValueObject); | |
RTPCCurvePoints.Add(Point2ValueObject); | |
//Set Curve Objects | |
RTPCCurveObject->SetArrayField("points", RTPCCurvePoints); | |
RTPCObject->SetObjectField("@Curve", RTPCCurveObject); | |
RTPCObject->SetStringField("@PropertyName", Property); | |
RTPCObject->SetStringField("@ControlInput", RTPCPath); | |
//Final assignments | |
TSharedRef<FJsonValueObject> RTPCObjectValue = MakeShareable(new FJsonValueObject(RTPCObject)); | |
TArray<TSharedPtr<FJsonValue>> RTPCArray; | |
RTPCArray.Add(RTPCObjectValue); | |
SwitchContainer->SetArrayField("@RTPC", RTPCArray); | |
TSharedRef<FJsonValueObject> ObjectValues = MakeShareable(new FJsonValueObject(SwitchContainer)); | |
TArray<TSharedPtr<FJsonValue>> argsArray; | |
argsArray.Add(ObjectValues); | |
args->SetArrayField("objects", argsArray); | |
args->SetStringField("onNameConflict", "merge"); | |
// This would add more and more copies of the RTPC to the object, that's way we check if it already exists at the start of this method. | |
args->SetStringField("listMode", "append"); | |
TSharedRef<FJsonObject> options = MakeShared<FJsonObject>(); | |
//TArray<TSharedPtr<FJsonValue>> optionsArray {MakeShareable(new FJsonValueString("id"))}; | |
//options->SetArrayField("return", optionsArray); | |
TSharedPtr<FJsonObject> result; | |
FAkWaapiClient::Get()->Call(ak::wwise::core::object::set, args, options, result); | |
} | |
} | |
FString UEnemyData::CreateRTPC(FString ParentPath, FString RTPCName, int Min, int Max, int Default) | |
{ | |
FString RTPCPatch = ""; | |
TSharedRef<FJsonObject> args = MakeShared<FJsonObject>(); | |
//Enemy Switches Workunit | |
TSharedRef<FJsonObject> workUnit = MakeShareable(new FJsonObject()); | |
workUnit->SetStringField("object", ParentPath); | |
//Switch Group | |
TSharedRef<FJsonObject> RTPCObject = MakeShareable(new FJsonObject()); | |
RTPCObject->SetStringField("type", "GameParameter"); | |
RTPCObject->SetStringField("name", RTPCName); | |
RTPCObject->SetNumberField("@Max", Max); | |
RTPCObject->SetNumberField("@Min", Min); | |
RTPCObject->SetNumberField("@InitialValue", Default); | |
TSharedRef<FJsonValueObject> RTPCObjectValue = MakeShareable(new FJsonValueObject(RTPCObject)); | |
//Switch Container Array | |
TArray<TSharedPtr<FJsonValue>> RTPCObjectsArray; | |
RTPCObjectsArray.Add(RTPCObjectValue); | |
workUnit->SetArrayField("children", RTPCObjectsArray); | |
//Workunits array | |
TSharedRef<FJsonValueObject> ObjectValues = MakeShareable(new FJsonValueObject(workUnit)); | |
TArray<TSharedPtr<FJsonValue>> argsArray; | |
argsArray.Add(ObjectValues); | |
args->SetArrayField("objects", argsArray); | |
args->SetStringField("onNameConflict", "merge"); | |
args->SetStringField("listMode", "append"); | |
TSharedRef<FJsonObject> options = MakeShared<FJsonObject>(); | |
TArray<TSharedPtr<FJsonValue>> optionsArray {MakeShareable(new FJsonValueString("path")), MakeShareable(new FJsonValueString("id"))}; | |
options->SetArrayField("return", optionsArray); | |
TSharedPtr<FJsonObject> result; | |
if (FAkWaapiClient::Get()->Call(ak::wwise::core::object::set, args, options, result)) | |
{ | |
auto resultObjs = result->GetArrayField("objects"); | |
auto childrenObj = resultObjs[0]->AsObject()->GetArrayField("children"); | |
RTPCPatch = childrenObj[0]->AsObject()->GetStringField("path"); | |
} | |
return RTPCPatch; | |
} | |
float UEnemyData::GetPropertyValue(FString ObjectPath, FString PropertyName) | |
{ | |
FString WAQLPath = "\"" + ObjectPath + "\""; | |
TSharedRef<FJsonObject> args = MakeShared<FJsonObject>(); | |
args->SetStringField("waql", WAQLPath); | |
TSharedRef<FJsonObject> options = MakeShared<FJsonObject>(); | |
TArray<TSharedPtr<FJsonValue>> optionsArray {MakeShareable(new FJsonValueString(PropertyName))}; | |
options->SetArrayField("return", optionsArray); | |
TSharedPtr<FJsonObject> result; | |
FAkWaapiClient::Get()->Call(ak::wwise::core::object::get, args, options, result); | |
if (result) | |
{ | |
TArray<TSharedPtr<FJsonValue>> resultArray = result->GetArrayField("return"); | |
return (float)resultArray[0]->AsObject()->GetNumberField(PropertyName); | |
} | |
return 0.0f; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment