Add SoundEffect audio system (ScriptableObject-based) with test assets

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Fable 5 committed 2026-07-10 13:25:56 +02:00
1 parent 75f5704ab8
commit 0be503c927
22 files changed
+741

No files matched your search

@@ -0,0 +1,114 @@
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace Prototype.Editor
{
public class AudioMixerSliderGUI : IMGUIContainer
{
public Object @object;
public string propertyPath;
public Vector2 valueRange = new(0, 1);
public float defaultValue;
bool dragging;
public AudioMixerSliderGUI() : base()
{
onGUIHandler = OnGUI;
RegisterCallback<MouseDownEvent>(OnMouseDown);
RegisterCallback<MouseMoveEvent>(OnMouseMove);
RegisterCallback<MouseUpEvent>(OnMouseUp);
}
SerializedProperty property
=> @object && !string.IsNullOrEmpty(propertyPath)
? new SerializedObject(@object).FindProperty(propertyPath)
: null;
public float value
{
get
{
return property?.floatValue ?? 0;
}
set
{
var property = this.property;
if (property is null)
return;
var newValue = Mathf.Clamp(value, valueRange.x, valueRange.y);
if (property.floatValue == newValue)
return;
property.floatValue = newValue;
property.serializedObject.ApplyModifiedProperties();
MarkDirtyRepaint();
}
}
Rect r => contentRect;
float s => r.width / 12;
float h => r.height / 16;
Rect r_inner => new(0, h / 2 + 8, r.width - s * 2, r.height - h - 18);
Rect r_slide => new(r.width / 2 - s / 2, r_inner.y, s, r_inner.height);
Rect r_button
{
get
{
var t = Mathf.InverseLerp(valueRange.x, valueRange.y, value);
return new(
(r.width - r_inner.width) / 2,
r_inner.y + r_inner.height * (1 - t) - h / 2,
r_inner.width,
h
);
}
}
void OnGUI()
{
EditorGUI.DrawRect(r, new(0, 0, 0, .3f));
EditorGUI.DrawRect(r_slide, new(0, 0, 0, .4f));
EditorGUI.DrawRect(r_button, new(1, 1, .6f, dragging ? .8f : .4f));
}
void OnMouseDown(MouseDownEvent e)
{
switch (e.button)
{
case 0:
dragging = true;
break;
case 1:
value = defaultValue;
break;
}
}
void OnMouseMove(MouseMoveEvent e)
{
if (!dragging)
return;
var t = 1 - (e.localMousePosition.y - r_inner.y) / r_inner.height;
value = Mathf.Lerp(valueRange.x, valueRange.y, t);
}
void OnMouseUp(MouseUpEvent e)
{
switch (e.button)
{
case 0:
dragging = false;
break;
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c818c5d558214584f80ca414dd1cab70
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,142 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.UIElements;
namespace Prototype.Editor
{
public class AudioMixerWindow : EditorWindow
{
[MenuItem("Tools/Audio Mixer")]
static void Init()
{
GetWindow<AudioMixerWindow>().titleContent = new("Audio Mixer");
}
AudioMixerGroup audioMixerGroup;
List<SoundEffect> soundEffects;
readonly List<(SoundEffect soundEffect, VisualElement element)> soundEffectTracks = new();
void OnEnable()
{
audioMixerGroup = null;
soundEffects = null;
RefreshLayout();
}
void RefreshLayout()
{
var root = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
AssetDatabase.GUIDToAssetPath(
AssetDatabase.FindAssets(
$"t:{nameof(VisualTreeAsset)} {nameof(AudioMixerWindow)}"
)[0]
)
).Instantiate();
rootVisualElement.Clear();
rootVisualElement.Add(root);
root.StretchToParentSize();
root.Q<ObjectField>().RegisterValueChangedCallback(x =>
{
audioMixerGroup = x.newValue as AudioMixerGroup;
RefreshTracksVisibility();
});
}
void OnInspectorUpdate()
{
var refreshTracksVisibility = false;
if (soundEffects is null)
{
soundEffects = new();
foreach (var guid in AssetDatabase.FindAssets($"t:{nameof(SoundEffect)}"))
{
soundEffects.Add(
AssetDatabase.LoadAssetAtPath<SoundEffect>(
AssetDatabase.GUIDToAssetPath(guid)
)
);
}
RefreshSoundEffectTracks();
refreshTracksVisibility = true;
}
if (refreshTracksVisibility)
RefreshTracksVisibility();
}
void RefreshSoundEffectTracks()
{
soundEffectTracks.Clear();
if (soundEffects is not null)
{
foreach (var soundEffect in soundEffects)
{
var track = new VisualElement();
track.AddToClassList("track");
var applyButton = new Button(soundEffect.ApplyVolumeMultiplierFactor)
{
text = "Apply"
};
applyButton.AddToClassList("accent");
track.Add(applyButton);
track.Add(
new AudioMixerSliderGUI
{
@object = soundEffect,
propertyPath = "volumeMultiplierFactor",
valueRange = new Vector2(0, 2),
defaultValue = 1
}
);
track.Add(
new Label(
soundEffect.name
.Replace(" (Sound Effect)", "")
.Replace(" - BGM", "")
.Replace(" - SFX", "")
)
);
track.Add(
new Button(() => Selection.activeObject = soundEffect)
{
text = "Select"
}
);
soundEffectTracks.Add((soundEffect, track));
}
}
}
void RefreshTracksVisibility()
{
var content = rootVisualElement.Q<ScrollView>().contentContainer;
content.Clear();
foreach (var (soundEffect, track) in soundEffectTracks)
{
if (audioMixerGroup &&
audioMixerGroup != soundEffect.audioMixerGroup)
{
continue;
}
content.Add(track);
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: db79bb44fd8fed0449189fb735c16e0a
@@ -0,0 +1,41 @@
#root #unity-content-container {
padding-left: 2px;
}
.track {
margin-right: 2px;
width: 50px;
}
.track > AudioMixerSliderGUI {
flex-grow: 1;
}
.track > Label {
overflow: hidden;
height: 42px;
white-space: normal;
-unity-text-align: middle-center;
font-size: 9px;
}
Button {
padding: 2.5px 0;
margin: 2px 0;
font-size: 10.5px;
border-color: transparent;
border-radius: 0;
}
Button { background-color: rgba(105, 155, 205, .25) }
Button:hover { background-color: rgba(55, 155, 255, .4) }
Button:active { background-color: rgba(0, 155, 255, .6) }
Button.accent { background-color: rgba(55, 205, 105, .25) }
Button.accent:hover { background-color: rgba(0, 205, 105, .4) }
Button.accent:active { background-color: rgba(0, 205, 105, .55) }
Button.disabled {
background-color: rgba(0, 0, 0, .2);
color: transparent;
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fe45eeb67beba6548bfeab8e7921df39
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
disableValidation: 0
@@ -0,0 +1,10 @@
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="True">
<Style src="project://database/Assets/_/Scripts/Audio/Editor/AudioMixerWindow.uss?fileID=7433441132597879392&amp;guid=fe45eeb67beba6548bfeab8e7921df39&amp;type=3#AudioMixerWindow" />
<ui:VisualElement name="root" style="flex-grow: 1;">
<ui:VisualElement style="flex-direction: row;">
<uie:ObjectField allow-scene-objects="false" type="UnityEngine.Audio.AudioMixerGroup, UnityEngine.AudioModule" style="flex-grow: 1;" />
<ui:EnumField type="Prototype.Editor.AudioMixerWindow+SoundType, Assembly-CSharp-Editor" style="flex-basis: 160px;" />
</ui:VisualElement>
<ui:ScrollView scroll-deceleration-rate="0,135" elasticity="0,1" name="tracks" mode="Horizontal" vertical-scroller-visibility="Hidden" horizontal-scroller-visibility="AlwaysVisible" style="flex-grow: 1;" />
</ui:VisualElement>
</ui:UXML>
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 7baee57de0202b94d829ac4ab1fc2016
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}