Files
2025-07-20 10:01:29 +08:00

369 lines
22 KiB
C#

using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
namespace EmeraldAI.Utility
{
[CustomEditor(typeof(EmeraldSoundProfile))]
[CanEditMultipleObjects]
public class EmeraldSoundProfileEditor : Editor
{
GUIStyle HelpButtonStyle;
GUIStyle FoldoutStyle;
Texture SoundsEditorIcon;
#region SerializedProperties
//Ints
SerializedProperty IdleSoundsSecondsMinProp, IdleSoundsSecondsMaxProp;
//Floats
SerializedProperty WalkFootstepVolumeProp, RunFootstepVolumeProp, BlockVolumeProp, AttackVolumeProp, DeathVolumeProp, IdleVolumeProp, InjuredVolumeProp, InjuredSoundOddsProp,
EquipVolumeProp, UnequipVolumeProp, WarningVolumeProp, RangedEquipVolumeProp, RangedUnequipVolumeProp;
//Objects
SerializedProperty SheatheWeaponProp, UnsheatheWeaponProp, RangedSheatheWeaponProp, RangedUnsheatheWeaponProp;
//Bools
SerializedProperty IdleSoundsFoldout, FootstepSoundsFoldout, InteractSoundsFoldout, EquipAndUnequipSoundsFoldout, AttackSoundsFoldout, InjuredSoundsFoldout, BlockSoundsFoldout, WarningSoundsFoldout, DeathSoundsFoldout;
//List
SerializedProperty AttackSoundsProp, InjuredSoundsProp, WarningSoundsProp, DeathSoundsProp, FootStepSoundsProp, IdleSoundsProp, BlockingSoundsProp;
//Reorderable List
ReorderableList InteractSoundsList;
#endregion
void OnEnable()
{
if (SoundsEditorIcon == null) SoundsEditorIcon = Resources.Load("Editor Icons/EmeraldSounds") as Texture;
InitializeProperties();
}
void InitializeProperties()
{
//Ints
IdleSoundsSecondsMinProp = serializedObject.FindProperty("IdleSoundsSecondsMin");
IdleSoundsSecondsMaxProp = serializedObject.FindProperty("IdleSoundsSecondsMax");
//Floats
WalkFootstepVolumeProp = serializedObject.FindProperty("WalkFootstepVolume");
RunFootstepVolumeProp = serializedObject.FindProperty("RunFootstepVolume");
BlockVolumeProp = serializedObject.FindProperty("BlockVolume");
InjuredVolumeProp = serializedObject.FindProperty("InjuredVolume");
InjuredSoundOddsProp = serializedObject.FindProperty("InjuredSoundOdds");
AttackVolumeProp = serializedObject.FindProperty("AttackVolume");
DeathVolumeProp = serializedObject.FindProperty("DeathVolume");
EquipVolumeProp = serializedObject.FindProperty("EquipVolume");
UnequipVolumeProp = serializedObject.FindProperty("UnequipVolume");
RangedEquipVolumeProp = serializedObject.FindProperty("RangedEquipVolume");
RangedUnequipVolumeProp = serializedObject.FindProperty("RangedUnequipVolume");
IdleVolumeProp = serializedObject.FindProperty("IdleVolume");
WarningVolumeProp = serializedObject.FindProperty("WarningVolume");
//Bools
IdleSoundsFoldout = serializedObject.FindProperty("IdleSoundsFoldout");
FootstepSoundsFoldout = serializedObject.FindProperty("FootstepSoundsFoldout");
InteractSoundsFoldout = serializedObject.FindProperty("InteractSoundsFoldout");
EquipAndUnequipSoundsFoldout = serializedObject.FindProperty("EquipAndUnequipSoundsFoldout");
AttackSoundsFoldout = serializedObject.FindProperty("AttackSoundsFoldout");
InjuredSoundsFoldout = serializedObject.FindProperty("InjuredSoundsFoldout");
BlockSoundsFoldout = serializedObject.FindProperty("BlockSoundsFoldout");
WarningSoundsFoldout = serializedObject.FindProperty("WarningSoundsFoldout");
DeathSoundsFoldout = serializedObject.FindProperty("DeathSoundsFoldout");
//Objects
SheatheWeaponProp = serializedObject.FindProperty("SheatheWeapon");
UnsheatheWeaponProp = serializedObject.FindProperty("UnsheatheWeapon");
RangedSheatheWeaponProp = serializedObject.FindProperty("RangedSheatheWeapon");
RangedUnsheatheWeaponProp = serializedObject.FindProperty("RangedUnsheatheWeapon");
//Lists
AttackSoundsProp = serializedObject.FindProperty("AttackSounds");
InjuredSoundsProp = serializedObject.FindProperty("InjuredSounds");
WarningSoundsProp = serializedObject.FindProperty("WarningSounds");
DeathSoundsProp = serializedObject.FindProperty("DeathSounds");
FootStepSoundsProp = serializedObject.FindProperty("FootStepSounds");
IdleSoundsProp = serializedObject.FindProperty("IdleSounds");
BlockingSoundsProp = serializedObject.FindProperty("BlockingSounds");
InitializeInteractSoundsList();
}
void InitializeInteractSoundsList()
{
//Interact Sounds
InteractSoundsList = new ReorderableList(serializedObject, serializedObject.FindProperty("InteractSounds"), true, true, true, true);
InteractSoundsList.drawElementCallback =
(Rect rect, int index, bool isActive, bool isFocused) =>
{
EditorGUI.BeginChangeCheck();
var element = InteractSoundsList.serializedProperty.GetArrayElementAtIndex(index);
EditorGUI.PropertyField(new Rect(rect.x + 60, rect.y, rect.width - 120, EditorGUIUtility.singleLineHeight), element.FindPropertyRelative("SoundEffectClip"), GUIContent.none);
EditorGUI.PropertyField(new Rect(rect.x, rect.y, 50, EditorGUIUtility.singleLineHeight), element.FindPropertyRelative("SoundEffectID"), GUIContent.none);
};
InteractSoundsList.drawHeaderCallback = rect =>
{
EditorGUI.LabelField(rect, " ID " + " Interact Sound Clip", EditorStyles.boldLabel);
};
}
public override void OnInspectorGUI()
{
FoldoutStyle = CustomEditorProperties.UpdateEditorStyles();
EmeraldSoundProfile self = (EmeraldSoundProfile)target;
serializedObject.Update();
CustomEditorProperties.BeginScriptHeader("Sound Profile", SoundsEditorIcon);
EditorGUILayout.Space();
IdleSounds(self);
EditorGUILayout.Space();
FootstepSounds(self);
EditorGUILayout.Space();
InteractSounds(self);
EditorGUILayout.Space();
EquipAndUnequipSounds(self);
EditorGUILayout.Space();
AttackSounds(self);
EditorGUILayout.Space();
InjuredSounds(self);
EditorGUILayout.Space();
BlockSounds(self);
EditorGUILayout.Space();
DeathSounds(self);
EditorGUILayout.Space();
WarningSounds(self);
EditorGUILayout.Space();
serializedObject.ApplyModifiedProperties();
CustomEditorProperties.EndScriptHeader();
}
void IdleSounds(EmeraldSoundProfile self)
{
IdleSoundsFoldout.boolValue = EditorGUILayout.Foldout(IdleSoundsFoldout.boolValue, "Idle Sounds", true, FoldoutStyle);
if (IdleSoundsFoldout.boolValue)
{
CustomEditorProperties.BeginFoldoutWindowBox();
CustomEditorProperties.TextTitleWithDescription("Idle Sounds", "Idle sounds will play randomly based on the Min and Max Idle Sound Seconds when not in combat.", true);
CustomEditorProperties.CustomFloatSlider(new Rect(), new GUIContent(), IdleVolumeProp, "Idle Volume", 0, 1);
CustomEditorProperties.CustomHelpLabelField("Controls the volume of idle sounds.", true);
CustomEditorProperties.CustomListPropertyField(IdleSoundsProp, "Idle Sounds", "Controls how many idle sounds this AI will use.", true);
if (self.IdleSounds.Count != 0)
{
EditorGUILayout.Space();
CustomEditorProperties.CustomIntField(new Rect(), new GUIContent(), IdleSoundsSecondsMinProp, "Min Idle Sound Seconds");
CustomEditorProperties.CustomHelpLabelField("Controls the minimum amount of seconds needed before playing a random idle sound from the list below. This amuont will be " +
"randomized with the Max Idle Sound Seconds.", true);
CustomEditorProperties.CustomIntField(new Rect(), new GUIContent(), IdleSoundsSecondsMaxProp, "Max Idle Sound Seconds");
CustomEditorProperties.CustomHelpLabelField("Controls the maximum amount of seconds needed before playing a random idle sound from the list below. This amuont will be " +
"randomized with the Min Idle Sound Seconds.", true);
}
CustomEditorProperties.EndFoldoutWindowBox();
}
}
void FootstepSounds(EmeraldSoundProfile self)
{
FootstepSoundsFoldout.boolValue = EditorGUILayout.Foldout(FootstepSoundsFoldout.boolValue, "Footstep Sounds", true, FoldoutStyle);
if (FootstepSoundsFoldout.boolValue)
{
CustomEditorProperties.BeginFoldoutWindowBox();
CustomEditorProperties.TextTitleWithDescription("Footstep Sounds", "Controls the sounds and settings an AI will use when playing footstep sounds. The must be added through Animation Events. See the button below for a tutorial.", true);
CustomEditorProperties.TutorialButton("Note: You will need to manually create a WalkFootstepSound and/or RunFootstepSound Animation Event to use this feature. " +
"For a tutorial on this, please press the 'See Tutorial' button below.", "https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-profiles-and-objects/sound-profile/setting-up-footstep-sounds");
CustomEditorProperties.CustomFloatSlider(new Rect(), new GUIContent(), WalkFootstepVolumeProp, "Walk Footsteps Volume", 0, 1);
CustomEditorProperties.CustomHelpLabelField("Controls the volume of walk footstep sounds.", false);
CustomEditorProperties.CustomFloatSlider(new Rect(), new GUIContent(), RunFootstepVolumeProp, "Run Footsteps Volume", 0, 1);
CustomEditorProperties.CustomHelpLabelField("Controls the volume of Run footstep sounds.", true);
EditorGUILayout.Space();
CustomEditorProperties.CustomListPropertyField(FootStepSoundsProp, "Footstep Sounds", "Controls how many footstep sounds this AI will use.", true);
CustomEditorProperties.EndFoldoutWindowBox();
}
}
void InteractSounds(EmeraldSoundProfile self)
{
InteractSoundsFoldout.boolValue = EditorGUILayout.Foldout(InteractSoundsFoldout.boolValue, "Interact Sounds", true, FoldoutStyle);
if (InteractSoundsFoldout.boolValue)
{
CustomEditorProperties.BeginFoldoutWindowBox();
CustomEditorProperties.TextTitleWithDescription("Interact Sounds", "Various sounds that can be called through Animation Events or programmatically using the sound effect ID. " +
"This can be useful for quests, dialouge, animation sound effects, etc.", true);
InteractSoundsList.DoLayoutList();
EditorGUILayout.Space();
CustomEditorProperties.EndFoldoutWindowBox();
}
}
void EquipAndUnequipSounds(EmeraldSoundProfile self)
{
EquipAndUnequipSoundsFoldout.boolValue = EditorGUILayout.Foldout(EquipAndUnequipSoundsFoldout.boolValue, "Equip & Unequip Sounds", true, FoldoutStyle);
if (EquipAndUnequipSoundsFoldout.boolValue)
{
CustomEditorProperties.BeginFoldoutWindowBox();
CustomEditorProperties.TextTitleWithDescription("Equip & Unequip Sounds", "Controls the sounds that play when the AI is equiping or unequiping their weapon.", true);
CustomEditorProperties.TutorialButton("Note: This is automatically called through the EquipWeapon and UnequipWeapon Animation Events. For a tutorial on this, please press the 'See Tutorial' button below.",
"https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-optional/items-component/creating-equippable-weapons");
EditorGUILayout.Space();
CustomEditorProperties.CustomFloatSlider(new Rect(), new GUIContent(), EquipVolumeProp, "Type 1 Equip Volume", 0, 1);
CustomEditorProperties.CustomHelpLabelField("Controls the volume of the Equip Sound.", false);
CustomEditorProperties.CustomObjectField(new Rect(), new GUIContent(), UnsheatheWeaponProp, "Type 1 Equip Sound", typeof(AudioClip), false);
CustomEditorProperties.CustomHelpLabelField("The sound that plays when this AI is equiping their weapon.", true);
EditorGUILayout.Space();
CustomEditorProperties.CustomFloatSlider(new Rect(), new GUIContent(), UnequipVolumeProp, "Type 1 Unequip Volume", 0, 1);
CustomEditorProperties.CustomHelpLabelField("Controls the volume of the Unequip Sound.", false);
CustomEditorProperties.CustomObjectField(new Rect(), new GUIContent(), SheatheWeaponProp, "Type 1 Unequip Sound", typeof(AudioClip), false);
CustomEditorProperties.CustomHelpLabelField("The sound that plays when this AI is unequiping their weapon.", true);
EditorGUILayout.Space();
CustomEditorProperties.CustomFloatSlider(new Rect(), new GUIContent(), RangedEquipVolumeProp, "Type 2 Equip Volume", 0, 1);
CustomEditorProperties.CustomHelpLabelField("Controls the volume of the Equip Sound.", false);
CustomEditorProperties.CustomObjectField(new Rect(), new GUIContent(), RangedUnsheatheWeaponProp, "Type 2 Equip Sound", typeof(AudioClip), false);
CustomEditorProperties.CustomHelpLabelField("The sound that plays when this AI is equiping their weapon.", true);
EditorGUILayout.Space();
CustomEditorProperties.CustomFloatSlider(new Rect(), new GUIContent(), RangedUnequipVolumeProp, "Type 2 Unequip Volume", 0, 1);
CustomEditorProperties.CustomHelpLabelField("Controls the volume of the Unequip Sound.", false);
CustomEditorProperties.CustomObjectField(new Rect(), new GUIContent(), RangedSheatheWeaponProp, "Type 2 Unequip Sound", typeof(AudioClip), false);
CustomEditorProperties.CustomHelpLabelField("The sound that plays when this AI is unequiping their weapon.", true);
EditorGUILayout.Space();
CustomEditorProperties.EndFoldoutWindowBox();
}
}
void AttackSounds(EmeraldSoundProfile self)
{
AttackSoundsFoldout.boolValue = EditorGUILayout.Foldout(AttackSoundsFoldout.boolValue, "Attack Sounds", true, FoldoutStyle);
if (AttackSoundsFoldout.boolValue)
{
CustomEditorProperties.BeginFoldoutWindowBox();
CustomEditorProperties.TextTitleWithDescription("Attack Sounds", "Controls the sounds (roars, grunts, shouts, growls, etc) that play when an AI triggers an attack. These must be added through Animation Events. See the button below for a tutorial.", true);
CustomEditorProperties.TutorialButton("Note: Attack Sounds are used through Animation Events. For a tutorial on this, please press the 'See Tutorial' button below.",
"https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-profiles-and-objects/sound-profile/setting-up-attack-sounds");
CustomEditorProperties.CustomFloatSlider(new Rect(), new GUIContent(), AttackVolumeProp, "Attack Volume", 0, 1);
CustomEditorProperties.CustomHelpLabelField("Controls the volume of Attack Sounds.", true);
CustomEditorProperties.CustomListPropertyField(AttackSoundsProp, "Attack Sounds", "Controls how many attack sounds this AI will use.", true);
CustomEditorProperties.EndFoldoutWindowBox();
}
}
void InjuredSounds(EmeraldSoundProfile self)
{
InjuredSoundsFoldout.boolValue = EditorGUILayout.Foldout(InjuredSoundsFoldout.boolValue, "Injured Sounds", true, FoldoutStyle);
if (InjuredSoundsFoldout.boolValue)
{
CustomEditorProperties.BeginFoldoutWindowBox();
CustomEditorProperties.TextTitleWithDescription("Injured Sounds", "Controls the sounds that play when an AI receives damage (grunts, growls, etc). These are different than impact sounds (which are played through an Ability Object).", true);
CustomEditorProperties.CustomIntSlider(new Rect(), new GUIContent(), InjuredSoundOddsProp, "Injured Sound Odds", 1, 100);
CustomEditorProperties.CustomHelpLabelField("Controls the odds of Injured Sounds playing (as they may not need to be played every time an AI is injured).", true);
CustomEditorProperties.CustomFloatSlider(new Rect(), new GUIContent(), InjuredVolumeProp, "Injured Volume", 0, 1);
CustomEditorProperties.CustomHelpLabelField("Controls the volume of Injured Sounds.", true);
CustomEditorProperties.CustomListPropertyField(InjuredSoundsProp, "Injured Sounds", "Controls how many injured sounds this AI will use.", true);
CustomEditorProperties.EndFoldoutWindowBox();
}
}
void BlockSounds(EmeraldSoundProfile self)
{
BlockSoundsFoldout.boolValue = EditorGUILayout.Foldout(BlockSoundsFoldout.boolValue, "Block Sounds", true, FoldoutStyle);
if (BlockSoundsFoldout.boolValue)
{
CustomEditorProperties.BeginFoldoutWindowBox();
CustomEditorProperties.TextTitleWithDescription("Block Sounds", "Controls the sounds that play when an AI receives damage while blocking.", true);
CustomEditorProperties.CustomFloatSlider(new Rect(), new GUIContent(), BlockVolumeProp, "Block Volume", 0, 1);
CustomEditorProperties.CustomHelpLabelField("Controls the volume of Block Sounds.", true);
CustomEditorProperties.CustomListPropertyField(BlockingSoundsProp, "Block Sounds", "Controls the sounds that happens when this AI is hit while blocking. " +
"Note: Blocking must be enabled with the proper animations setup in order for this to work.", true);
CustomEditorProperties.EndFoldoutWindowBox();
}
}
void WarningSounds(EmeraldSoundProfile self)
{
WarningSoundsFoldout.boolValue = EditorGUILayout.Foldout(WarningSoundsFoldout.boolValue, "Warning Sounds", true, FoldoutStyle);
if (WarningSoundsFoldout.boolValue)
{
CustomEditorProperties.BeginFoldoutWindowBox();
CustomEditorProperties.TextTitleWithDescription("Warning Sounds", "Controls the sounds that play when an AI receives damage.", true);
CustomEditorProperties.TutorialButton("Note: Warning Sounds are only used by Cautious Behavior Types with a Confidence greater than Coward and are done through Animation Events. For a tutorial on this, " +
"please press the 'See Tutorial' button below.", "https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-profiles-and-objects/sound-profile/setting-up-warning-sounds");
CustomEditorProperties.CustomFloatSlider(new Rect(), new GUIContent(), WarningVolumeProp, "Warning Volume", 0, 1);
CustomEditorProperties.CustomHelpLabelField("Controls the volume of Warning Sounds.", true);
CustomEditorProperties.CustomListPropertyField(WarningSoundsProp, "Warning Sounds", "Controls how many warning sounds this AI will use.", true);
CustomEditorProperties.EndFoldoutWindowBox();
}
}
void DeathSounds(EmeraldSoundProfile self)
{
DeathSoundsFoldout.boolValue = EditorGUILayout.Foldout(DeathSoundsFoldout.boolValue, "Death Sounds", true, FoldoutStyle);
if (DeathSoundsFoldout.boolValue)
{
CustomEditorProperties.BeginFoldoutWindowBox();
CustomEditorProperties.TextTitleWithDescription("Death Sounds", "Controls the sounds that play when an AI dies.", true);
CustomEditorProperties.TutorialButton("Note: Death Sounds are used through Animation Events. For a tutorial on this, please press the 'See Tutorial' button below.",
"https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-profiles-and-objects/sound-profile/setting-up-death-sounds");
CustomEditorProperties.CustomFloatSlider(new Rect(), new GUIContent(), DeathVolumeProp, "Death Volume", 0, 1);
CustomEditorProperties.CustomHelpLabelField("Controls the volume of Death Sounds.", true);
CustomEditorProperties.CustomListPropertyField(DeathSoundsProp, "Death Sounds", "Controls how many death sounds this AI will use.", true);
CustomEditorProperties.EndFoldoutWindowBox();
}
}
/// <summary>
/// Draws a sound list based on the passed parameters.
/// </summary>
void DrawSoundList(ReorderableList ListRef, string DisplayName)
{
ListRef.drawHeaderCallback = rect =>
{
EditorGUI.LabelField(rect, DisplayName, EditorStyles.boldLabel);
};
ListRef.drawElementCallback =
(Rect rect, int index, bool isActive, bool isFocused) =>
{
var element = ListRef.serializedProperty.GetArrayElementAtIndex(index);
EditorGUI.ObjectField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, GUIContent.none);
};
}
}
}