From 73accf8dc6477c5c69eab3a937c62734abef3d08 Mon Sep 17 00:00:00 2001 From: Mr-Jay-Gatsby Date: Mon, 13 Feb 2023 11:35:45 -0500 Subject: [PATCH 01/76] initial commit --- VisualPinball.Engine/VPT/Enums.cs | 2 + VisualPinball.Engine/VPT/ISoundEmitter.cs | 53 +++ .../VPT/ISoundEmitter.cs.meta | 11 + VisualPinball.Engine/VPT/MechSounds.meta | 8 + .../VPT/MechSounds/MechSoundsData.cs | 100 +++++ .../VPT/MechSounds/MechSoundsData.cs.meta | 11 + VisualPinball.Unity/Assets/Presets.meta | 2 +- .../Inspectors/MechanicalSoundsInspector.cs | 212 +++++++++++ .../MechanicalSoundsInspector.cs.meta | 11 + .../VisualPinball.Unity.Editor/Utils/Icons.cs | 10 +- .../VPT/Sounds.meta | 8 + .../VPT/Sounds/SoundInspector.cs | 357 ++++++++++++++++++ .../VPT/Sounds/SoundInspector.cs.meta | 11 + .../VisualPinball.Unity/Game/Player.cs | 5 + .../VPT/Flipper/FlipperApi.cs | 2 +- .../VisualPinball.Unity/VPT/Sounds.meta | 8 + .../VPT/Sounds/GameSounds.meta | 8 + .../VPT/Sounds/MechSounds.meta | 8 + .../Sounds/MechSounds/MechSoundsComponent.cs | 209 ++++++++++ .../MechSounds/MechSoundsComponent.cs.meta | 11 + .../VPT/Sounds/SoundAsset.cs | 72 ++++ .../VPT/Sounds/SoundAsset.cs.meta | 11 + 22 files changed, 1126 insertions(+), 4 deletions(-) create mode 100644 VisualPinball.Engine/VPT/ISoundEmitter.cs create mode 100644 VisualPinball.Engine/VPT/ISoundEmitter.cs.meta create mode 100644 VisualPinball.Engine/VPT/MechSounds.meta create mode 100644 VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs create mode 100644 VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechanicalSoundsInspector.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechanicalSoundsInspector.cs.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/GameSounds.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs.meta diff --git a/VisualPinball.Engine/VPT/Enums.cs b/VisualPinball.Engine/VPT/Enums.cs index de49e49e1..d27c4a5cc 100644 --- a/VisualPinball.Engine/VPT/Enums.cs +++ b/VisualPinball.Engine/VPT/Enums.cs @@ -146,4 +146,6 @@ public static class TroughType public const int TwoCoilsOneSwitch = 3; public const int ClassicSingleBall = 4; } + + } diff --git a/VisualPinball.Engine/VPT/ISoundEmitter.cs b/VisualPinball.Engine/VPT/ISoundEmitter.cs new file mode 100644 index 000000000..cfec15ad5 --- /dev/null +++ b/VisualPinball.Engine/VPT/ISoundEmitter.cs @@ -0,0 +1,53 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.Collections.Generic; +using Unity.VisualScripting.YamlDotNet.Core.Tokens; +using UnityEngine; + +namespace VisualPinball.Engine.VPT +{ + //Implemented in components relating to sounds, including the Mechanical Sounds component. + public interface ISoundEmitter + { + SoundTrigger[] AvailableTriggers { get;} + VolumeEmitter[] GetVolumeEmitters(SoundTrigger trigger); + event EventHandler OnSound; + } + + public struct SoundTrigger + { + public string Id; + public string Name; + + } + + //The emitter for the sound trigger. Determines how the volume will be calculated. + //Example: The "Fixed" volume emitter would be an emitter for the "Coil On" trigger. Same volume as configured by the sound asset. + //Example: The "Ball Velocity" volume emitter would be an emitter for the "Ball Collision" trigger. Volume depends on the ball velocity upon collision. + public struct VolumeEmitter + { + public string Id; + public string Name; + } + + public struct SoundEventArgs + { + public SoundTrigger Trigger; + public float Volume; + } +} diff --git a/VisualPinball.Engine/VPT/ISoundEmitter.cs.meta b/VisualPinball.Engine/VPT/ISoundEmitter.cs.meta new file mode 100644 index 000000000..c4022772e --- /dev/null +++ b/VisualPinball.Engine/VPT/ISoundEmitter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7129feb5a774383458adb84d3b08fabe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Engine/VPT/MechSounds.meta b/VisualPinball.Engine/VPT/MechSounds.meta new file mode 100644 index 000000000..4d6559344 --- /dev/null +++ b/VisualPinball.Engine/VPT/MechSounds.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f9a957a9e90457b45ba74b1c0b726c3f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs b/VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs new file mode 100644 index 000000000..79166e386 --- /dev/null +++ b/VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs @@ -0,0 +1,100 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.Collections.Generic; +using VisualPinball.Engine.IO; +using System.IO; +using VisualPinball.Engine.VPT.Table; +using UnityEngine; + +namespace VisualPinball.Engine.VPT.MechSounds +{ + [Serializable] + public class MechSoundsData : ItemData + { + + public override string GetName() => Name; + public override void SetName(string name) { Name = name; } + + [BiffString("NAME", IsWideString = true, Pos = 14)] + public string Name = string.Empty; + public SoundTrigger[] AvailableTriggers; + public SoundTrigger SelectedTrigger; + public VolumeEmitter[] AvailableEmitters; + + [Serializable] + public class MechSound + { + public int Trigger; + public ScriptableObject Sound; + public int Volume; + public float VolumeValue = 1; + public actionType Action = actionType.PlayOnce; + public float Fade = 50; + + } + + void AddNew() + { + SoundList.Add(new MechSound()); + } + + void Remove(int index) + { + SoundList.RemoveAt(index); + } + + public List SoundList; + public enum actionType { PlayOnce, Loop }; + + #region BIFF + + + public MechSoundsData() : base(StoragePrefix.VpeGameItem) + { + } + + public MechSoundsData(string name) : base(StoragePrefix.VpeGameItem) + { + Name = name; + } + + static MechSoundsData() + { + Init(typeof(MechSoundsData), Attributes); + } + + public MechSoundsData(BinaryReader reader, string storageName) : this(storageName) + { + Load(this, reader, Attributes); + } + + public override void Write(BinaryWriter writer, HashWriter hashWriter) + { + writer.Write((int)ItemType.Flipper); + WriteRecord(writer, Attributes, hashWriter); + WriteEnd(writer, hashWriter); + } + + private static readonly Dictionary> Attributes = new Dictionary>(); + + #endregion + + + } +} + diff --git a/VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs.meta b/VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs.meta new file mode 100644 index 000000000..b60e41019 --- /dev/null +++ b/VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fae296b1de8a64345a205b44db48c153 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Presets.meta b/VisualPinball.Unity/Assets/Presets.meta index 0d4105838..d3dab9898 100644 --- a/VisualPinball.Unity/Assets/Presets.meta +++ b/VisualPinball.Unity/Assets/Presets.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 74b6f483aa6bd6c49bc05dca5f2c6750 +guid: a61d04b442140514a9bfb858f9ed8f05 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechanicalSoundsInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechanicalSoundsInspector.cs new file mode 100644 index 000000000..32d412cd4 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechanicalSoundsInspector.cs @@ -0,0 +1,212 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using UnityEngine; +using UnityEditor; +using VisualPinball.Engine.VPT; +using VisualPinball.Engine.VPT.MechSounds; + +namespace VisualPinball.Unity.Editor +{ + [CustomEditor(typeof(MechSoundsComponent)), CanEditMultipleObjects] + + //public class MechanicalSoundInspector : UnityEditor.Editor, ISoundEmitter + public class MechanicalSoundInspector : MainInspector + { + + private MechSoundsComponent _myTarget; + private SerializedProperty _soundList; + private const float _buttonWidth = 150; + + + protected override void OnEnable() + { + + _myTarget = (MechSoundsComponent)target; + _soundList = serializedObject.FindProperty(nameof(MechSoundsComponent.SoundList)); + InitTriggers(); + SetSoundTriggers(); + InitVolumeEmitters(); + } + + public override void OnInspectorGUI() + { + if (HasErrors()) + { + return; + } + + serializedObject.Update(); + + EditorGUILayout.LabelField("Current Sounds"); + + GUILayout.BeginVertical("Box"); + for (int i = 0; i < _soundList.arraySize; i++) + { + SerializedProperty _element = _soundList.GetArrayElementAtIndex(i); + SerializedProperty _triggerProperty = _element.FindPropertyRelative("Trigger"); + SerializedProperty _soundProperty = _element.FindPropertyRelative("Sound"); + SerializedProperty _volumeSelectionProperty = _element.FindPropertyRelative("Volume"); + SerializedProperty _volumeProperty = _element.FindPropertyRelative("VolumeValue"); + SerializedProperty _actionSelectionProperty = _element.FindPropertyRelative("Action"); + SerializedProperty _fadeProperty = _element.FindPropertyRelative("Fade"); + + _triggerProperty.intValue = EditorGUILayout.Popup("Trigger", _triggerProperty.intValue, GetTriggerOptions(_myTarget.AvailableTriggers)); + _myTarget.SelectedTrigger = GetSelectedTrigger(_triggerProperty.intValue); + + EditorGUILayout.Space(5); + _soundProperty.objectReferenceValue = EditorGUILayout.ObjectField("Sound", _soundProperty.objectReferenceValue, typeof(SoundAsset), true); + + EditorGUILayout.Space(5); + _volumeSelectionProperty.intValue = EditorGUILayout.Popup(new GUIContent("Volume", "Depends on trigger selected: \n 'Fixed'-Not dependent on any playfield action. \n 'Ball Velocity'- Gameplay-related (collision)."), _volumeSelectionProperty.intValue, GetEmitterOptions(_myTarget.AvailableEmitters)); + + EditorGUILayout.Space(5); + EditorGUILayout.BeginHorizontal(); + GUILayout.Label("", GUILayout.Width(EditorGUIUtility.labelWidth)); + _volumeProperty.floatValue = EditorGUILayout.Slider("", _volumeProperty.floatValue, 0.1f, 2); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.Space(5); + EditorGUILayout.PropertyField(_actionSelectionProperty); + + EditorGUILayout.Space(5); + EditorGUILayout.BeginHorizontal(); + _fadeProperty.floatValue = EditorGUILayout.Slider("Fade", _fadeProperty.floatValue, 0, 300); + GUILayout.Label("ms", new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Bold }); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.Space(15); + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (GUILayout.Button("Remove this sound", GUILayout.Width(_buttonWidth))) + { + _soundList.DeleteArrayElementAtIndex(i); + } + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); + + }//end soundlist loop + GUILayout.EndVertical(); + + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (GUILayout.Button("Add a new sound", GUILayout.Width(_buttonWidth))) + { + _myTarget.SoundList.Add(new MechSoundsData.MechSound()); + } + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + serializedObject.ApplyModifiedProperties(); + } + + + #region Initialize/Set methods + private void InitTriggers() + { + if (_myTarget.AvailableTriggers == null || _myTarget.AvailableTriggers.Length == 0) + { + //Debug.Log("Initializing the SoundTriggers array..."); + _myTarget.AvailableTriggers = new SoundTrigger[1]; + } + } + private void SetSoundTriggers() + { + //flipper on button press event + string CoilOnId = "coil_on"; + string CoilOnName = "Coil On"; + //flipper release button press event + string CoilOffId = "coil_off"; + string CoilOffName = "Coil Off"; + //ball collision with the flipper + string BallCollisionId = "ball_collision"; + string BallCollisionName = "Ball Collision"; + + string[] Ids = new string[] { CoilOnId, CoilOffId, BallCollisionId }; + string[] Names = new string[] { CoilOnName, CoilOffName, BallCollisionName }; + + int index = Ids.Length; + SoundTrigger soundTrigger; + SoundTrigger[] triggers = new SoundTrigger[index]; + + for (int i = 0; i < index; i++) + { + soundTrigger = new SoundTrigger(); + soundTrigger.Id = Ids[i]; + soundTrigger.Name = Names[i]; + triggers[i] = soundTrigger; + } + + _myTarget.AvailableTriggers = triggers; + _myTarget.SelectedTrigger = triggers[0]; + serializedObject.ApplyModifiedProperties(); + + } + + private string[] GetTriggerOptions(SoundTrigger[] triggers) + { + int index = triggers.Length; + string[] options = new string[index]; + + for (int i = 0; i < index; i++) + { + options[i] = triggers[i].Name; + } + + return options; + } + + + private SoundTrigger GetSelectedTrigger(int index) + { + SoundTrigger sTrigger = new SoundTrigger(); + sTrigger.Id = _myTarget.AvailableTriggers[index].Id; + sTrigger.Name = _myTarget.AvailableTriggers[index].Name; + + return sTrigger; + } + + private void InitVolumeEmitters() + { + if (_myTarget.AvailableEmitters == null || _myTarget.AvailableEmitters.Length == 0) + { + _myTarget.AvailableEmitters = new VolumeEmitter[1]; + } + } + + private string[] GetEmitterOptions(VolumeEmitter[] volEmitters) + { + int index = volEmitters.Length; + string[] options = new string[index]; + + for (int i = 0; i < index; i++) + { + options[i] = volEmitters[i].Name; + } + + return options; + } + #endregion + + } +} + diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechanicalSoundsInspector.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechanicalSoundsInspector.cs.meta new file mode 100644 index 000000000..d2f54cad9 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechanicalSoundsInspector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d745160357eec104ab1beb6bd2ebdd3c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs index 1a9abde26..3cc82a76f 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs @@ -80,9 +80,11 @@ public IconVariant(string name, IconSize size, IconColor color) private const string KickerName = "kicker"; private const string LightGroupName = "light_group"; private const string LightName = "light"; + private const string LoopButtonName = "player"; private const string MechName = "mech"; private const string MechPinMameName = "mech_pinmame"; private const string PlayfieldName = "playfield"; + private const string PlayButtonName = "player"; private const string PlugName = "plug"; private const string PlungerName = "plunger"; private const string PrimitiveName = "primitive"; @@ -94,6 +96,7 @@ public IconVariant(string name, IconSize size, IconColor color) private const string ScoreReelSingleName = "score_reel_single"; private const string SlingshotName = "slingshot"; private const string SpinnerName = "spinner"; + private const string StopButtonName = "kicker"; private const string SurfaceName = "surface"; private const string SwitchNcName = "switch_nc"; private const string SwitchNoName = "switch_no"; @@ -117,8 +120,8 @@ public IconVariant(string name, IconSize size, IconColor color) private static readonly string[] Names = { AssetLibraryName, BallRollerName, BallName, BoltName, BumperName, CalendarName, CannonName, CoilName, DropTargetBankName, DropTargetName, FlasherName, - FlipperName, GateName, GateLifterName, HitTargetName, KeyName, KickerName, LightGroupName, LightName, MechName, MechPinMameName, PlayfieldName, PlugName, - PhysicsName, PlungerName, PrimitiveName, RampName, RotatorName, RubberName, ScoreReelName, ScoreReelSingleName, SlingshotName, SpinnerName, SurfaceName, + FlipperName, GateName, GateLifterName, HitTargetName, KeyName, KickerName, LightGroupName, LightName, LoopButtonName, MechName, MechPinMameName, PlayfieldName, PlayButtonName, PlugName, + PhysicsName, PlungerName, PrimitiveName, RampName, RotatorName, RubberName, ScoreReelName, ScoreReelSingleName, SlingshotName, SpinnerName, StopButtonName, SurfaceName, SwitchNcName, SwitchNoName, TableName, TeleporterName, TriggerName, TroughName, CoilEventName, SwitchEventName, LampEventName, LampSeqName, MetalWireGuideName, PlayerVariableName, PlayerVariableEventName, TableVariableName, TableVariableEventName, UpdateDisplayName, DisplayEventName @@ -182,12 +185,14 @@ private static IIconLookup[] GetLookups() { public static Texture2D Key(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(KeyName, size, color); public static Texture2D Kicker(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(KickerName, size, color); public static Texture2D Light(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(LightName, size, color); + public static Texture2D LoopButton(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(LoopButtonName, size, color); public static Texture2D LightGroup(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(LightGroupName, size, color); public static Texture2D Mech(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(MechName, size, color); public static Texture2D MechPinMame(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(MechPinMameName, size, color); public static Texture2D MetalWireGuide(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(MetalWireGuideName, size, color); public static Texture2D Physics(IconSize size = IconSize.Small, IconColor color = IconColor.Gray) => Instance.GetItem(PhysicsName, size, color); public static Texture2D Playfield(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PlayfieldName, size, color); + public static Texture2D PlayButton(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PlayButtonName, size, color); public static Texture2D Plug(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PlugName, size, color); public static Texture2D Plunger(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PlungerName, size, color); public static Texture2D Primitive(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(PrimitiveName, size, color); @@ -198,6 +203,7 @@ private static IIconLookup[] GetLookups() { public static Texture2D ScoreReelSingle(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(ScoreReelSingleName, size, color); public static Texture2D Slingshot(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(SlingshotName, size, color); public static Texture2D Spinner(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(SpinnerName, size, color); + public static Texture2D StopButton(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(StopButtonName, size, color); public static Texture2D Surface(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(SurfaceName, size, color); public static Texture2D Switch(bool isClosed, IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(isClosed ? SwitchNcName : SwitchNoName, size, color); public static Texture2D Table(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(TableName, size, color); diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds.meta new file mode 100644 index 000000000..6042bc759 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8dc2c04b7ef683d4a8076a16ebddc298 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs new file mode 100644 index 000000000..6cdd15566 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs @@ -0,0 +1,357 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using UnityEngine; +using UnityEditor; +using UnityEngine.Audio; + +namespace VisualPinball.Unity.Editor +{ + + + [CustomEditor(typeof(SoundAsset)), CanEditMultipleObjects] + public class SoundsInspector : UnityEditor.Editor + { + SerializedProperty Name; + SerializedProperty Description; + SerializedProperty VolumeCorrection; + SerializedProperty Clips; + SerializedProperty ClipSelection; + SerializedProperty RandomizePitch; + SerializedProperty RandomizeSpeed; + SerializedProperty RandomizeVolume; + + private const string _playButtonText = "Play"; + private const string _loopButtonText = "Loop"; + private IconColor _loopButtonColor = IconColor.Gray; + private const string _stopButtonText = "Stop"; + private const float _buttonHeight = 30; + private const float _buttonWidth = 50; + private const float _clipDelayModifier = 0.2f;//helps set the time between playing audio clips, if the current clip is less than 1 second in length + private int _clipIndex; + private bool _loop = false; + private AudioClip[] _clipArray; + private GameObject[] _soundObjects; + private int _soundObjectsLength = 0; + private float _nextStartTime = (float)AudioSettings.dspTime + _clipDelayModifier; + private int _nextClip = 0; + private bool _clipPlaying = false; + + + private void OnEnable() + { + Name = serializedObject.FindProperty(nameof(SoundAsset.Name)); + Description = serializedObject.FindProperty(nameof(SoundAsset.Description)); + VolumeCorrection = serializedObject.FindProperty(nameof(SoundAsset.VolumeCorrection)); + Clips = serializedObject.FindProperty(nameof(SoundAsset.Clips)); + ClipSelection = serializedObject.FindProperty(nameof(SoundAsset.ClipSelection)); + RandomizePitch = serializedObject.FindProperty(nameof(SoundAsset.RandomizePitch)); + RandomizeSpeed = serializedObject.FindProperty(nameof(SoundAsset.RandomizeSpeed)); + RandomizeVolume = serializedObject.FindProperty(nameof(SoundAsset.RandomizeVolume)); + + } + + private void OnDisable() + { + EditorApplication.update -= Update; + } + + private void OnDestroy() + { + } + + public override void OnInspectorGUI() + { + + EditorApplication.update += Update; + serializedObject.Update(); + + EditorGUILayout.PropertyField(Name, true); + + using (var horizontalScope = new GUILayout.HorizontalScope()) + { + + EditorGUILayout.PropertyField(Description, GUILayout.Height(100)); + } + + EditorGUILayout.PropertyField(VolumeCorrection, true); + EditorGUILayout.PropertyField(Clips, true); + EditorGUILayout.PropertyField(ClipSelection, true); + EditorGUILayout.PropertyField(RandomizePitch, true); + EditorGUILayout.PropertyField(RandomizeSpeed, true); + EditorGUILayout.PropertyField(RandomizeVolume, true); + + EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); + + + GUILayout.BeginHorizontal(); + GUILayout.Space(100); + + _clipArray = new AudioClip[Clips.arraySize]; + for (int i = 0; i < Clips.arraySize; i++) + { + _clipArray[i] = (AudioClip)Clips.GetArrayElementAtIndex(i).objectReferenceValue; + } + + + if (GUILayout.Button(new GUIContent(_playButtonText, Icons.PlayButton(IconSize.Small, IconColor.Orange)), GUILayout.Height(_buttonHeight), GUILayout.Width(_buttonWidth))) + { + if (_loop == false && _clipPlaying == false) //play single clip only when loop of clips not playing and current single clip is not already playing + { + if (ClipSelection.intValue == 0) + { PlayRoundRobin(_clipArray); } + else { PlayRandom(_clipArray); } + + + } + } + + if (GUILayout.Button(new GUIContent(_loopButtonText, Icons.PlayButton(IconSize.Small, _loopButtonColor)), GUILayout.Height(_buttonHeight), GUILayout.Width(_buttonWidth))) + { + //if loop then sound objects are setup in the Update method + if (_loop) + { + _loopButtonColor = IconColor.Gray; + Destroy();//destroy temporary game objects then set loop toggle to false + } + else + { + _loop = true; + _loopButtonColor = IconColor.Orange; + } + } + + if (GUILayout.Button(new GUIContent(_stopButtonText, Icons.StopButton(IconSize.Small, IconColor.Orange)), GUILayout.Height(_buttonHeight), GUILayout.Width(_buttonWidth))) + { + Stop(); + _loopButtonColor = IconColor.Gray; + } + + + GUILayout.EndHorizontal(); + + serializedObject.ApplyModifiedProperties(); + + } + + + private void Stop() + { + if (_loop) + { + for (int i = 0; i < _clipArray.Length; i++) + { + if (GameObject.Find($"Sound{i}") != null) + { + AudioSource source = GameObject.Find($"Sound{i}").GetComponent(); + source.Stop(); + Destroy();//destroy temporary game objects then set loop toggle to false + } + } + } + else + { + GameObject ob = GameObject.Find("Sound"); + + if (ob != null) + { + + AudioSource source = ob.GetComponent(); + source.Stop(); + Destroy(); + } + + } + } + + private void Destroy() + { + if (_loop) + { + for (int i = 0; i < _clipArray.Length; i++) + { + GameObject ob = GameObject.Find($"Sound{i}"); + DestroyImmediate(ob); + + } + + _soundObjectsLength = 0; + _nextStartTime = (float)AudioSettings.dspTime + _clipDelayModifier; + _loop = false; + + } + else + { + _clipPlaying = false; + GameObject ob = GameObject.Find("Sound"); + DestroyImmediate(ob); + } + + + + } + private void PlayRoundRobin(AudioClip[] _clipArray) + { + if (_clipIndex < _clipArray.Length) + { + PlayClip(_clipArray[_clipIndex]); + _clipIndex++; + } + + else + { + _clipIndex = 0; + PlayClip(_clipArray[_clipIndex]); + _clipIndex++; + } + } + + + + private void PlayRandom(AudioClip[] _clipArray) + { + if (_clipIndex == _clipArray.Length) + { + _clipIndex = 0; + } + + if (_clipArray.Length > 1)//randomize clip to play only if more than one clip + { + _clipIndex = Random.Range(0, _clipArray.Length); + PlayClip(_clipArray[_clipIndex]); + } + else + { PlayClip(_clipArray[_clipIndex]); } + + } + + + private void PlayClip(AudioClip clip) + { + GameObject tempGameObject = new GameObject("Sound"); + tempGameObject = GetTempGameObject(tempGameObject); + AudioSource audioSource = tempGameObject.GetComponent(); + audioSource.clip = clip; + _clipPlaying = true; + audioSource.Play(); + + + } + private GameObject[] GetSoundObjects() + { + GameObject[] objects = new GameObject[_clipArray.Length]; + + for (int i = 0; i < _clipArray.Length; i++) + { + GameObject tempGameObject = new GameObject($"Sound{i}"); + tempGameObject = GetTempGameObject(tempGameObject); + AudioSource audioSource = tempGameObject.GetComponent(); + audioSource.clip = _clipArray[i]; + objects[i] = tempGameObject; + } + + _soundObjectsLength = objects.Length; + + return objects; + + + } + + // Create a temporary gameobject so that a temporary audiosource can be attached to it, in order to play audioclips + private GameObject GetTempGameObject(GameObject tempGameObject) + { + + Vector3 position = new Vector3(5, 1, 2); + float volume = VolumeCorrection.floatValue; + float pitchModifier = RandomizePitch.floatValue; + float speedModifier = RandomizeSpeed.floatValue; + float volumeModifier = RandomizeVolume.floatValue; + + if (volumeModifier != 1) + { volume = volumeModifier; } + + tempGameObject.transform.position = position; + //_tempGameObject.hideFlags = HideFlags.HideAndDontSave; //dont save object in editor and dont show in heirarchy + AudioSource tempAudioSource = (AudioSource)tempGameObject.AddComponent(typeof(AudioSource)); + tempAudioSource.spatialBlend = 1f; + tempAudioSource.pitch = pitchModifier; + tempAudioSource.volume = volume; + float value; + + AudioMixer audioMixer = Resources.Load("SoundMixer"); + AudioMixerGroup[] audioMixGroup = audioMixer.FindMatchingGroups("Master"); + audioMixGroup[0].audioMixer.SetFloat("pitchShifter", speedModifier); + //audioMixGroup[0].audioMixer.GetFloat("pitchShifter", out value); + //Debug.Log(value.ToString()); + tempAudioSource.outputAudioMixerGroup = audioMixGroup[0]; + + return tempGameObject; + + } + + private void Update() + { + + if (_loop) + { + //set up new array of gameobjects based on most current number of audioclips, if new loop is initiated + if (_soundObjects == null || _soundObjectsLength == 0) + { _soundObjects = GetSoundObjects();} + + if (AudioSettings.dspTime > _nextStartTime - 1) + { + int index = ClipSelection.intValue == 0 ? _nextClip : Random.Range(0, _soundObjects.Length); + AudioSource source = _soundObjects[index].GetComponent(); + AudioClip clipToPlay = source.clip; + source.PlayScheduled(_nextStartTime); + + // Checks how long the Clip will last and updates the Next Start Time with a new value + float duration = clipToPlay.samples / clipToPlay.frequency; + float clipLength = clipToPlay.length; + + if (clipLength < 1) + { clipLength = clipLength + _clipDelayModifier; } + + _nextStartTime = _nextStartTime + clipLength; + + // Increase the clip index number, reset if it runs out of clips + _nextClip = _nextClip < _soundObjects.Length - 1 ? _nextClip + 1 : 0; + } + + } + + else + { + GameObject ob = GameObject.Find("Sound"); + if (ob != null) + { + + AudioSource source = ob.GetComponent(); + if (source.isPlaying == false) + { + Destroy(); + + } + + + } + + } + } + + } +} + diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs.meta new file mode 100644 index 000000000..723eb9c7c --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bace99bbc8f020f49b66c0ce06780514 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs index 9aee1854e..033708aca 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -24,6 +24,9 @@ using VisualPinball.Engine.Common; using VisualPinball.Engine.Game; using VisualPinball.Engine.Game.Engines; +using VisualPinball.Engine.VPT.Flipper; +using VisualPinball.Engine.VPT.MechSounds; +using VisualPinball.Engine.VPT.Trigger; using Color = VisualPinball.Engine.Math.Color; using Logger = NLog.Logger; @@ -453,4 +456,6 @@ public BallEvent(int ballId, GameObject ball) Ball = ball; } } + + } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs index 25cae1a33..d365ff2a5 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs @@ -17,6 +17,7 @@ // ReSharper disable EventNeverSubscribedTo.Global #pragma warning disable 67 +using Codice.CM.SEIDInfo; using System; using Unity.Mathematics; using UnityEngine; @@ -35,7 +36,6 @@ public class FlipperApi : CollidableApi public event EventHandler Init; - /// /// Event emitted when the flipper was touched by the ball, but did /// not collide. diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds.meta new file mode 100644 index 000000000..697162bf9 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f028dd712c2180947bba5a587be6fca6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/GameSounds.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/GameSounds.meta new file mode 100644 index 000000000..7395b2c89 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/GameSounds.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 941dfc7e839dd5f4194835c1c47fb2c4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds.meta new file mode 100644 index 000000000..04596e81d --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4372796933d0ef74fa75d5603c72aab8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs new file mode 100644 index 000000000..e67f9bb02 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs @@ -0,0 +1,209 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +using System; +using System.Collections.Generic; +using UnityEngine; +using VisualPinball.Engine.VPT; +using VisualPinball.Engine.VPT.MechSounds; +using VisualPinball.Engine.VPT.Table; + + +namespace VisualPinball.Unity +{ + + [AddComponentMenu("Visual Pinball/Sounds/Mechanical Sounds")] + public class MechSoundsComponent : MainComponent, ISoundEmitter + { + #region Data + + public List SoundList; + + [SerializeField] + private SoundTrigger[] _availableTriggers; + public SoundTrigger[] AvailableTriggers + { + get { return _availableTriggers; } + set { _availableTriggers = value; } + } + + public SoundTrigger SelectedTrigger; + + [SerializeField] + private VolumeEmitter[] _availableEmitters; + public VolumeEmitter[] AvailableEmitters + { + + get + { + _availableEmitters = GetVolumeEmitters(SelectedTrigger); + return _availableEmitters; + } + set { _availableEmitters = value; } + } + + public VolumeEmitter[] GetVolumeEmitters(SoundTrigger trigger) + { + + string Id; + string Name; + string[] Ids; + string[] Names; + + switch (trigger.Name) + { + case "Coil On": + Ids = new string[1]; + Names = new string[1]; + Id = "fixed"; + Name = "Fixed"; + Ids[0] = Id; + Names[0] = Name; + break; + case "Coil Off": + Ids = new string[1]; + Names = new string[1]; + Id = "fixed"; + Name = "Fixed"; + Ids[0] = Id; + Names[0] = Name; + break; + case "Ball Collision": + Ids = new string[1]; + Names = new string[1]; + Id = "ball_velocity"; + Name = "Ball Velocity"; + Ids[0] = Id; + Names[0] = Name; + break; + default: + Ids = new string[1]; + Names = new string[1]; + Id = "fixed"; + Name = "Fixed"; + Ids[0] = Id; + Names[0] = Name; + break; + } + + int index = Ids.Length; + VolumeEmitter volEmitter; + VolumeEmitter[] volEmitters = new VolumeEmitter[index]; + + for (int i = 0; i < index; i++) + { + volEmitter = new VolumeEmitter(); + volEmitter.Id = Ids[i]; + volEmitter.Name = Names[i]; + volEmitters[i] = volEmitter; + } + + return volEmitters; + } + #endregion + + #region ISoundEmitter + [SerializeField] + public event EventHandler OnSound; + + private void _OnSound(object sender, SwitchEventArgs e) + { + OnSound?.Invoke(this, new SoundEventArgs { Trigger = SelectedTrigger, Volume = SoundList[0].Volume }); + } + + #endregion + + #region Overrides and Constants + + public override ItemType ItemType => ItemType.Sound; + public override string ItemName => "Mechanical Sounds"; + + public override MechSoundsData InstantiateData() => new MechSoundsData(); + + public override bool HasProceduralMesh => false; + + #endregion + + + #region Conversion + + public override IEnumerable SetData(MechSoundsData data) + { + var updatedComponents = new List { this }; + + SoundList = data.SoundList; + AvailableTriggers = data.AvailableTriggers; + SelectedTrigger = data.SelectedTrigger; + AvailableEmitters = data.AvailableEmitters; + + return updatedComponents; + } + + + public override MechSoundsData CopyDataTo(MechSoundsData data, string[] materialNames, string[] textureNames, bool forExport) + { + data.Name = name; + data.SoundList= SoundList; + data.AvailableTriggers = AvailableTriggers; + data.SelectedTrigger = SelectedTrigger; + data.AvailableEmitters = AvailableEmitters; + + return data; + } + + + public override IEnumerable SetReferencedData(MechSoundsData data, Table table, IMaterialProvider materialProvider, ITextureProvider textureProvider, Dictionary components) + { + + return Array.Empty(); + } + + + #endregion + + #region Runtime + + private FlipperApi _flipperApi; + + private void Awake() + { + GetComponentInParent().RegisterMechSound(this); + } + + private void Start() + { + _flipperApi = GetComponentInParent().TableApi.Flipper(this); + _flipperApi.Switch += _OnSound; + + + } + + private void Update() + { + + } + + #endregion + + #region Editor Tools + + + + #endregion + } +} + diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs.meta new file mode 100644 index 000000000..c79190540 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c28cae51000318145b40983f440013ab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs new file mode 100644 index 000000000..2757f3ef3 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs @@ -0,0 +1,72 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +using UnityEngine; +using System; +using System.Collections.Generic; + +namespace VisualPinball.Unity +{ + /// + + /// + [CreateAssetMenu(fileName = "Sound", menuName = "Visual Pinball/Sound", order = 102)] + public class SoundAsset : ScriptableObject + { + + public string Name; + [Space(15)] + public string Description; + + [Range(0,1)] + [Space(15)] + public float VolumeCorrection = 1; //audio clips in unity have a volume range of 0 to 1 + + [Space(15)] + public AudioClip[] Clips; + + public enum Selection + { + RoundRobin, + Random + } + + [Space(15)] + public Selection ClipSelection; + + [Range(0.1f, 2f)] + [Space(15)] + public float RandomizePitch = 1; + + [Range(0.1f, 2f)] + [Space(15)] + public float RandomizeSpeed = 1; + + [Range(0, 1f)] + [Space(15)] + public float RandomizeVolume = 1; + + void OnValidate() + { + + } + + + + } + +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs.meta new file mode 100644 index 000000000..8fdd6acfd --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8a6287696a5efed489f573445fda4418 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 00c4fa98ed470453a84d3256228aa29a43e8baaa Mon Sep 17 00:00:00 2001 From: Mr-Jay-Gatsby Date: Wed, 15 Feb 2023 16:28:37 -0500 Subject: [PATCH 02/76] Individual component implements ISoundEmitter, starting with flipper component. Have mechsoundcomponent inherit from monobehaviour directly. Update mechsoundinspector to inherit from unity editor directly and by adding a propertydrawer to handle mechsound appearance. Initialize soundlist in mechsoundscomponent. --- .../VPT/MechSounds/MechSoundsData.cs | 24 +- .../Inspectors/MechSoundDrawer.cs | 137 +++++++++++ ...pector.cs.meta => MechSoundDrawer.cs.meta} | 2 +- .../Inspectors/MechSoundsInspector.cs | 83 +++++++ .../Inspectors/MechSoundsInspector.cs.meta | 11 + .../Inspectors/MechanicalSoundsInspector.cs | 212 ------------------ .../VPT/Flipper/FlipperComponent.cs | 36 ++- .../Sounds/MechSounds/MechSoundsComponent.cs | 178 ++------------- 8 files changed, 283 insertions(+), 400 deletions(-) create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs rename VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/{MechanicalSoundsInspector.cs.meta => MechSoundDrawer.cs.meta} (83%) create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs.meta delete mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechanicalSoundsInspector.cs diff --git a/VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs b/VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs index 79166e386..8c5669072 100644 --- a/VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs +++ b/VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs @@ -36,30 +36,8 @@ public class MechSoundsData : ItemData public SoundTrigger SelectedTrigger; public VolumeEmitter[] AvailableEmitters; - [Serializable] - public class MechSound - { - public int Trigger; - public ScriptableObject Sound; - public int Volume; - public float VolumeValue = 1; - public actionType Action = actionType.PlayOnce; - public float Fade = 50; - - } - - void AddNew() - { - SoundList.Add(new MechSound()); - } - - void Remove(int index) - { - SoundList.RemoveAt(index); - } + - public List SoundList; - public enum actionType { PlayOnce, Loop }; #region BIFF diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs new file mode 100644 index 000000000..3236d74d7 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs @@ -0,0 +1,137 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using Unity.Entities; +using UnityEditor; +using UnityEngine; +using VisualPinball.Engine.VPT; +using static VisualPinball.Unity.MechSoundsComponent; + +namespace VisualPinball.Unity.Editor +{ + [CustomPropertyDrawer(typeof(MechSound))] + public class MechSoundDrawer : PropertyDrawer + { + private SoundTrigger _selectedTrigger; + private const float _buttonWidth = 150; + + private SoundTrigger[] _availiableTriggers; + private VolumeEmitter[] _availableEmitters; + private const string _volEmitter = "fixed"; + private const string _volEmitterName = "Fixed"; + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + MechSoundsComponent ob = (MechSoundsComponent)property.serializedObject.targetObject; + GameObject go = ob.gameObject; + + EditorGUI.BeginProperty(position, label, property); + + EditorGUI.LabelField(position, label); + + var _triggerProperty = property.FindPropertyRelative("Trigger"); + var _soundProperty = property.FindPropertyRelative("Sound"); + var _volumeSelectionProperty = property.FindPropertyRelative("Volume"); + var _volumeProperty = property.FindPropertyRelative("VolumeValue"); + var _actionSelectionProperty = property.FindPropertyRelative("Action"); + var _fadeProperty = property.FindPropertyRelative("Fade"); + + _availiableTriggers = go.GetComponent().AvailableTriggers; + _triggerProperty.intValue = EditorGUILayout.Popup("Trigger", _triggerProperty.intValue, GetTriggerOptions(_availiableTriggers)); + + _selectedTrigger = GetSelectedTrigger(_triggerProperty.intValue); + _availableEmitters = go.GetComponent().GetVolumeEmitters(_selectedTrigger); + + EditorGUILayout.Space(5); + _soundProperty.objectReferenceValue = EditorGUILayout.ObjectField("Sound", _soundProperty.objectReferenceValue, typeof(SoundAsset), true); + + EditorGUILayout.Space(5); + _volumeSelectionProperty.intValue = EditorGUILayout.Popup(new GUIContent("Volume", "Depends on trigger selected: \n 'Fixed'-Not dependent on any playfield action. \n 'Ball Velocity'- Gameplay-related (collision)."), _volumeSelectionProperty.intValue, GetEmitterOptions(_availableEmitters)); + + EditorGUILayout.Space(5); + EditorGUILayout.BeginHorizontal(); + GUILayout.Label("", GUILayout.Width(EditorGUIUtility.labelWidth)); + _volumeProperty.floatValue = EditorGUILayout.Slider("", _volumeProperty.floatValue, 0.1f, 2); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.Space(5); + EditorGUILayout.PropertyField(_actionSelectionProperty); + + EditorGUILayout.Space(5); + EditorGUILayout.BeginHorizontal(); + _fadeProperty.floatValue = EditorGUILayout.Slider("Fade", _fadeProperty.floatValue, 0, 300); + GUILayout.Label("ms", new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Bold }); + EditorGUILayout.EndHorizontal(); + + EditorGUI.EndProperty(); + + + } + + + #region Set methods + + private string[] GetTriggerOptions(SoundTrigger[] triggers) + { + int index = triggers.Length; + string[] options = new string[index]; + + for (int i = 0; i < index; i++) + { + options[i] = triggers[i].Name; + } + + return options; + } + + + private SoundTrigger GetSelectedTrigger(int index) + { + SoundTrigger sTrigger = new SoundTrigger(); + sTrigger.Id = _availiableTriggers[index].Id; + sTrigger.Name = _availiableTriggers[index].Name; + + return sTrigger; + } + + private string[] GetEmitterOptions(VolumeEmitter[] volEmitters) + { + string[] options; + + if (volEmitters == null) + { + options = new string[] {_volEmitterName}; //'fixed' by default + } + else + { + + int index = volEmitters.Length; + options = new string[index]; + + for (int i = 0; i < index; i++) + { + options[i] = volEmitters[i].Name; + } + + } + + + return options; + } + #endregion + + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechanicalSoundsInspector.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs.meta similarity index 83% rename from VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechanicalSoundsInspector.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs.meta index d2f54cad9..21419ad3a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechanicalSoundsInspector.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d745160357eec104ab1beb6bd2ebdd3c +guid: 884fb5b527309ef489e8b27aa9e4809d MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs new file mode 100644 index 000000000..69a8e56e6 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs @@ -0,0 +1,83 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using UnityEngine; +using UnityEditor; +using static VisualPinball.Unity.MechSoundsComponent; + +namespace VisualPinball.Unity.Editor +{ + [CustomEditor(typeof(MechSoundsComponent)), CanEditMultipleObjects] + public class MechanicalSoundInspector : UnityEditor.Editor + { + + private MechSoundsComponent _myTarget; + private SerializedProperty _soundList; + private const float _buttonWidth = 150; + + private void OnEnable() + { + + _myTarget = (MechSoundsComponent)target; + _soundList = serializedObject.FindProperty(nameof(MechSoundsComponent.SoundList)); + + } + + public override void OnInspectorGUI() + { + + serializedObject.Update(); + + EditorGUILayout.LabelField("Current Sounds"); + + for (int i = 0; i < _soundList.arraySize; i++) + { + EditorGUILayout.PropertyField(_soundList.GetArrayElementAtIndex(i), GUIContent.none); + EditorGUILayout.Space(15); + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (GUILayout.Button("Remove this sound", GUILayout.Width(_buttonWidth))) + { + _soundList.DeleteArrayElementAtIndex(i); + + } + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); + } + + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (GUILayout.Button("Add a new sound", GUILayout.Width(_buttonWidth))) + { + _myTarget.SoundList.Add(new MechSound()); + + } + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + serializedObject.ApplyModifiedProperties(); + } + + + + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs.meta new file mode 100644 index 000000000..19facfd5d --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8dca340821f92c74e8cf97cd3fff29df +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechanicalSoundsInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechanicalSoundsInspector.cs deleted file mode 100644 index 32d412cd4..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechanicalSoundsInspector.cs +++ /dev/null @@ -1,212 +0,0 @@ -// Visual Pinball Engine -// Copyright (C) 2023 freezy and VPE Team -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -using UnityEngine; -using UnityEditor; -using VisualPinball.Engine.VPT; -using VisualPinball.Engine.VPT.MechSounds; - -namespace VisualPinball.Unity.Editor -{ - [CustomEditor(typeof(MechSoundsComponent)), CanEditMultipleObjects] - - //public class MechanicalSoundInspector : UnityEditor.Editor, ISoundEmitter - public class MechanicalSoundInspector : MainInspector - { - - private MechSoundsComponent _myTarget; - private SerializedProperty _soundList; - private const float _buttonWidth = 150; - - - protected override void OnEnable() - { - - _myTarget = (MechSoundsComponent)target; - _soundList = serializedObject.FindProperty(nameof(MechSoundsComponent.SoundList)); - InitTriggers(); - SetSoundTriggers(); - InitVolumeEmitters(); - } - - public override void OnInspectorGUI() - { - if (HasErrors()) - { - return; - } - - serializedObject.Update(); - - EditorGUILayout.LabelField("Current Sounds"); - - GUILayout.BeginVertical("Box"); - for (int i = 0; i < _soundList.arraySize; i++) - { - SerializedProperty _element = _soundList.GetArrayElementAtIndex(i); - SerializedProperty _triggerProperty = _element.FindPropertyRelative("Trigger"); - SerializedProperty _soundProperty = _element.FindPropertyRelative("Sound"); - SerializedProperty _volumeSelectionProperty = _element.FindPropertyRelative("Volume"); - SerializedProperty _volumeProperty = _element.FindPropertyRelative("VolumeValue"); - SerializedProperty _actionSelectionProperty = _element.FindPropertyRelative("Action"); - SerializedProperty _fadeProperty = _element.FindPropertyRelative("Fade"); - - _triggerProperty.intValue = EditorGUILayout.Popup("Trigger", _triggerProperty.intValue, GetTriggerOptions(_myTarget.AvailableTriggers)); - _myTarget.SelectedTrigger = GetSelectedTrigger(_triggerProperty.intValue); - - EditorGUILayout.Space(5); - _soundProperty.objectReferenceValue = EditorGUILayout.ObjectField("Sound", _soundProperty.objectReferenceValue, typeof(SoundAsset), true); - - EditorGUILayout.Space(5); - _volumeSelectionProperty.intValue = EditorGUILayout.Popup(new GUIContent("Volume", "Depends on trigger selected: \n 'Fixed'-Not dependent on any playfield action. \n 'Ball Velocity'- Gameplay-related (collision)."), _volumeSelectionProperty.intValue, GetEmitterOptions(_myTarget.AvailableEmitters)); - - EditorGUILayout.Space(5); - EditorGUILayout.BeginHorizontal(); - GUILayout.Label("", GUILayout.Width(EditorGUIUtility.labelWidth)); - _volumeProperty.floatValue = EditorGUILayout.Slider("", _volumeProperty.floatValue, 0.1f, 2); - EditorGUILayout.EndHorizontal(); - - EditorGUILayout.Space(5); - EditorGUILayout.PropertyField(_actionSelectionProperty); - - EditorGUILayout.Space(5); - EditorGUILayout.BeginHorizontal(); - _fadeProperty.floatValue = EditorGUILayout.Slider("Fade", _fadeProperty.floatValue, 0, 300); - GUILayout.Label("ms", new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Bold }); - EditorGUILayout.EndHorizontal(); - - EditorGUILayout.Space(15); - GUILayout.BeginHorizontal(); - GUILayout.FlexibleSpace(); - if (GUILayout.Button("Remove this sound", GUILayout.Width(_buttonWidth))) - { - _soundList.DeleteArrayElementAtIndex(i); - } - GUILayout.FlexibleSpace(); - GUILayout.EndHorizontal(); - - EditorGUILayout.Space(); - EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); - - }//end soundlist loop - GUILayout.EndVertical(); - - EditorGUILayout.Space(); - EditorGUILayout.Space(); - - GUILayout.BeginHorizontal(); - GUILayout.FlexibleSpace(); - if (GUILayout.Button("Add a new sound", GUILayout.Width(_buttonWidth))) - { - _myTarget.SoundList.Add(new MechSoundsData.MechSound()); - } - GUILayout.FlexibleSpace(); - GUILayout.EndHorizontal(); - - serializedObject.ApplyModifiedProperties(); - } - - - #region Initialize/Set methods - private void InitTriggers() - { - if (_myTarget.AvailableTriggers == null || _myTarget.AvailableTriggers.Length == 0) - { - //Debug.Log("Initializing the SoundTriggers array..."); - _myTarget.AvailableTriggers = new SoundTrigger[1]; - } - } - private void SetSoundTriggers() - { - //flipper on button press event - string CoilOnId = "coil_on"; - string CoilOnName = "Coil On"; - //flipper release button press event - string CoilOffId = "coil_off"; - string CoilOffName = "Coil Off"; - //ball collision with the flipper - string BallCollisionId = "ball_collision"; - string BallCollisionName = "Ball Collision"; - - string[] Ids = new string[] { CoilOnId, CoilOffId, BallCollisionId }; - string[] Names = new string[] { CoilOnName, CoilOffName, BallCollisionName }; - - int index = Ids.Length; - SoundTrigger soundTrigger; - SoundTrigger[] triggers = new SoundTrigger[index]; - - for (int i = 0; i < index; i++) - { - soundTrigger = new SoundTrigger(); - soundTrigger.Id = Ids[i]; - soundTrigger.Name = Names[i]; - triggers[i] = soundTrigger; - } - - _myTarget.AvailableTriggers = triggers; - _myTarget.SelectedTrigger = triggers[0]; - serializedObject.ApplyModifiedProperties(); - - } - - private string[] GetTriggerOptions(SoundTrigger[] triggers) - { - int index = triggers.Length; - string[] options = new string[index]; - - for (int i = 0; i < index; i++) - { - options[i] = triggers[i].Name; - } - - return options; - } - - - private SoundTrigger GetSelectedTrigger(int index) - { - SoundTrigger sTrigger = new SoundTrigger(); - sTrigger.Id = _myTarget.AvailableTriggers[index].Id; - sTrigger.Name = _myTarget.AvailableTriggers[index].Name; - - return sTrigger; - } - - private void InitVolumeEmitters() - { - if (_myTarget.AvailableEmitters == null || _myTarget.AvailableEmitters.Length == 0) - { - _myTarget.AvailableEmitters = new VolumeEmitter[1]; - } - } - - private string[] GetEmitterOptions(VolumeEmitter[] volEmitters) - { - int index = volEmitters.Length; - string[] options = new string[index]; - - for (int i = 0; i < index; i++) - { - options[i] = volEmitters[i].Name; - } - - return options; - } - #endregion - - } -} - diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs index 3ee02ad77..268382904 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -39,7 +39,7 @@ namespace VisualPinball.Unity [HelpURL("https://docs.visualpinball.org/creators-guide/manual/mechanisms/flippers.html")] public class FlipperComponent : MainRenderableComponent, IFlipperData, ISwitchDeviceComponent, ICoilDeviceComponent, IOnSurfaceComponent, - IRotatableComponent + IRotatableComponent, ISoundEmitter { #region Data @@ -126,12 +126,46 @@ public class FlipperComponent : MainRenderableComponent, protected override Type MeshComponentType { get; } = typeof(MeshComponent); protected override Type ColliderComponentType { get; } = typeof(ColliderComponent); + public const string MainCoilItem = "main_coil"; public const string HoldCoilItem = "hold_coil"; public const string EosSwitchItem = "eos_switch"; + //ISoundEmitter SoundTrigger Ids + private const string SoundCoilOn = "coil_on"; + private const string SoundCoilOff = "coil_off"; + private const string SoundCoilCollision = "ball_collision"; + //ISoundEmitter SoundTrigger Names + private const string SoundCoilOnName = "Coil On"; + private const string SoundCoilOffName = "Coil Off"; + private const string SoundCoilCollisionName = "Ball Collision"; + //ISoundEmitter VolumeEmitter Ids + private const string VolumeBallVelocity = "ball_velocity"; + //ISoundEmitter VolumeEmitter Names + private const string VolumeBallVelocityName = "Ball Velocity"; #endregion + #region ISoundEmitter + public SoundTrigger[] AvailableTriggers => new[] { + new SoundTrigger { Id = SoundCoilOn, Name = SoundCoilOnName }, + new SoundTrigger { Id = SoundCoilOff, Name = SoundCoilOffName}, + new SoundTrigger { Id = SoundCoilCollision, Name = SoundCoilCollisionName }, + }; + + public VolumeEmitter[] GetVolumeEmitters(SoundTrigger trigger) + { + switch (trigger.Id) + { + case SoundCoilCollision: + return new[] { new VolumeEmitter { Id = VolumeBallVelocity, Name = VolumeBallVelocityName } }; + } + return null; // null means only "Fixed" will be shown in the volume dropdown. + } + + public event EventHandler OnSound; + #endregion + + #region Wiring public IEnumerable AvailableSwitches => new[] { diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs index e67f9bb02..0a3913826 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs @@ -19,191 +19,43 @@ using System.Collections.Generic; using UnityEngine; using VisualPinball.Engine.VPT; -using VisualPinball.Engine.VPT.MechSounds; -using VisualPinball.Engine.VPT.Table; - namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Sounds/Mechanical Sounds")] - public class MechSoundsComponent : MainComponent, ISoundEmitter + public class MechSoundsComponent : MonoBehaviour { #region Data - public List SoundList; - - [SerializeField] - private SoundTrigger[] _availableTriggers; - public SoundTrigger[] AvailableTriggers + [Serializable] + public class MechSound { - get { return _availableTriggers; } - set { _availableTriggers = value; } - } - - public SoundTrigger SelectedTrigger; + public int Trigger; + public ScriptableObject Sound; + public int Volume; + public float VolumeValue = 1; + public actionType Action = actionType.PlayOnce; + public float Fade = 50; - [SerializeField] - private VolumeEmitter[] _availableEmitters; - public VolumeEmitter[] AvailableEmitters - { - - get - { - _availableEmitters = GetVolumeEmitters(SelectedTrigger); - return _availableEmitters; - } - set { _availableEmitters = value; } } - public VolumeEmitter[] GetVolumeEmitters(SoundTrigger trigger) + void AddNew() { - - string Id; - string Name; - string[] Ids; - string[] Names; - - switch (trigger.Name) - { - case "Coil On": - Ids = new string[1]; - Names = new string[1]; - Id = "fixed"; - Name = "Fixed"; - Ids[0] = Id; - Names[0] = Name; - break; - case "Coil Off": - Ids = new string[1]; - Names = new string[1]; - Id = "fixed"; - Name = "Fixed"; - Ids[0] = Id; - Names[0] = Name; - break; - case "Ball Collision": - Ids = new string[1]; - Names = new string[1]; - Id = "ball_velocity"; - Name = "Ball Velocity"; - Ids[0] = Id; - Names[0] = Name; - break; - default: - Ids = new string[1]; - Names = new string[1]; - Id = "fixed"; - Name = "Fixed"; - Ids[0] = Id; - Names[0] = Name; - break; - } - - int index = Ids.Length; - VolumeEmitter volEmitter; - VolumeEmitter[] volEmitters = new VolumeEmitter[index]; - - for (int i = 0; i < index; i++) - { - volEmitter = new VolumeEmitter(); - volEmitter.Id = Ids[i]; - volEmitter.Name = Names[i]; - volEmitters[i] = volEmitter; - } - - return volEmitters; + SoundList.Add(new MechSound()); } - #endregion - - #region ISoundEmitter - [SerializeField] - public event EventHandler OnSound; - private void _OnSound(object sender, SwitchEventArgs e) + void Remove(int index) { - OnSound?.Invoke(this, new SoundEventArgs { Trigger = SelectedTrigger, Volume = SoundList[0].Volume }); + SoundList.RemoveAt(index); } - #endregion - - #region Overrides and Constants - - public override ItemType ItemType => ItemType.Sound; - public override string ItemName => "Mechanical Sounds"; - - public override MechSoundsData InstantiateData() => new MechSoundsData(); - - public override bool HasProceduralMesh => false; - - #endregion - - #region Conversion - - public override IEnumerable SetData(MechSoundsData data) - { - var updatedComponents = new List { this }; - - SoundList = data.SoundList; - AvailableTriggers = data.AvailableTriggers; - SelectedTrigger = data.SelectedTrigger; - AvailableEmitters = data.AvailableEmitters; - - return updatedComponents; - } - - - public override MechSoundsData CopyDataTo(MechSoundsData data, string[] materialNames, string[] textureNames, bool forExport) - { - data.Name = name; - data.SoundList= SoundList; - data.AvailableTriggers = AvailableTriggers; - data.SelectedTrigger = SelectedTrigger; - data.AvailableEmitters = AvailableEmitters; - - return data; - } - - - public override IEnumerable SetReferencedData(MechSoundsData data, Table table, IMaterialProvider materialProvider, ITextureProvider textureProvider, Dictionary components) - { - - return Array.Empty(); - } - + public List SoundList = new List(1); + public enum actionType { PlayOnce, Loop }; #endregion - #region Runtime - - private FlipperApi _flipperApi; - - private void Awake() - { - GetComponentInParent().RegisterMechSound(this); - } - - private void Start() - { - _flipperApi = GetComponentInParent().TableApi.Flipper(this); - _flipperApi.Switch += _OnSound; - - - } - - private void Update() - { - - } - - #endregion - - #region Editor Tools - - - - #endregion } } From 53e0bb59e11b51516c1b9d096a258a498d67f12e Mon Sep 17 00:00:00 2001 From: Mr-Jay-Gatsby Date: Thu, 16 Feb 2023 10:14:40 -0500 Subject: [PATCH 03/76] Get the component that implements ISoundEmitter for pulling data to the MechSound Drawer. Handle when no component attached to the gameobject implements it. --- .../Inspectors/MechSoundDrawer.cs | 5 +++-- .../Inspectors/MechSoundsInspector.cs | 14 +++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs index 3236d74d7..955d1895f 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs @@ -37,6 +37,7 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten { MechSoundsComponent ob = (MechSoundsComponent)property.serializedObject.targetObject; GameObject go = ob.gameObject; + ISoundEmitter component = ob.GetComponent(); EditorGUI.BeginProperty(position, label, property); @@ -49,11 +50,11 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten var _actionSelectionProperty = property.FindPropertyRelative("Action"); var _fadeProperty = property.FindPropertyRelative("Fade"); - _availiableTriggers = go.GetComponent().AvailableTriggers; + _availiableTriggers = component.AvailableTriggers; _triggerProperty.intValue = EditorGUILayout.Popup("Trigger", _triggerProperty.intValue, GetTriggerOptions(_availiableTriggers)); _selectedTrigger = GetSelectedTrigger(_triggerProperty.intValue); - _availableEmitters = go.GetComponent().GetVolumeEmitters(_selectedTrigger); + _availableEmitters = component.GetVolumeEmitters(_selectedTrigger); EditorGUILayout.Space(5); _soundProperty.objectReferenceValue = EditorGUILayout.ObjectField("Sound", _soundProperty.objectReferenceValue, typeof(SoundAsset), true); diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs index 69a8e56e6..7bce1fa13 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs @@ -17,6 +17,7 @@ using UnityEngine; using UnityEditor; using static VisualPinball.Unity.MechSoundsComponent; +using VisualPinball.Engine.VPT; namespace VisualPinball.Unity.Editor { @@ -38,7 +39,18 @@ private void OnEnable() public override void OnInspectorGUI() { - + + MechSoundsComponent ob = (MechSoundsComponent)serializedObject.targetObject; + GameObject go = ob.gameObject; + + ISoundEmitter component = ob.GetComponent(); + if (component == null) + { + if(EditorUtility.DisplayDialog("Error", "No component attached to this game object implements ISoundEmitter interface.", "ok")) + { return; } + + } + serializedObject.Update(); EditorGUILayout.LabelField("Current Sounds"); From fc2791256707e84a8aea122556de2c71cd9018a2 Mon Sep 17 00:00:00 2001 From: Mr-Jay-Gatsby Date: Thu, 16 Feb 2023 13:58:55 -0500 Subject: [PATCH 04/76] SoundInspector updates including refactor playroundrobin and playrandom methods, refactor handling of audio clip playing using an established gameobject's audiosource, and moved call to update event into OnEnable method. Also deleted mechsounddata and some other minor cleanup. --- .../VPT/MechSounds/MechSoundsData.cs | 78 ------ .../VPT/MechSounds/MechSoundsData.cs.meta | 11 - .../Inspectors/MechSoundDrawer.cs | 15 +- .../VPT/Sounds/SoundInspector.cs | 222 +++++------------- .../VisualPinball.Unity/Game/Player.cs | 2 - .../VPT/Flipper/FlipperApi.cs | 2 - .../VPT/Flipper/FlipperComponent.cs | 1 + .../Sounds/MechSounds/MechSoundsComponent.cs | 6 +- 8 files changed, 74 insertions(+), 263 deletions(-) delete mode 100644 VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs delete mode 100644 VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs.meta diff --git a/VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs b/VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs deleted file mode 100644 index 8c5669072..000000000 --- a/VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Visual Pinball Engine -// Copyright (C) 2023 freezy and VPE Team -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -using System; -using System.Collections.Generic; -using VisualPinball.Engine.IO; -using System.IO; -using VisualPinball.Engine.VPT.Table; -using UnityEngine; - -namespace VisualPinball.Engine.VPT.MechSounds -{ - [Serializable] - public class MechSoundsData : ItemData - { - - public override string GetName() => Name; - public override void SetName(string name) { Name = name; } - - [BiffString("NAME", IsWideString = true, Pos = 14)] - public string Name = string.Empty; - public SoundTrigger[] AvailableTriggers; - public SoundTrigger SelectedTrigger; - public VolumeEmitter[] AvailableEmitters; - - - - - #region BIFF - - - public MechSoundsData() : base(StoragePrefix.VpeGameItem) - { - } - - public MechSoundsData(string name) : base(StoragePrefix.VpeGameItem) - { - Name = name; - } - - static MechSoundsData() - { - Init(typeof(MechSoundsData), Attributes); - } - - public MechSoundsData(BinaryReader reader, string storageName) : this(storageName) - { - Load(this, reader, Attributes); - } - - public override void Write(BinaryWriter writer, HashWriter hashWriter) - { - writer.Write((int)ItemType.Flipper); - WriteRecord(writer, Attributes, hashWriter); - WriteEnd(writer, hashWriter); - } - - private static readonly Dictionary> Attributes = new Dictionary>(); - - #endregion - - - } -} - diff --git a/VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs.meta b/VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs.meta deleted file mode 100644 index b60e41019..000000000 --- a/VisualPinball.Engine/VPT/MechSounds/MechSoundsData.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: fae296b1de8a64345a205b44db48c153 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs index 955d1895f..76010ec92 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs @@ -25,10 +25,9 @@ namespace VisualPinball.Unity.Editor [CustomPropertyDrawer(typeof(MechSound))] public class MechSoundDrawer : PropertyDrawer { - private SoundTrigger _selectedTrigger; private const float _buttonWidth = 150; - private SoundTrigger[] _availiableTriggers; + private int _selTrigger; private VolumeEmitter[] _availableEmitters; private const string _volEmitter = "fixed"; private const string _volEmitterName = "Fixed"; @@ -43,24 +42,22 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten EditorGUI.LabelField(position, label); - var _triggerProperty = property.FindPropertyRelative("Trigger"); var _soundProperty = property.FindPropertyRelative("Sound"); - var _volumeSelectionProperty = property.FindPropertyRelative("Volume"); var _volumeProperty = property.FindPropertyRelative("VolumeValue"); var _actionSelectionProperty = property.FindPropertyRelative("Action"); var _fadeProperty = property.FindPropertyRelative("Fade"); _availiableTriggers = component.AvailableTriggers; - _triggerProperty.intValue = EditorGUILayout.Popup("Trigger", _triggerProperty.intValue, GetTriggerOptions(_availiableTriggers)); - - _selectedTrigger = GetSelectedTrigger(_triggerProperty.intValue); - _availableEmitters = component.GetVolumeEmitters(_selectedTrigger); + _selTrigger = EditorGUILayout.Popup("Trigger", _selTrigger, GetTriggerOptions(_availiableTriggers)); + + ob.SelectedTrigger = GetSelectedTrigger(_selTrigger); + _availableEmitters = component.GetVolumeEmitters(ob.SelectedTrigger); EditorGUILayout.Space(5); _soundProperty.objectReferenceValue = EditorGUILayout.ObjectField("Sound", _soundProperty.objectReferenceValue, typeof(SoundAsset), true); EditorGUILayout.Space(5); - _volumeSelectionProperty.intValue = EditorGUILayout.Popup(new GUIContent("Volume", "Depends on trigger selected: \n 'Fixed'-Not dependent on any playfield action. \n 'Ball Velocity'- Gameplay-related (collision)."), _volumeSelectionProperty.intValue, GetEmitterOptions(_availableEmitters)); + EditorGUILayout.Popup(new GUIContent("Volume", "Depends on trigger selected: \n 'Fixed'-Not dependent on any playfield action. \n 'Ball Velocity'- Gameplay-related (collision)."), 0, GetEmitterOptions(_availableEmitters)); EditorGUILayout.Space(5); EditorGUILayout.BeginHorizontal(); diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs index 6cdd15566..fb619e870 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs @@ -40,15 +40,15 @@ public class SoundsInspector : UnityEditor.Editor private const string _stopButtonText = "Stop"; private const float _buttonHeight = 30; private const float _buttonWidth = 50; - private const float _clipDelayModifier = 0.2f;//helps set the time between playing audio clips, if the current clip is less than 1 second in length + private const float _clipDelayModifier = 1f;//helps set the time between playing audio clips, if the current clip is less than 1 second in length private int _clipIndex; private bool _loop = false; private AudioClip[] _clipArray; - private GameObject[] _soundObjects; - private int _soundObjectsLength = 0; private float _nextStartTime = (float)AudioSettings.dspTime + _clipDelayModifier; private int _nextClip = 0; private bool _clipPlaying = false; + private GameObject _flipper; + private AudioSource _audioSource; private void OnEnable() @@ -62,6 +62,13 @@ private void OnEnable() RandomizeSpeed = serializedObject.FindProperty(nameof(SoundAsset.RandomizeSpeed)); RandomizeVolume = serializedObject.FindProperty(nameof(SoundAsset.RandomizeVolume)); + //get a gameobject with a mechsounds component attached, to play sounds from + _flipper = GameObject.Find("Left Flipper"); + _audioSource = _flipper.GetComponent(); + SetAudioProperties(); + + EditorApplication.update += Update; + } private void OnDisable() @@ -76,7 +83,6 @@ private void OnDestroy() public override void OnInspectorGUI() { - EditorApplication.update += Update; serializedObject.Update(); EditorGUILayout.PropertyField(Name, true); @@ -88,8 +94,8 @@ public override void OnInspectorGUI() } EditorGUILayout.PropertyField(VolumeCorrection, true); - EditorGUILayout.PropertyField(Clips, true); - EditorGUILayout.PropertyField(ClipSelection, true); + EditorGUILayout.PropertyField(Clips); + EditorGUILayout.PropertyField(ClipSelection, true); EditorGUILayout.PropertyField(RandomizePitch, true); EditorGUILayout.PropertyField(RandomizeSpeed, true); EditorGUILayout.PropertyField(RandomizeVolume, true); @@ -114,18 +120,16 @@ public override void OnInspectorGUI() if (ClipSelection.intValue == 0) { PlayRoundRobin(_clipArray); } else { PlayRandom(_clipArray); } - - } } if (GUILayout.Button(new GUIContent(_loopButtonText, Icons.PlayButton(IconSize.Small, _loopButtonColor)), GUILayout.Height(_buttonHeight), GUILayout.Width(_buttonWidth))) { - //if loop then sound objects are setup in the Update method + if (_loop) { _loopButtonColor = IconColor.Gray; - Destroy();//destroy temporary game objects then set loop toggle to false + Stop(); } else { @@ -143,180 +147,85 @@ public override void OnInspectorGUI() GUILayout.EndHorizontal(); + SetAudioProperties(); + serializedObject.ApplyModifiedProperties(); } - - private void Stop() + private void SetAudioProperties() { - if (_loop) - { - for (int i = 0; i < _clipArray.Length; i++) - { - if (GameObject.Find($"Sound{i}") != null) - { - AudioSource source = GameObject.Find($"Sound{i}").GetComponent(); - source.Stop(); - Destroy();//destroy temporary game objects then set loop toggle to false - } - } - } - else - { - GameObject ob = GameObject.Find("Sound"); - if (ob != null) - { + float volume = VolumeCorrection.floatValue; + float pitchModifier = RandomizePitch.floatValue; + float speedModifier = RandomizeSpeed.floatValue; + float volumeModifier = RandomizeVolume.floatValue; - AudioSource source = ob.GetComponent(); - source.Stop(); - Destroy(); - } + if (volumeModifier != 1) + { volume = volumeModifier; } - } - } + _audioSource.spatialBlend = 1f; + _audioSource.pitch = pitchModifier; + _audioSource.volume = volume; - private void Destroy() - { - if (_loop) - { - for (int i = 0; i < _clipArray.Length; i++) - { - GameObject ob = GameObject.Find($"Sound{i}"); - DestroyImmediate(ob); + float value; - } + AudioMixer audioMixer = Resources.Load("SoundMixer"); + AudioMixerGroup[] audioMixGroup = audioMixer.FindMatchingGroups("Master"); + audioMixGroup[0].audioMixer.SetFloat("pitchShifter", speedModifier); + //audioMixGroup[0].audioMixer.GetFloat("pitchShifter", out value); + //Debug.Log(value.ToString()); + _audioSource.outputAudioMixerGroup = audioMixGroup[0]; - _soundObjectsLength = 0; - _nextStartTime = (float)AudioSettings.dspTime + _clipDelayModifier; - _loop = false; + } + private void Stop() + { - } - else + if (_flipper != null) { - _clipPlaying = false; - GameObject ob = GameObject.Find("Sound"); - DestroyImmediate(ob); - } - - + _audioSource.Stop(); + if (_loop) + { + _nextStartTime = (float)AudioSettings.dspTime + _clipDelayModifier; + _loop = false; + } + else + { + _clipPlaying = false; + } + } } private void PlayRoundRobin(AudioClip[] _clipArray) { - if (_clipIndex < _clipArray.Length) - { - PlayClip(_clipArray[_clipIndex]); - _clipIndex++; - } + var nextIndex = _clipIndex + 1 == _clipArray.Length ? 0 : _clipIndex + 1; + PlayClip(_clipArray[nextIndex]); + _clipIndex = nextIndex; - else - { - _clipIndex = 0; - PlayClip(_clipArray[_clipIndex]); - _clipIndex++; - } } - - private void PlayRandom(AudioClip[] _clipArray) { - if (_clipIndex == _clipArray.Length) - { - _clipIndex = 0; - } - - if (_clipArray.Length > 1)//randomize clip to play only if more than one clip - { - _clipIndex = Random.Range(0, _clipArray.Length); - PlayClip(_clipArray[_clipIndex]); - } - else - { PlayClip(_clipArray[_clipIndex]); } - + PlayClip(_clipArray[Random.Range(0, _clipArray.Length)]); } - private void PlayClip(AudioClip clip) { - GameObject tempGameObject = new GameObject("Sound"); - tempGameObject = GetTempGameObject(tempGameObject); - AudioSource audioSource = tempGameObject.GetComponent(); - audioSource.clip = clip; + _audioSource.clip = clip; _clipPlaying = true; - audioSource.Play(); - - - } - private GameObject[] GetSoundObjects() - { - GameObject[] objects = new GameObject[_clipArray.Length]; - - for (int i = 0; i < _clipArray.Length; i++) - { - GameObject tempGameObject = new GameObject($"Sound{i}"); - tempGameObject = GetTempGameObject(tempGameObject); - AudioSource audioSource = tempGameObject.GetComponent(); - audioSource.clip = _clipArray[i]; - objects[i] = tempGameObject; - } - - _soundObjectsLength = objects.Length; - - return objects; - - - } - - // Create a temporary gameobject so that a temporary audiosource can be attached to it, in order to play audioclips - private GameObject GetTempGameObject(GameObject tempGameObject) - { - - Vector3 position = new Vector3(5, 1, 2); - float volume = VolumeCorrection.floatValue; - float pitchModifier = RandomizePitch.floatValue; - float speedModifier = RandomizeSpeed.floatValue; - float volumeModifier = RandomizeVolume.floatValue; - - if (volumeModifier != 1) - { volume = volumeModifier; } - - tempGameObject.transform.position = position; - //_tempGameObject.hideFlags = HideFlags.HideAndDontSave; //dont save object in editor and dont show in heirarchy - AudioSource tempAudioSource = (AudioSource)tempGameObject.AddComponent(typeof(AudioSource)); - tempAudioSource.spatialBlend = 1f; - tempAudioSource.pitch = pitchModifier; - tempAudioSource.volume = volume; - float value; - - AudioMixer audioMixer = Resources.Load("SoundMixer"); - AudioMixerGroup[] audioMixGroup = audioMixer.FindMatchingGroups("Master"); - audioMixGroup[0].audioMixer.SetFloat("pitchShifter", speedModifier); - //audioMixGroup[0].audioMixer.GetFloat("pitchShifter", out value); - //Debug.Log(value.ToString()); - tempAudioSource.outputAudioMixerGroup = audioMixGroup[0]; - - return tempGameObject; - - } - + _audioSource.Play(); + } private void Update() { if (_loop) { - //set up new array of gameobjects based on most current number of audioclips, if new loop is initiated - if (_soundObjects == null || _soundObjectsLength == 0) - { _soundObjects = GetSoundObjects();} - if (AudioSettings.dspTime > _nextStartTime - 1) { - int index = ClipSelection.intValue == 0 ? _nextClip : Random.Range(0, _soundObjects.Length); - AudioSource source = _soundObjects[index].GetComponent(); - AudioClip clipToPlay = source.clip; - source.PlayScheduled(_nextStartTime); + int index = ClipSelection.intValue == 0 ? _nextClip : Random.Range(0, _clipArray.Length); + _audioSource.clip = _clipArray[index]; + AudioClip clipToPlay = _audioSource.clip; + _audioSource.PlayScheduled(_nextStartTime); // Checks how long the Clip will last and updates the Next Start Time with a new value float duration = clipToPlay.samples / clipToPlay.frequency; @@ -328,25 +237,20 @@ private void Update() _nextStartTime = _nextStartTime + clipLength; // Increase the clip index number, reset if it runs out of clips - _nextClip = _nextClip < _soundObjects.Length - 1 ? _nextClip + 1 : 0; + _nextClip = _nextClip < _clipArray.Length - 1 ? _nextClip + 1 : 0; } } else { - GameObject ob = GameObject.Find("Sound"); - if (ob != null) + if (_flipper != null) { - - AudioSource source = ob.GetComponent(); - if (source.isPlaying == false) + if (_audioSource.isPlaying == false) { - Destroy(); + Stop(); } - - } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs index 033708aca..7ce3175df 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -24,8 +24,6 @@ using VisualPinball.Engine.Common; using VisualPinball.Engine.Game; using VisualPinball.Engine.Game.Engines; -using VisualPinball.Engine.VPT.Flipper; -using VisualPinball.Engine.VPT.MechSounds; using VisualPinball.Engine.VPT.Trigger; using Color = VisualPinball.Engine.Math.Color; using Logger = NLog.Logger; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs index d365ff2a5..dbb8be679 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs @@ -15,9 +15,7 @@ // along with this program. If not, see . // ReSharper disable EventNeverSubscribedTo.Global -#pragma warning disable 67 -using Codice.CM.SEIDInfo; using System; using Unity.Mathematics; using UnityEngine; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs index 268382904..9dbbafa93 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -163,6 +163,7 @@ public VolumeEmitter[] GetVolumeEmitters(SoundTrigger trigger) } public event EventHandler OnSound; + #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs index 0a3913826..8620289d6 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs @@ -24,16 +24,18 @@ namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Sounds/Mechanical Sounds")] + [RequireComponent(typeof(AudioSource))] public class MechSoundsComponent : MonoBehaviour { #region Data + public SoundTrigger SelectedTrigger; + public float Volume; + [Serializable] public class MechSound { - public int Trigger; public ScriptableObject Sound; - public int Volume; public float VolumeValue = 1; public actionType Action = actionType.PlayOnce; public float Fade = 50; From e0ba6a07a9c473093e54aaca2f7258be50da90b9 Mon Sep 17 00:00:00 2001 From: Mr-Jay-Gatsby Date: Sat, 18 Feb 2023 14:07:41 -0500 Subject: [PATCH 05/76] Further attempt to set pitch shifter value when testing sounds in the editor. --- .../VPT/Sounds/SoundInspector.cs | 24 ++++++++++++------- .../VisualPinball.Unity/Game/Player.cs | 9 +++++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs index fb619e870..17dd22bf2 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs @@ -17,6 +17,7 @@ using UnityEngine; using UnityEditor; using UnityEngine.Audio; +using System.Threading.Tasks; namespace VisualPinball.Unity.Editor { @@ -65,7 +66,6 @@ private void OnEnable() //get a gameobject with a mechsounds component attached, to play sounds from _flipper = GameObject.Find("Left Flipper"); _audioSource = _flipper.GetComponent(); - SetAudioProperties(); EditorApplication.update += Update; @@ -76,6 +76,7 @@ private void OnDisable() EditorApplication.update -= Update; } + private void OnDestroy() { } @@ -147,12 +148,19 @@ public override void OnInspectorGUI() GUILayout.EndHorizontal(); - SetAudioProperties(); + //SetAudioProperties(); + DelayedUpdateAudio(); serializedObject.ApplyModifiedProperties(); } + //attempt to update audiomixer pitch shifter value + async void DelayedUpdateAudio() + { + await Task.Delay(1000); + SetAudioProperties(); + } private void SetAudioProperties() { @@ -170,12 +178,12 @@ private void SetAudioProperties() float value; - AudioMixer audioMixer = Resources.Load("SoundMixer"); - AudioMixerGroup[] audioMixGroup = audioMixer.FindMatchingGroups("Master"); - audioMixGroup[0].audioMixer.SetFloat("pitchShifter", speedModifier); - //audioMixGroup[0].audioMixer.GetFloat("pitchShifter", out value); - //Debug.Log(value.ToString()); - _audioSource.outputAudioMixerGroup = audioMixGroup[0]; + //AudioMixer audioMixer = Resources.Load("SoundMixer"); + AudioMixerGroup audioMixGroup = _audioSource.outputAudioMixerGroup;//audioMixer.FindMatchingGroups("Master"); + audioMixGroup.audioMixer.SetFloat("pitchShifter", speedModifier);//speedModifier);//speedModifier); + audioMixGroup.audioMixer.GetFloat("pitchShifter", out value); + Debug.Log(value.ToString()); + //_audioSource.outputAudioMixerGroup = audioMixGroup[0]; } private void Stop() diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs index 7ce3175df..56705dd0d 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -19,11 +19,13 @@ using System.Linq; using NLog; using Unity.Mathematics; +using UnityEditor; using UnityEngine; using UnityEngine.InputSystem; using VisualPinball.Engine.Common; using VisualPinball.Engine.Game; using VisualPinball.Engine.Game.Engines; +using VisualPinball.Engine.VPT; using VisualPinball.Engine.VPT.Trigger; using Color = VisualPinball.Engine.Math.Color; using Logger = NLog.Logger; @@ -179,6 +181,13 @@ private void Start() OnPlayerStarted?.Invoke(this, EventArgs.Empty); } + private void Component_OnSound(object sender, SoundEventArgs e) + { + //throw new NotImplementedException(); + Debug.Log("test"); + + } + private void Update() { OnUpdate?.Invoke(this, EventArgs.Empty); From 78c02399e389ca1e3bcfef8a01dbd10252728699 Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 19 Feb 2023 00:32:16 +0100 Subject: [PATCH 06/76] sound: Simplify sound asset and move play routine from inspector to asset. --- .../VPT/Sounds/SoundInspector.cs | 306 ++++++------------ .../VPT/Sounds/SoundAsset.cs | 78 +++-- 2 files changed, 152 insertions(+), 232 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs index 17dd22bf2..7810b27a8 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs @@ -14,256 +14,144 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -using UnityEngine; +using System.Linq; using UnityEditor; -using UnityEngine.Audio; -using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.SceneManagement; namespace VisualPinball.Unity.Editor { - [CustomEditor(typeof(SoundAsset)), CanEditMultipleObjects] public class SoundsInspector : UnityEditor.Editor { - SerializedProperty Name; - SerializedProperty Description; - SerializedProperty VolumeCorrection; - SerializedProperty Clips; - SerializedProperty ClipSelection; - SerializedProperty RandomizePitch; - SerializedProperty RandomizeSpeed; - SerializedProperty RandomizeVolume; - - private const string _playButtonText = "Play"; - private const string _loopButtonText = "Loop"; - private IconColor _loopButtonColor = IconColor.Gray; - private const string _stopButtonText = "Stop"; - private const float _buttonHeight = 30; - private const float _buttonWidth = 50; - private const float _clipDelayModifier = 1f;//helps set the time between playing audio clips, if the current clip is less than 1 second in length - private int _clipIndex; - private bool _loop = false; - private AudioClip[] _clipArray; - private float _nextStartTime = (float)AudioSettings.dspTime + _clipDelayModifier; - private int _nextClip = 0; - private bool _clipPlaying = false; - private GameObject _flipper; - private AudioSource _audioSource; - - + private SerializedProperty _nameProperty; + private SerializedProperty _descriptionProperty; + private SerializedProperty _volumeCorrectionProperty; + private SerializedProperty _clipsProperty; + private SerializedProperty _clipSelectionProperty; + private SerializedProperty _randomizePitchProperty; + private SerializedProperty _randomizeVolumeProperty; + private SerializedProperty _loopProperty; + + private SoundAsset _soundAsset; + + private AudioSource _editorAudioSource; + //private AudioMixer _editorAudioMixer; + + private const float ButtonHeight = 30; + private const float ButtonWidth = 50; + private void OnEnable() { - Name = serializedObject.FindProperty(nameof(SoundAsset.Name)); - Description = serializedObject.FindProperty(nameof(SoundAsset.Description)); - VolumeCorrection = serializedObject.FindProperty(nameof(SoundAsset.VolumeCorrection)); - Clips = serializedObject.FindProperty(nameof(SoundAsset.Clips)); - ClipSelection = serializedObject.FindProperty(nameof(SoundAsset.ClipSelection)); - RandomizePitch = serializedObject.FindProperty(nameof(SoundAsset.RandomizePitch)); - RandomizeSpeed = serializedObject.FindProperty(nameof(SoundAsset.RandomizeSpeed)); - RandomizeVolume = serializedObject.FindProperty(nameof(SoundAsset.RandomizeVolume)); - - //get a gameobject with a mechsounds component attached, to play sounds from - _flipper = GameObject.Find("Left Flipper"); - _audioSource = _flipper.GetComponent(); - - EditorApplication.update += Update; - - } - - private void OnDisable() - { - EditorApplication.update -= Update; - } - - - private void OnDestroy() - { + _nameProperty = serializedObject.FindProperty(nameof(SoundAsset.Name)); + _descriptionProperty = serializedObject.FindProperty(nameof(SoundAsset.Description)); + _volumeCorrectionProperty = serializedObject.FindProperty(nameof(SoundAsset.VolumeCorrection)); + _clipsProperty = serializedObject.FindProperty(nameof(SoundAsset.Clips)); + _clipSelectionProperty = serializedObject.FindProperty(nameof(SoundAsset.ClipSelection)); + _randomizePitchProperty = serializedObject.FindProperty(nameof(SoundAsset.RandomizePitch)); + _randomizeVolumeProperty = serializedObject.FindProperty(nameof(SoundAsset.RandomizeVolume)); + _loopProperty = serializedObject.FindProperty(nameof(SoundAsset.Loop)); + + _editorAudioSource = GetOrCreateAudioSource(); + //_editorAudioMixer = AssetDatabase.LoadAssetAtPath("Packages/org.visualpinball.engine.unity/VisualPinball.Unity/Assets/Resources/EditorMixer.mixer"); + //_editorAudioSource.outputAudioMixerGroup = _editorAudioMixer.outputAudioMixerGroup; + + _soundAsset = target as SoundAsset; } public override void OnInspectorGUI() { - serializedObject.Update(); - EditorGUILayout.PropertyField(Name, true); + EditorGUILayout.PropertyField(_nameProperty, true); using (var horizontalScope = new GUILayout.HorizontalScope()) { - - EditorGUILayout.PropertyField(Description, GUILayout.Height(100)); + EditorGUILayout.PropertyField(_descriptionProperty, GUILayout.Height(100)); } - EditorGUILayout.PropertyField(VolumeCorrection, true); - EditorGUILayout.PropertyField(Clips); - EditorGUILayout.PropertyField(ClipSelection, true); - EditorGUILayout.PropertyField(RandomizePitch, true); - EditorGUILayout.PropertyField(RandomizeSpeed, true); - EditorGUILayout.PropertyField(RandomizeVolume, true); - - EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); - + EditorGUILayout.PropertyField(_volumeCorrectionProperty, true); + EditorGUILayout.PropertyField(_clipsProperty); + EditorGUILayout.PropertyField(_clipSelectionProperty, true); + EditorGUILayout.PropertyField(_randomizePitchProperty, true); + EditorGUILayout.PropertyField(_randomizeVolumeProperty, true); + EditorGUILayout.PropertyField(_loopProperty); + + serializedObject.ApplyModifiedProperties(); + // center button GUILayout.BeginHorizontal(); - GUILayout.Space(100); - - _clipArray = new AudioClip[Clips.arraySize]; - for (int i = 0; i < Clips.arraySize; i++) - { - _clipArray[i] = (AudioClip)Clips.GetArrayElementAtIndex(i).objectReferenceValue; - } - - - if (GUILayout.Button(new GUIContent(_playButtonText, Icons.PlayButton(IconSize.Small, IconColor.Orange)), GUILayout.Height(_buttonHeight), GUILayout.Width(_buttonWidth))) - { - if (_loop == false && _clipPlaying == false) //play single clip only when loop of clips not playing and current single clip is not already playing - { - if (ClipSelection.intValue == 0) - { PlayRoundRobin(_clipArray); } - else { PlayRandom(_clipArray); } - } + GUILayout.FlexibleSpace(); + if (PlayStopButton()) { + PlayStop(); } - - if (GUILayout.Button(new GUIContent(_loopButtonText, Icons.PlayButton(IconSize.Small, _loopButtonColor)), GUILayout.Height(_buttonHeight), GUILayout.Width(_buttonWidth))) - { - - if (_loop) - { - _loopButtonColor = IconColor.Gray; - Stop(); - } - else - { - _loop = true; - _loopButtonColor = IconColor.Orange; - } - } - - if (GUILayout.Button(new GUIContent(_stopButtonText, Icons.StopButton(IconSize.Small, IconColor.Orange)), GUILayout.Height(_buttonHeight), GUILayout.Width(_buttonWidth))) - { - Stop(); - _loopButtonColor = IconColor.Gray; - } - - + GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); - - //SetAudioProperties(); - DelayedUpdateAudio(); - - serializedObject.ApplyModifiedProperties(); - } - //attempt to update audiomixer pitch shifter value - async void DelayedUpdateAudio() + private bool PlayStopButton() { - await Task.Delay(1000); - SetAudioProperties(); + return _editorAudioSource.isPlaying + ? GUILayout.Button(new GUIContent("Stop", Icons.StopButton(IconSize.Small, IconColor.Orange)), + GUILayout.Height(ButtonHeight), GUILayout.Width(ButtonWidth)) + : GUILayout.Button(new GUIContent("Play", Icons.PlayButton(IconSize.Small, IconColor.Orange)), + GUILayout.Height(ButtonHeight), GUILayout.Width(ButtonWidth)); } - private void SetAudioProperties() - { - - float volume = VolumeCorrection.floatValue; - float pitchModifier = RandomizePitch.floatValue; - float speedModifier = RandomizeSpeed.floatValue; - float volumeModifier = RandomizeVolume.floatValue; - - if (volumeModifier != 1) - { volume = volumeModifier; } - - _audioSource.spatialBlend = 1f; - _audioSource.pitch = pitchModifier; - _audioSource.volume = volume; - - float value; - //AudioMixer audioMixer = Resources.Load("SoundMixer"); - AudioMixerGroup audioMixGroup = _audioSource.outputAudioMixerGroup;//audioMixer.FindMatchingGroups("Master"); - audioMixGroup.audioMixer.SetFloat("pitchShifter", speedModifier);//speedModifier);//speedModifier); - audioMixGroup.audioMixer.GetFloat("pitchShifter", out value); - Debug.Log(value.ToString()); - //_audioSource.outputAudioMixerGroup = audioMixGroup[0]; - - } - private void Stop() + private void PlayStop() { - - if (_flipper != null) - { - _audioSource.Stop(); - - if (_loop) - { - _nextStartTime = (float)AudioSettings.dspTime + _clipDelayModifier; - _loop = false; - } - else - { - _clipPlaying = false; - } + if (_editorAudioSource.isPlaying) { + _soundAsset.Stop(_editorAudioSource); + } else { + _soundAsset.Play(_editorAudioSource); } } - private void PlayRoundRobin(AudioClip[] _clipArray) - { - var nextIndex = _clipIndex + 1 == _clipArray.Length ? 0 : _clipIndex + 1; - PlayClip(_clipArray[nextIndex]); - _clipIndex = nextIndex; - } - - private void PlayRandom(AudioClip[] _clipArray) - { - PlayClip(_clipArray[Random.Range(0, _clipArray.Length)]); - } - - private void PlayClip(AudioClip clip) - { - _audioSource.clip = clip; - _clipPlaying = true; - _audioSource.Play(); - } - private void Update() + /// + /// Gets or creates the editor GameObject for playing sounds in the editor. + /// + /// The hierarchy looks like that: + /// + /// [scene root] + /// | + /// -- EditorScene + /// | + /// -- EditorAudio (with AudioSource component) + /// + /// AudioSource of the editor GameObject for test playing audio. + private static AudioSource GetOrCreateAudioSource() { - - if (_loop) - { - if (AudioSettings.dspTime > _nextStartTime - 1) - { - int index = ClipSelection.intValue == 0 ? _nextClip : Random.Range(0, _clipArray.Length); - _audioSource.clip = _clipArray[index]; - AudioClip clipToPlay = _audioSource.clip; - _audioSource.PlayScheduled(_nextStartTime); - - // Checks how long the Clip will last and updates the Next Start Time with a new value - float duration = clipToPlay.samples / clipToPlay.frequency; - float clipLength = clipToPlay.length; - - if (clipLength < 1) - { clipLength = clipLength + _clipDelayModifier; } + // todo check whether we'll instantiate those live in the future or rely on a provided prefab + var editorSceneGo = SceneManager.GetActiveScene().GetRootGameObjects() + .FirstOrDefault(go => go.name == "EditorScene"); - _nextStartTime = _nextStartTime + clipLength; - - // Increase the clip index number, reset if it runs out of clips - _nextClip = _nextClip < _clipArray.Length - 1 ? _nextClip + 1 : 0; - } - - } - - else - { - if (_flipper != null) - { - if (_audioSource.isPlaying == false) - { - Stop(); + if (editorSceneGo == null) { + editorSceneGo = new GameObject("EditorScene"); + } - } + GameObject editorAudioGo = null; + for (var i = 0; i < editorSceneGo.transform.childCount; i++) { + var go = editorSceneGo.transform.GetChild(i).gameObject; + if (go.name != "EditorAudio") { + continue; } + editorAudioGo = go; + break; + } + if (editorAudioGo == null) { + editorAudioGo = new GameObject("EditorAudio"); + editorAudioGo.transform.SetParent(editorSceneGo.transform); + } + + var audioSource = editorAudioGo.GetComponent(); + if (!audioSource) { + audioSource = editorAudioGo.AddComponent(); } - } + return audioSource; + } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs index 2757f3ef3..d63d7f35a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs @@ -14,29 +14,26 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// ReSharper disable InconsistentNaming -using UnityEngine; using System; -using System.Collections.Generic; +using UnityEngine; +using Random = UnityEngine.Random; namespace VisualPinball.Unity { - /// - - /// [CreateAssetMenu(fileName = "Sound", menuName = "Visual Pinball/Sound", order = 102)] public class SoundAsset : ScriptableObject { + #region Properties + public string Name; - [Space(15)] public string Description; - [Range(0,1)] - [Space(15)] + [Range(0, 1)] public float VolumeCorrection = 1; //audio clips in unity have a volume range of 0 to 1 - [Space(15)] public AudioClip[] Clips; public enum Selection @@ -45,28 +42,63 @@ public enum Selection Random } - [Space(15)] public Selection ClipSelection; - [Range(0.1f, 2f)] - [Space(15)] - public float RandomizePitch = 1; + [Range(0, 0.3f)] + public float RandomizePitch; - [Range(0.1f, 2f)] - [Space(15)] - public float RandomizeSpeed = 1; + // todo needs to go through the mixer + // [Range(0, 0.3f)] + // public float RandomizeSpeed; - [Range(0, 1f)] - [Space(15)] - public float RandomizeVolume = 1; + [Range(0, 0.5f)] + public float RandomizeVolume; - void OnValidate() - { + public bool Loop; - } + #endregion + + #region Runtime + private int _clipIndex; + + #endregion + + public void Play(AudioSource audioSource) + { + if (Clips.Length == 0) { + return; + } + audioSource.volume = Volume; + audioSource.pitch = Pitch; + audioSource.loop = Loop; + audioSource.clip = GetClip(); + audioSource.Play(); + } + + public void Stop(AudioSource audioSource) + { + audioSource.Stop(); + } + private float Pitch => 1f + Random.Range(-RandomizePitch / 2, RandomizePitch / 2); + private float Volume => VolumeCorrection - Random.Range(0, RandomizeVolume); + + private AudioClip GetClip() + { + switch (ClipSelection) { + case Selection.RoundRobin: + var clip = Clips[_clipIndex]; + _clipIndex = (_clipIndex + 1) % Clips.Length; + return clip; + + case Selection.Random: + return Clips[Random.Range(0, Clips.Length)]; + + default: + throw new ArgumentOutOfRangeException(); + } + } } - } From 3d4848ef73bbb0e6798b635078d7203e8e6ddcc4 Mon Sep 17 00:00:00 2001 From: freezy Date: Wed, 1 Mar 2023 22:31:49 +0100 Subject: [PATCH 07/76] sound: Refactor editor classes. --- VisualPinball.Engine/VPT/ISoundEmitter.cs | 53 --- .../Inspectors/MechSoundDrawer.cs | 135 -------- .../Inspectors/MechSoundsInspector.cs | 95 ------ .../VisualPinball.Unity.Editor/Sound.meta | 3 + .../Sound/MechSoundDrawer.cs | 87 +++++ .../MechSoundDrawer.cs.meta | 22 +- .../Sound/MechSoundsInspector.cs | 61 ++++ .../MechSoundsInspector.cs.meta | 22 +- .../SoundAssetInspector.cs} | 314 +++++++++--------- .../SoundAssetInspector.cs.meta} | 22 +- .../VPT/Sounds.meta | 8 - .../VisualPinball.Unity/Sound.meta | 3 + .../Sound/ISoundEmitter.cs | 62 ++++ .../Sound}/ISoundEmitter.cs.meta | 22 +- .../VisualPinball.Unity/Sound/MechSound.cs | 43 +++ .../Sound/MechSound.cs.meta | 3 + .../MechSoundsComponent.cs | 94 ++---- .../MechSoundsComponent.cs.meta | 0 .../{VPT/Sounds => Sound}/SoundAsset.cs | 208 ++++++------ .../{VPT/Sounds => Sound}/SoundAsset.cs.meta | 0 .../VisualPinball.Unity/Sound/SoundTrigger.cs | 39 +++ .../Sound/SoundTrigger.cs.meta | 3 + .../Sound/VolumeEmitter.cs | 31 ++ .../Sound/VolumeEmitter.cs.meta | 3 + .../VPT/Flipper/FlipperComponent.cs | 18 +- .../VisualPinball.Unity/VPT/Sounds.meta | 8 - .../VPT/Sounds/GameSounds.meta | 8 - .../VPT/Sounds/MechSounds.meta | 8 - 28 files changed, 687 insertions(+), 688 deletions(-) delete mode 100644 VisualPinball.Engine/VPT/ISoundEmitter.cs delete mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs delete mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Sound.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundDrawer.cs rename VisualPinball.Unity/VisualPinball.Unity.Editor/{Inspectors => Sound}/MechSoundDrawer.cs.meta (95%) create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs rename VisualPinball.Unity/VisualPinball.Unity.Editor/{Inspectors => Sound}/MechSoundsInspector.cs.meta (95%) rename VisualPinball.Unity/VisualPinball.Unity.Editor/{VPT/Sounds/SoundInspector.cs => Sound/SoundAssetInspector.cs} (96%) rename VisualPinball.Unity/VisualPinball.Unity.Editor/{VPT/Sounds/SoundInspector.cs.meta => Sound/SoundAssetInspector.cs.meta} (95%) delete mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Sound.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs rename {VisualPinball.Engine/VPT => VisualPinball.Unity/VisualPinball.Unity/Sound}/ISoundEmitter.cs.meta (95%) create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs.meta rename VisualPinball.Unity/VisualPinball.Unity/{VPT/Sounds/MechSounds => Sound}/MechSoundsComponent.cs (62%) rename VisualPinball.Unity/VisualPinball.Unity/{VPT/Sounds/MechSounds => Sound}/MechSoundsComponent.cs.meta (100%) rename VisualPinball.Unity/VisualPinball.Unity/{VPT/Sounds => Sound}/SoundAsset.cs (95%) rename VisualPinball.Unity/VisualPinball.Unity/{VPT/Sounds => Sound}/SoundAsset.cs.meta (100%) create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Sound/SoundTrigger.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Sound/SoundTrigger.cs.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Sound/VolumeEmitter.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Sound/VolumeEmitter.cs.meta delete mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds.meta delete mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/GameSounds.meta delete mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds.meta diff --git a/VisualPinball.Engine/VPT/ISoundEmitter.cs b/VisualPinball.Engine/VPT/ISoundEmitter.cs deleted file mode 100644 index cfec15ad5..000000000 --- a/VisualPinball.Engine/VPT/ISoundEmitter.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Visual Pinball Engine -// Copyright (C) 2023 freezy and VPE Team -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -using System; -using System.Collections.Generic; -using Unity.VisualScripting.YamlDotNet.Core.Tokens; -using UnityEngine; - -namespace VisualPinball.Engine.VPT -{ - //Implemented in components relating to sounds, including the Mechanical Sounds component. - public interface ISoundEmitter - { - SoundTrigger[] AvailableTriggers { get;} - VolumeEmitter[] GetVolumeEmitters(SoundTrigger trigger); - event EventHandler OnSound; - } - - public struct SoundTrigger - { - public string Id; - public string Name; - - } - - //The emitter for the sound trigger. Determines how the volume will be calculated. - //Example: The "Fixed" volume emitter would be an emitter for the "Coil On" trigger. Same volume as configured by the sound asset. - //Example: The "Ball Velocity" volume emitter would be an emitter for the "Ball Collision" trigger. Volume depends on the ball velocity upon collision. - public struct VolumeEmitter - { - public string Id; - public string Name; - } - - public struct SoundEventArgs - { - public SoundTrigger Trigger; - public float Volume; - } -} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs deleted file mode 100644 index 76010ec92..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs +++ /dev/null @@ -1,135 +0,0 @@ -// Visual Pinball Engine -// Copyright (C) 2023 freezy and VPE Team -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -using Unity.Entities; -using UnityEditor; -using UnityEngine; -using VisualPinball.Engine.VPT; -using static VisualPinball.Unity.MechSoundsComponent; - -namespace VisualPinball.Unity.Editor -{ - [CustomPropertyDrawer(typeof(MechSound))] - public class MechSoundDrawer : PropertyDrawer - { - private const float _buttonWidth = 150; - private SoundTrigger[] _availiableTriggers; - private int _selTrigger; - private VolumeEmitter[] _availableEmitters; - private const string _volEmitter = "fixed"; - private const string _volEmitterName = "Fixed"; - - public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) - { - MechSoundsComponent ob = (MechSoundsComponent)property.serializedObject.targetObject; - GameObject go = ob.gameObject; - ISoundEmitter component = ob.GetComponent(); - - EditorGUI.BeginProperty(position, label, property); - - EditorGUI.LabelField(position, label); - - var _soundProperty = property.FindPropertyRelative("Sound"); - var _volumeProperty = property.FindPropertyRelative("VolumeValue"); - var _actionSelectionProperty = property.FindPropertyRelative("Action"); - var _fadeProperty = property.FindPropertyRelative("Fade"); - - _availiableTriggers = component.AvailableTriggers; - _selTrigger = EditorGUILayout.Popup("Trigger", _selTrigger, GetTriggerOptions(_availiableTriggers)); - - ob.SelectedTrigger = GetSelectedTrigger(_selTrigger); - _availableEmitters = component.GetVolumeEmitters(ob.SelectedTrigger); - - EditorGUILayout.Space(5); - _soundProperty.objectReferenceValue = EditorGUILayout.ObjectField("Sound", _soundProperty.objectReferenceValue, typeof(SoundAsset), true); - - EditorGUILayout.Space(5); - EditorGUILayout.Popup(new GUIContent("Volume", "Depends on trigger selected: \n 'Fixed'-Not dependent on any playfield action. \n 'Ball Velocity'- Gameplay-related (collision)."), 0, GetEmitterOptions(_availableEmitters)); - - EditorGUILayout.Space(5); - EditorGUILayout.BeginHorizontal(); - GUILayout.Label("", GUILayout.Width(EditorGUIUtility.labelWidth)); - _volumeProperty.floatValue = EditorGUILayout.Slider("", _volumeProperty.floatValue, 0.1f, 2); - EditorGUILayout.EndHorizontal(); - - EditorGUILayout.Space(5); - EditorGUILayout.PropertyField(_actionSelectionProperty); - - EditorGUILayout.Space(5); - EditorGUILayout.BeginHorizontal(); - _fadeProperty.floatValue = EditorGUILayout.Slider("Fade", _fadeProperty.floatValue, 0, 300); - GUILayout.Label("ms", new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Bold }); - EditorGUILayout.EndHorizontal(); - - EditorGUI.EndProperty(); - - - } - - - #region Set methods - - private string[] GetTriggerOptions(SoundTrigger[] triggers) - { - int index = triggers.Length; - string[] options = new string[index]; - - for (int i = 0; i < index; i++) - { - options[i] = triggers[i].Name; - } - - return options; - } - - - private SoundTrigger GetSelectedTrigger(int index) - { - SoundTrigger sTrigger = new SoundTrigger(); - sTrigger.Id = _availiableTriggers[index].Id; - sTrigger.Name = _availiableTriggers[index].Name; - - return sTrigger; - } - - private string[] GetEmitterOptions(VolumeEmitter[] volEmitters) - { - string[] options; - - if (volEmitters == null) - { - options = new string[] {_volEmitterName}; //'fixed' by default - } - else - { - - int index = volEmitters.Length; - options = new string[index]; - - for (int i = 0; i < index; i++) - { - options[i] = volEmitters[i].Name; - } - - } - - - return options; - } - #endregion - - } -} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs deleted file mode 100644 index 7bce1fa13..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Visual Pinball Engine -// Copyright (C) 2023 freezy and VPE Team -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -using UnityEngine; -using UnityEditor; -using static VisualPinball.Unity.MechSoundsComponent; -using VisualPinball.Engine.VPT; - -namespace VisualPinball.Unity.Editor -{ - [CustomEditor(typeof(MechSoundsComponent)), CanEditMultipleObjects] - public class MechanicalSoundInspector : UnityEditor.Editor - { - - private MechSoundsComponent _myTarget; - private SerializedProperty _soundList; - private const float _buttonWidth = 150; - - private void OnEnable() - { - - _myTarget = (MechSoundsComponent)target; - _soundList = serializedObject.FindProperty(nameof(MechSoundsComponent.SoundList)); - - } - - public override void OnInspectorGUI() - { - - MechSoundsComponent ob = (MechSoundsComponent)serializedObject.targetObject; - GameObject go = ob.gameObject; - - ISoundEmitter component = ob.GetComponent(); - if (component == null) - { - if(EditorUtility.DisplayDialog("Error", "No component attached to this game object implements ISoundEmitter interface.", "ok")) - { return; } - - } - - serializedObject.Update(); - - EditorGUILayout.LabelField("Current Sounds"); - - for (int i = 0; i < _soundList.arraySize; i++) - { - EditorGUILayout.PropertyField(_soundList.GetArrayElementAtIndex(i), GUIContent.none); - EditorGUILayout.Space(15); - GUILayout.BeginHorizontal(); - GUILayout.FlexibleSpace(); - if (GUILayout.Button("Remove this sound", GUILayout.Width(_buttonWidth))) - { - _soundList.DeleteArrayElementAtIndex(i); - - } - GUILayout.FlexibleSpace(); - GUILayout.EndHorizontal(); - - EditorGUILayout.Space(); - EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); - } - - EditorGUILayout.Space(); - EditorGUILayout.Space(); - - GUILayout.BeginHorizontal(); - GUILayout.FlexibleSpace(); - if (GUILayout.Button("Add a new sound", GUILayout.Width(_buttonWidth))) - { - _myTarget.SoundList.Add(new MechSound()); - - } - GUILayout.FlexibleSpace(); - GUILayout.EndHorizontal(); - - serializedObject.ApplyModifiedProperties(); - } - - - - } -} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound.meta new file mode 100644 index 000000000..dc4a50b28 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d6280d7fb0f340b09b058831259ab274 +timeCreated: 1677682143 \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundDrawer.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundDrawer.cs new file mode 100644 index 000000000..807eaa33f --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundDrawer.cs @@ -0,0 +1,87 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace VisualPinball.Unity.Editor +{ + [CustomPropertyDrawer(typeof(MechSound))] + public class MechSoundDrawer : PropertyDrawer + { + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + return (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing) * 5 + 4f; + } + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + // retrieve reference to GO and component + var mechSoundsComponent = (MechSoundsComponent)property.serializedObject.targetObject; + var soundEmitter = mechSoundsComponent.GetComponent(); + + EditorGUI.BeginProperty(position, label, property); + + // init height + position.height = EditorGUIUtility.singleLineHeight; + + // trigger drop-down + var triggerIdProperty = property.FindPropertyRelative(nameof(MechSound.TriggerId)); + var triggers = soundEmitter.AvailableTriggers; + if (triggers.Length > 0) { + var triggerIndex = triggers.ToList().FindIndex(t => t.Id == triggerIdProperty.stringValue); + if (triggerIndex == -1) { // pre-select first trigger in list, if none set. + triggerIndex = 0; + } + EditorGUI.BeginChangeCheck(); + triggerIndex = EditorGUI.Popup(position, "Trigger on", triggerIndex, triggers.Select(t => t.Name).ToArray()); + if (EditorGUI.EndChangeCheck()) { + triggerIdProperty.stringValue = triggers[triggerIndex].Id; + } + } else { + EditorGUI.LabelField(position, "No Triggers found."); + } + position.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + + // sound object picker + var soundProperty = property.FindPropertyRelative(nameof(MechSound.Sound)); + EditorGUI.BeginChangeCheck(); + var soundValue = EditorGUI.ObjectField(position, "Sound", soundProperty.objectReferenceValue, typeof(SoundAsset), true); + if (EditorGUI.EndChangeCheck()) { + soundProperty.objectReferenceValue = soundValue; + } + position.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + + // volume + var volumeProperty = property.FindPropertyRelative(nameof(MechSound.Volume)); + EditorGUI.PropertyField(position, volumeProperty); + position.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + + // action + var actionProperty = property.FindPropertyRelative(nameof(MechSound.Action)); + EditorGUI.PropertyField(position, actionProperty); + position.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + + // fade + var fadeProperty = property.FindPropertyRelative(nameof(MechSound.Fade)); + EditorGUI.PropertyField(position, fadeProperty); + position.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + + EditorGUI.EndProperty(); + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundDrawer.cs.meta similarity index 95% rename from VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundDrawer.cs.meta index 21419ad3a..67e6c6874 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundDrawer.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundDrawer.cs.meta @@ -1,11 +1,11 @@ -fileFormatVersion: 2 -guid: 884fb5b527309ef489e8b27aa9e4809d -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: +fileFormatVersion: 2 +guid: 884fb5b527309ef489e8b27aa9e4809d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs new file mode 100644 index 000000000..66e38ca81 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs @@ -0,0 +1,61 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using UnityEditor; +using UnityEngine; + +namespace VisualPinball.Unity.Editor +{ + [CustomEditor(typeof(MechSoundsComponent)), CanEditMultipleObjects] + public class MechanicalSoundInspector : UnityEditor.Editor + { + private SerializedProperty _soundsProperty; + + private void OnEnable() + { + _soundsProperty = serializedObject.FindProperty(nameof(MechSoundsComponent.Sounds)); + + var comp = target as MechSoundsComponent; + var audioSource = comp!.GetComponent(); + if (audioSource != null) { + audioSource.playOnAwake = false; + } + } + + public override void OnInspectorGUI() + { + var comp = target as MechSoundsComponent; + + var soundEmitter = comp!.GetComponent(); + if (soundEmitter == null) { + EditorGUILayout.HelpBox("Cannot find sound emitter. This component only works with a sound emitter on the same GameObject.", MessageType.Error); + return; + } + + var audioSource = comp.GetComponent(); + if (audioSource == null) { + EditorGUILayout.HelpBox("Cannot find audio source. This component only works with an audio source on the same GameObject.", MessageType.Error); + return; + } + + serializedObject.Update(); + + EditorGUILayout.PropertyField(_soundsProperty); + + serializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs.meta similarity index 95% rename from VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs.meta index 19facfd5d..3c428cf24 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/MechSoundsInspector.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs.meta @@ -1,11 +1,11 @@ -fileFormatVersion: 2 -guid: 8dca340821f92c74e8cf97cd3fff29df -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: +fileFormatVersion: 2 +guid: 8dca340821f92c74e8cf97cd3fff29df +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundAssetInspector.cs similarity index 96% rename from VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs rename to VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundAssetInspector.cs index 7810b27a8..b9d2c3d90 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundAssetInspector.cs @@ -1,157 +1,157 @@ -// Visual Pinball Engine -// Copyright (C) 2023 freezy and VPE Team -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -using System.Linq; -using UnityEditor; -using UnityEngine; -using UnityEngine.SceneManagement; - -namespace VisualPinball.Unity.Editor -{ - - [CustomEditor(typeof(SoundAsset)), CanEditMultipleObjects] - public class SoundsInspector : UnityEditor.Editor - { - private SerializedProperty _nameProperty; - private SerializedProperty _descriptionProperty; - private SerializedProperty _volumeCorrectionProperty; - private SerializedProperty _clipsProperty; - private SerializedProperty _clipSelectionProperty; - private SerializedProperty _randomizePitchProperty; - private SerializedProperty _randomizeVolumeProperty; - private SerializedProperty _loopProperty; - - private SoundAsset _soundAsset; - - private AudioSource _editorAudioSource; - //private AudioMixer _editorAudioMixer; - - private const float ButtonHeight = 30; - private const float ButtonWidth = 50; - - private void OnEnable() - { - _nameProperty = serializedObject.FindProperty(nameof(SoundAsset.Name)); - _descriptionProperty = serializedObject.FindProperty(nameof(SoundAsset.Description)); - _volumeCorrectionProperty = serializedObject.FindProperty(nameof(SoundAsset.VolumeCorrection)); - _clipsProperty = serializedObject.FindProperty(nameof(SoundAsset.Clips)); - _clipSelectionProperty = serializedObject.FindProperty(nameof(SoundAsset.ClipSelection)); - _randomizePitchProperty = serializedObject.FindProperty(nameof(SoundAsset.RandomizePitch)); - _randomizeVolumeProperty = serializedObject.FindProperty(nameof(SoundAsset.RandomizeVolume)); - _loopProperty = serializedObject.FindProperty(nameof(SoundAsset.Loop)); - - _editorAudioSource = GetOrCreateAudioSource(); - //_editorAudioMixer = AssetDatabase.LoadAssetAtPath("Packages/org.visualpinball.engine.unity/VisualPinball.Unity/Assets/Resources/EditorMixer.mixer"); - //_editorAudioSource.outputAudioMixerGroup = _editorAudioMixer.outputAudioMixerGroup; - - _soundAsset = target as SoundAsset; - } - - public override void OnInspectorGUI() - { - serializedObject.Update(); - - EditorGUILayout.PropertyField(_nameProperty, true); - - using (var horizontalScope = new GUILayout.HorizontalScope()) - { - EditorGUILayout.PropertyField(_descriptionProperty, GUILayout.Height(100)); - } - - EditorGUILayout.PropertyField(_volumeCorrectionProperty, true); - EditorGUILayout.PropertyField(_clipsProperty); - EditorGUILayout.PropertyField(_clipSelectionProperty, true); - EditorGUILayout.PropertyField(_randomizePitchProperty, true); - EditorGUILayout.PropertyField(_randomizeVolumeProperty, true); - EditorGUILayout.PropertyField(_loopProperty); - - serializedObject.ApplyModifiedProperties(); - - // center button - GUILayout.BeginHorizontal(); - GUILayout.FlexibleSpace(); - if (PlayStopButton()) { - PlayStop(); - } - GUILayout.FlexibleSpace(); - GUILayout.EndHorizontal(); - } - - private bool PlayStopButton() - { - return _editorAudioSource.isPlaying - ? GUILayout.Button(new GUIContent("Stop", Icons.StopButton(IconSize.Small, IconColor.Orange)), - GUILayout.Height(ButtonHeight), GUILayout.Width(ButtonWidth)) - : GUILayout.Button(new GUIContent("Play", Icons.PlayButton(IconSize.Small, IconColor.Orange)), - GUILayout.Height(ButtonHeight), GUILayout.Width(ButtonWidth)); - } - - private void PlayStop() - { - if (_editorAudioSource.isPlaying) { - _soundAsset.Stop(_editorAudioSource); - } else { - _soundAsset.Play(_editorAudioSource); - } - } - - /// - /// Gets or creates the editor GameObject for playing sounds in the editor. - /// - /// The hierarchy looks like that: - /// - /// [scene root] - /// | - /// -- EditorScene - /// | - /// -- EditorAudio (with AudioSource component) - /// - /// AudioSource of the editor GameObject for test playing audio. - private static AudioSource GetOrCreateAudioSource() - { - // todo check whether we'll instantiate those live in the future or rely on a provided prefab - var editorSceneGo = SceneManager.GetActiveScene().GetRootGameObjects() - .FirstOrDefault(go => go.name == "EditorScene"); - - if (editorSceneGo == null) { - editorSceneGo = new GameObject("EditorScene"); - } - - GameObject editorAudioGo = null; - for (var i = 0; i < editorSceneGo.transform.childCount; i++) { - var go = editorSceneGo.transform.GetChild(i).gameObject; - if (go.name != "EditorAudio") { - continue; - } - editorAudioGo = go; - break; - } - - if (editorAudioGo == null) { - editorAudioGo = new GameObject("EditorAudio"); - editorAudioGo.transform.SetParent(editorSceneGo.transform); - } - - var audioSource = editorAudioGo.GetComponent(); - if (!audioSource) { - audioSource = editorAudioGo.AddComponent(); - } - - return audioSource; - } - } -} - +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace VisualPinball.Unity.Editor +{ + + [CustomEditor(typeof(SoundAsset)), CanEditMultipleObjects] + public class SoundAssetInspector : UnityEditor.Editor + { + private SerializedProperty _nameProperty; + private SerializedProperty _descriptionProperty; + private SerializedProperty _volumeCorrectionProperty; + private SerializedProperty _clipsProperty; + private SerializedProperty _clipSelectionProperty; + private SerializedProperty _randomizePitchProperty; + private SerializedProperty _randomizeVolumeProperty; + private SerializedProperty _loopProperty; + + private SoundAsset _soundAsset; + + private AudioSource _editorAudioSource; + //private AudioMixer _editorAudioMixer; + + private const float ButtonHeight = 30; + private const float ButtonWidth = 50; + + private void OnEnable() + { + _nameProperty = serializedObject.FindProperty(nameof(SoundAsset.Name)); + _descriptionProperty = serializedObject.FindProperty(nameof(SoundAsset.Description)); + _volumeCorrectionProperty = serializedObject.FindProperty(nameof(SoundAsset.VolumeCorrection)); + _clipsProperty = serializedObject.FindProperty(nameof(SoundAsset.Clips)); + _clipSelectionProperty = serializedObject.FindProperty(nameof(SoundAsset.ClipSelection)); + _randomizePitchProperty = serializedObject.FindProperty(nameof(SoundAsset.RandomizePitch)); + _randomizeVolumeProperty = serializedObject.FindProperty(nameof(SoundAsset.RandomizeVolume)); + _loopProperty = serializedObject.FindProperty(nameof(SoundAsset.Loop)); + + _editorAudioSource = GetOrCreateAudioSource(); + //_editorAudioMixer = AssetDatabase.LoadAssetAtPath("Packages/org.visualpinball.engine.unity/VisualPinball.Unity/Assets/Resources/EditorMixer.mixer"); + //_editorAudioSource.outputAudioMixerGroup = _editorAudioMixer.outputAudioMixerGroup; + + _soundAsset = target as SoundAsset; + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.PropertyField(_nameProperty, true); + + using (var horizontalScope = new GUILayout.HorizontalScope()) + { + EditorGUILayout.PropertyField(_descriptionProperty, GUILayout.Height(100)); + } + + EditorGUILayout.PropertyField(_volumeCorrectionProperty, true); + EditorGUILayout.PropertyField(_clipsProperty); + EditorGUILayout.PropertyField(_clipSelectionProperty, true); + EditorGUILayout.PropertyField(_randomizePitchProperty, true); + EditorGUILayout.PropertyField(_randomizeVolumeProperty, true); + EditorGUILayout.PropertyField(_loopProperty); + + serializedObject.ApplyModifiedProperties(); + + // center button + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (PlayStopButton()) { + PlayStop(); + } + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + } + + private bool PlayStopButton() + { + return _editorAudioSource.isPlaying + ? GUILayout.Button(new GUIContent("Stop", Icons.StopButton(IconSize.Small, IconColor.Orange)), + GUILayout.Height(ButtonHeight), GUILayout.Width(ButtonWidth)) + : GUILayout.Button(new GUIContent("Play", Icons.PlayButton(IconSize.Small, IconColor.Orange)), + GUILayout.Height(ButtonHeight), GUILayout.Width(ButtonWidth)); + } + + private void PlayStop() + { + if (_editorAudioSource.isPlaying) { + _soundAsset.Stop(_editorAudioSource); + } else { + _soundAsset.Play(_editorAudioSource); + } + } + + /// + /// Gets or creates the editor GameObject for playing sounds in the editor. + /// + /// The hierarchy looks like that: + /// + /// [scene root] + /// | + /// -- EditorScene + /// | + /// -- EditorAudio (with AudioSource component) + /// + /// AudioSource of the editor GameObject for test playing audio. + private static AudioSource GetOrCreateAudioSource() + { + // todo check whether we'll instantiate those live in the future or rely on a provided prefab + var editorSceneGo = SceneManager.GetActiveScene().GetRootGameObjects() + .FirstOrDefault(go => go.name == "EditorScene"); + + if (editorSceneGo == null) { + editorSceneGo = new GameObject("EditorScene"); + } + + GameObject editorAudioGo = null; + for (var i = 0; i < editorSceneGo.transform.childCount; i++) { + var go = editorSceneGo.transform.GetChild(i).gameObject; + if (go.name != "EditorAudio") { + continue; + } + editorAudioGo = go; + break; + } + + if (editorAudioGo == null) { + editorAudioGo = new GameObject("EditorAudio"); + editorAudioGo.transform.SetParent(editorSceneGo.transform); + } + + var audioSource = editorAudioGo.GetComponent(); + if (!audioSource) { + audioSource = editorAudioGo.AddComponent(); + } + + return audioSource; + } + } +} + diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundAssetInspector.cs.meta similarity index 95% rename from VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundAssetInspector.cs.meta index 723eb9c7c..9530058fc 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds/SoundInspector.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundAssetInspector.cs.meta @@ -1,11 +1,11 @@ -fileFormatVersion: 2 -guid: bace99bbc8f020f49b66c0ce06780514 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: +fileFormatVersion: 2 +guid: bace99bbc8f020f49b66c0ce06780514 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds.meta deleted file mode 100644 index 6042bc759..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Sounds.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 8dc2c04b7ef683d4a8076a16ebddc298 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound.meta b/VisualPinball.Unity/VisualPinball.Unity/Sound.meta new file mode 100644 index 000000000..6554ff4e7 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 24e3e95b7dc44c3ebd511ab4c4194a65 +timeCreated: 1677678507 \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs new file mode 100644 index 000000000..138b6b522 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs @@ -0,0 +1,62 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; + +namespace VisualPinball.Unity +{ + /// + /// An interface for item components that emit mechanical sounds. + /// + public interface ISoundEmitter + { + /// + /// A list of triggers that can be linked to emitting a sound. + /// + SoundTrigger[] AvailableTriggers { get; } + + /// + /// For each , returns whether their + /// sound is volume-dependent on the triggering event. + /// + /// Sound trigger ID + /// Assigned volume emitter. null if fixed volume + VolumeEmitter GetVolumeEmitter(string triggerId); + + /// + /// The sound event, to which the subscribes to. + /// + event EventHandler OnSound; + } + + public readonly struct SoundEventArgs + { + public readonly SoundTrigger Trigger; + public readonly float Volume; + + public SoundEventArgs(SoundTrigger trigger) + { + Trigger = trigger; + Volume = 1f; + } + + public SoundEventArgs(SoundTrigger trigger, float volume) + { + Trigger = trigger; + Volume = volume; + } + } +} diff --git a/VisualPinball.Engine/VPT/ISoundEmitter.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs.meta similarity index 95% rename from VisualPinball.Engine/VPT/ISoundEmitter.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs.meta index c4022772e..ca209a4c5 100644 --- a/VisualPinball.Engine/VPT/ISoundEmitter.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs.meta @@ -1,11 +1,11 @@ -fileFormatVersion: 2 -guid: 7129feb5a774383458adb84d3b08fabe -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: +fileFormatVersion: 2 +guid: 7129feb5a774383458adb84d3b08fabe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs new file mode 100644 index 000000000..4ae50cce9 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs @@ -0,0 +1,43 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// ReSharper disable InconsistentNaming + +using System; +using UnityEngine; + +namespace VisualPinball.Unity +{ + [Serializable] + public class MechSound + { + [SerializeReference] + public ScriptableObject Sound; + + public string TriggerId; + + [Range(0, 1)] + public float Volume = 1; + + public MechSoundAction Action = MechSoundAction.Play; + + [Min(0)] + [Unit("ms")] + public float Fade; + } + + public enum MechSoundAction { Play, Stop }; +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs.meta new file mode 100644 index 000000000..4917bf27a --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e750cca599b34fd1aa5c29d5b38788f4 +timeCreated: 1677682479 \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs similarity index 62% rename from VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs rename to VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs index 8620289d6..cd38fd303 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs @@ -1,63 +1,31 @@ -// Visual Pinball Engine -// Copyright (C) 2023 freezy and VPE Team -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - - -using System; -using System.Collections.Generic; -using UnityEngine; -using VisualPinball.Engine.VPT; - -namespace VisualPinball.Unity -{ - - [AddComponentMenu("Visual Pinball/Sounds/Mechanical Sounds")] - [RequireComponent(typeof(AudioSource))] - public class MechSoundsComponent : MonoBehaviour - { - #region Data - - public SoundTrigger SelectedTrigger; - public float Volume; - - [Serializable] - public class MechSound - { - public ScriptableObject Sound; - public float VolumeValue = 1; - public actionType Action = actionType.PlayOnce; - public float Fade = 50; - - } - - void AddNew() - { - SoundList.Add(new MechSound()); - } - - void Remove(int index) - { - SoundList.RemoveAt(index); - } - - - public List SoundList = new List(1); - public enum actionType { PlayOnce, Loop }; - - #endregion - - } -} - +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// ReSharper disable InconsistentNaming + +using System.Collections.Generic; +using UnityEngine; + +namespace VisualPinball.Unity +{ + [AddComponentMenu("Visual Pinball/Sounds/Mechanical Sounds")] + [RequireComponent(typeof(AudioSource))] + public class MechSoundsComponent : MonoBehaviour + { + public List Sounds = new(); + } +} + diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs.meta similarity index 100% rename from VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds/MechSoundsComponent.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs.meta diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundAsset.cs similarity index 95% rename from VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs rename to VisualPinball.Unity/VisualPinball.Unity/Sound/SoundAsset.cs index d63d7f35a..d5e82ac3a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundAsset.cs @@ -1,104 +1,104 @@ -// Visual Pinball Engine -// Copyright (C) 2023 freezy and VPE Team -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// ReSharper disable InconsistentNaming - -using System; -using UnityEngine; -using Random = UnityEngine.Random; - -namespace VisualPinball.Unity -{ - [CreateAssetMenu(fileName = "Sound", menuName = "Visual Pinball/Sound", order = 102)] - public class SoundAsset : ScriptableObject - { - - #region Properties - - public string Name; - public string Description; - - [Range(0, 1)] - public float VolumeCorrection = 1; //audio clips in unity have a volume range of 0 to 1 - - public AudioClip[] Clips; - - public enum Selection - { - RoundRobin, - Random - } - - public Selection ClipSelection; - - [Range(0, 0.3f)] - public float RandomizePitch; - - // todo needs to go through the mixer - // [Range(0, 0.3f)] - // public float RandomizeSpeed; - - [Range(0, 0.5f)] - public float RandomizeVolume; - - public bool Loop; - - #endregion - - #region Runtime - - private int _clipIndex; - - #endregion - - public void Play(AudioSource audioSource) - { - if (Clips.Length == 0) { - return; - } - audioSource.volume = Volume; - audioSource.pitch = Pitch; - audioSource.loop = Loop; - audioSource.clip = GetClip(); - audioSource.Play(); - } - - - public void Stop(AudioSource audioSource) - { - audioSource.Stop(); - } - - private float Pitch => 1f + Random.Range(-RandomizePitch / 2, RandomizePitch / 2); - private float Volume => VolumeCorrection - Random.Range(0, RandomizeVolume); - - private AudioClip GetClip() - { - switch (ClipSelection) { - case Selection.RoundRobin: - var clip = Clips[_clipIndex]; - _clipIndex = (_clipIndex + 1) % Clips.Length; - return clip; - - case Selection.Random: - return Clips[Random.Range(0, Clips.Length)]; - - default: - throw new ArgumentOutOfRangeException(); - } - } - } -} +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// ReSharper disable InconsistentNaming + +using System; +using UnityEngine; +using Random = UnityEngine.Random; + +namespace VisualPinball.Unity +{ + [CreateAssetMenu(fileName = "Sound", menuName = "Visual Pinball/Sound", order = 102)] + public class SoundAsset : ScriptableObject + { + + #region Properties + + public string Name; + public string Description; + + [Range(0, 1)] + public float VolumeCorrection = 1; //audio clips in unity have a volume range of 0 to 1 + + public AudioClip[] Clips; + + public enum Selection + { + RoundRobin, + Random + } + + public Selection ClipSelection; + + [Range(0, 0.3f)] + public float RandomizePitch; + + // todo needs to go through the mixer + // [Range(0, 0.3f)] + // public float RandomizeSpeed; + + [Range(0, 0.5f)] + public float RandomizeVolume; + + public bool Loop; + + #endregion + + #region Runtime + + private int _clipIndex; + + #endregion + + public void Play(AudioSource audioSource) + { + if (Clips.Length == 0) { + return; + } + audioSource.volume = Volume; + audioSource.pitch = Pitch; + audioSource.loop = Loop; + audioSource.clip = GetClip(); + audioSource.Play(); + } + + + public void Stop(AudioSource audioSource) + { + audioSource.Stop(); + } + + private float Pitch => 1f + Random.Range(-RandomizePitch / 2, RandomizePitch / 2); + private float Volume => VolumeCorrection - Random.Range(0, RandomizeVolume); + + private AudioClip GetClip() + { + switch (ClipSelection) { + case Selection.RoundRobin: + var clip = Clips[_clipIndex]; + _clipIndex = (_clipIndex + 1) % Clips.Length; + return clip; + + case Selection.Random: + return Clips[Random.Range(0, Clips.Length)]; + + default: + throw new ArgumentOutOfRangeException(); + } + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundAsset.cs.meta similarity index 100% rename from VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/SoundAsset.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity/Sound/SoundAsset.cs.meta diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundTrigger.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundTrigger.cs new file mode 100644 index 000000000..865bedd77 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundTrigger.cs @@ -0,0 +1,39 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +namespace VisualPinball.Unity +{ + /// + /// A sound trigger is describes how a mechanical sound is triggered. + /// + /// During edit time, sound triggers are declared by game items so they + /// can be linked to a . During runtime, they + /// are used to identify which sound to play. + /// + public struct SoundTrigger + { + /// + /// The ID of the trigger. When you change the ID of a trigger, + /// all already associated triggers will be cleared. + /// + public string Id; + + /// + /// Name of the trigger, used for display purposes only. + /// + public string Name; + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundTrigger.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundTrigger.cs.meta new file mode 100644 index 000000000..523a6340d --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundTrigger.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 82f302a40f4f455d819e782d1f1a6127 +timeCreated: 1677678720 \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/VolumeEmitter.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/VolumeEmitter.cs new file mode 100644 index 000000000..2c2ec4bb4 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/VolumeEmitter.cs @@ -0,0 +1,31 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +namespace VisualPinball.Unity +{ + /// + /// Volume emitters are defined by . They allow for + /// certain triggers (i.e. collision) to pass an additional volume argument + /// when playing the sound. + /// + public struct VolumeEmitter + { + public string Id; + public string Name; + + public static VolumeEmitter Static => new() { Id = "static", Name = "Static" }; + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/VolumeEmitter.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Sound/VolumeEmitter.cs.meta new file mode 100644 index 000000000..e8071b339 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/VolumeEmitter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0d97aa9171d647e1924074d77b882d95 +timeCreated: 1677678737 \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs index 9dbbafa93..b35b99e6b 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -23,8 +23,9 @@ using System; using System.Collections.Generic; +using System.Linq; +using Unity.Collections; using Unity.Mathematics; -using UnityEditor; using UnityEngine; using VisualPinball.Engine.Game.Engines; using VisualPinball.Engine.Math; @@ -146,24 +147,31 @@ public class FlipperComponent : MainRenderableComponent, #endregion #region ISoundEmitter + public SoundTrigger[] AvailableTriggers => new[] { new SoundTrigger { Id = SoundCoilOn, Name = SoundCoilOnName }, new SoundTrigger { Id = SoundCoilOff, Name = SoundCoilOffName}, new SoundTrigger { Id = SoundCoilCollision, Name = SoundCoilCollisionName }, }; - public VolumeEmitter[] GetVolumeEmitters(SoundTrigger trigger) + public VolumeEmitter GetVolumeEmitter(string triggerId) { - switch (trigger.Id) + switch (triggerId) { case SoundCoilCollision: - return new[] { new VolumeEmitter { Id = VolumeBallVelocity, Name = VolumeBallVelocityName } }; + return new VolumeEmitter { Id = VolumeBallVelocity, Name = VolumeBallVelocityName }; } - return null; // null means only "Fixed" will be shown in the volume dropdown. + return VolumeEmitter.Static; } public event EventHandler OnSound; + public void PlaySound(SoundTrigger trigger, float volume) + { + var msc = GetComponent(); + var sa = msc.Sounds.First(s => s.TriggerId == trigger.Id); + } + #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds.meta deleted file mode 100644 index 697162bf9..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: f028dd712c2180947bba5a587be6fca6 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/GameSounds.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/GameSounds.meta deleted file mode 100644 index 7395b2c89..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/GameSounds.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 941dfc7e839dd5f4194835c1c47fb2c4 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds.meta deleted file mode 100644 index 04596e81d..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Sounds/MechSounds.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 4372796933d0ef74fa75d5603c72aab8 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: From f6291d1377cce7f43ed29f546f0d6122aca2c47a Mon Sep 17 00:00:00 2001 From: freezy Date: Thu, 2 Mar 2023 00:04:08 +0100 Subject: [PATCH 08/76] sound: Get the runtime working. --- .../Sound/MechSoundDrawer.cs | 5 +- .../Sound/ISoundEmitter.cs | 20 ++------ .../VisualPinball.Unity/Sound/MechSound.cs | 2 +- .../Sound/MechSoundsComponent.cs | 50 +++++++++++++++++++ .../VisualPinball.Unity/Sound/SoundAsset.cs | 6 +-- .../VPT/Flipper/FlipperApi.cs | 4 ++ .../VPT/Flipper/FlipperComponent.cs | 47 +++++++---------- 7 files changed, 79 insertions(+), 55 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundDrawer.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundDrawer.cs index 807eaa33f..7194aedd6 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundDrawer.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundDrawer.cs @@ -47,11 +47,8 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten if (triggerIndex == -1) { // pre-select first trigger in list, if none set. triggerIndex = 0; } - EditorGUI.BeginChangeCheck(); triggerIndex = EditorGUI.Popup(position, "Trigger on", triggerIndex, triggers.Select(t => t.Name).ToArray()); - if (EditorGUI.EndChangeCheck()) { - triggerIdProperty.stringValue = triggers[triggerIndex].Id; - } + triggerIdProperty.stringValue = triggers[triggerIndex].Id; } else { EditorGUI.LabelField(position, "No Triggers found."); } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs index 138b6b522..a8c67145c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs @@ -28,14 +28,6 @@ public interface ISoundEmitter /// SoundTrigger[] AvailableTriggers { get; } - /// - /// For each , returns whether their - /// sound is volume-dependent on the triggering event. - /// - /// Sound trigger ID - /// Assigned volume emitter. null if fixed volume - VolumeEmitter GetVolumeEmitter(string triggerId); - /// /// The sound event, to which the subscribes to. /// @@ -44,18 +36,12 @@ public interface ISoundEmitter public readonly struct SoundEventArgs { - public readonly SoundTrigger Trigger; + public readonly string TriggerId; public readonly float Volume; - - public SoundEventArgs(SoundTrigger trigger) - { - Trigger = trigger; - Volume = 1f; - } - public SoundEventArgs(SoundTrigger trigger, float volume) + public SoundEventArgs(string triggerId, float volume) { - Trigger = trigger; + TriggerId = triggerId; Volume = volume; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs index 4ae50cce9..afd0ccf01 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs @@ -25,7 +25,7 @@ namespace VisualPinball.Unity public class MechSound { [SerializeReference] - public ScriptableObject Sound; + public SoundAsset Sound; public string TriggerId; diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs index cd38fd303..42e48fce6 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs @@ -16,8 +16,12 @@ // ReSharper disable InconsistentNaming +using System; using System.Collections.Generic; +using System.Linq; +using NLog; using UnityEngine; +using Logger = NLog.Logger; namespace VisualPinball.Unity { @@ -25,7 +29,53 @@ namespace VisualPinball.Unity [RequireComponent(typeof(AudioSource))] public class MechSoundsComponent : MonoBehaviour { + [SerializeField] public List Sounds = new(); + + [NonSerialized] + private ISoundEmitter _soundEmitter; + [NonSerialized] + private AudioSource _audioSource; + [NonSerialized] + private Dictionary _sounds = new(); + + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + private void Awake() + { + _soundEmitter = GetComponent(); + _audioSource = GetComponent(); + + _sounds = Sounds.ToDictionary(s => s.TriggerId, s => s); + } + + private void Start() + { + if (_soundEmitter != null && _audioSource) { + _soundEmitter.OnSound += EmitSound; + + } else { + Logger.Warn($"Cannot initialize mech sound for {name} due to missing ISoundEmitter or AudioSource."); + } + } + + private void OnDestroy() + { + if (_soundEmitter != null) { + _soundEmitter.OnSound -= EmitSound; + } + } + + private void EmitSound(object sender, SoundEventArgs e) + { + if (_sounds.ContainsKey(e.TriggerId)) { + _sounds[e.TriggerId].Sound.Play(_audioSource, e.Volume); + Debug.Log($"Playing sound {e.TriggerId} for {name}"); + + } else { + Debug.LogError($"Unknown trigger {e.TriggerId} for {name}"); + } + } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundAsset.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundAsset.cs index d5e82ac3a..304eaf835 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundAsset.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundAsset.cs @@ -25,7 +25,6 @@ namespace VisualPinball.Unity [CreateAssetMenu(fileName = "Sound", menuName = "Visual Pinball/Sound", order = 102)] public class SoundAsset : ScriptableObject { - #region Properties public string Name; @@ -60,16 +59,17 @@ public enum Selection #region Runtime + [NonSerialized] private int _clipIndex; #endregion - public void Play(AudioSource audioSource) + public void Play(AudioSource audioSource, float volume = 1) { if (Clips.Length == 0) { return; } - audioSource.volume = Volume; + audioSource.volume = Volume * volume; audioSource.pitch = Pitch; audioSource.loop = Loop; audioSource.clip = GetClip(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs index dbb8be679..a330524b9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs @@ -94,6 +94,8 @@ public void RotateToEnd() state.Movement.StartRotateToEndTime = PhysicsEngine.TimeMsec; state.Movement.AngleAtRotateToEnd = state.Movement.Angle; state.Solenoid.Value = true; + + MainComponent.EmitSound(FlipperComponent.SoundCoilOn, MainComponent.RotatePosition); } /// @@ -105,6 +107,8 @@ public void RotateToStart() ref var state = ref PhysicsEngine.FlipperState(ItemId); state.Movement.EnableRotateEvent = -1; state.Solenoid.Value = false; + + MainComponent.EmitSound(FlipperComponent.SoundCoilOff); } internal ref FlipperState State => ref PhysicsEngine.FlipperState(ItemId); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs index b35b99e6b..c9ea7beb9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -23,7 +23,6 @@ using System; using System.Collections.Generic; -using System.Linq; using Unity.Collections; using Unity.Mathematics; using UnityEngine; @@ -127,49 +126,37 @@ public class FlipperComponent : MainRenderableComponent, protected override Type MeshComponentType { get; } = typeof(MeshComponent); protected override Type ColliderComponentType { get; } = typeof(ColliderComponent); - public const string MainCoilItem = "main_coil"; public const string HoldCoilItem = "hold_coil"; public const string EosSwitchItem = "eos_switch"; - //ISoundEmitter SoundTrigger Ids - private const string SoundCoilOn = "coil_on"; - private const string SoundCoilOff = "coil_off"; - private const string SoundCoilCollision = "ball_collision"; - //ISoundEmitter SoundTrigger Names - private const string SoundCoilOnName = "Coil On"; - private const string SoundCoilOffName = "Coil Off"; - private const string SoundCoilCollisionName = "Ball Collision"; - //ISoundEmitter VolumeEmitter Ids - private const string VolumeBallVelocity = "ball_velocity"; - //ISoundEmitter VolumeEmitter Names - private const string VolumeBallVelocityName = "Ball Velocity"; + + public const string SoundCoilOn = "sound_coil_on"; + public const string SoundCoilOff = "sound_coil_off"; + public const string SoundCoilCollision = "sound_ball_collision"; #endregion #region ISoundEmitter public SoundTrigger[] AvailableTriggers => new[] { - new SoundTrigger { Id = SoundCoilOn, Name = SoundCoilOnName }, - new SoundTrigger { Id = SoundCoilOff, Name = SoundCoilOffName}, - new SoundTrigger { Id = SoundCoilCollision, Name = SoundCoilCollisionName }, + new SoundTrigger { Id = SoundCoilOn, Name = "Coil On" }, + new SoundTrigger { Id = SoundCoilOff, Name = "Coil Off"}, + new SoundTrigger { Id = SoundCoilCollision, Name = "Ball Collision" }, }; - public VolumeEmitter GetVolumeEmitter(string triggerId) - { - switch (triggerId) - { - case SoundCoilCollision: - return new VolumeEmitter { Id = VolumeBallVelocity, Name = VolumeBallVelocityName }; - } - return VolumeEmitter.Static; - } - public event EventHandler OnSound; - public void PlaySound(SoundTrigger trigger, float volume) + internal void EmitSound(string triggerId, float volume = 1) { - var msc = GetComponent(); - var sa = msc.Sounds.First(s => s.TriggerId == trigger.Id); + OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume)); + } + + public float RotatePosition { + get { + var start = (_startAngle + 360) % 360; + var end = (EndAngle + 360) % 360; + return 1 - (transform.localEulerAngles.y - start) / (end - start); + } } #endregion From 43a82330ff28c03763ad147f70a824de4ff3dd0a Mon Sep 17 00:00:00 2001 From: freezy Date: Thu, 2 Mar 2023 00:09:42 +0100 Subject: [PATCH 09/76] sounds: Clean up obsolete stuff. --- VisualPinball.Engine/VPT/Enums.cs | 2 -- .../VisualPinball.Unity/Game/Player.cs | 11 ------- .../VisualPinball.Unity/Sound/SoundTrigger.cs | 2 +- .../Sound/VolumeEmitter.cs | 31 ------------------- .../Sound/VolumeEmitter.cs.meta | 3 -- 5 files changed, 1 insertion(+), 48 deletions(-) delete mode 100644 VisualPinball.Unity/VisualPinball.Unity/Sound/VolumeEmitter.cs delete mode 100644 VisualPinball.Unity/VisualPinball.Unity/Sound/VolumeEmitter.cs.meta diff --git a/VisualPinball.Engine/VPT/Enums.cs b/VisualPinball.Engine/VPT/Enums.cs index d27c4a5cc..de49e49e1 100644 --- a/VisualPinball.Engine/VPT/Enums.cs +++ b/VisualPinball.Engine/VPT/Enums.cs @@ -146,6 +146,4 @@ public static class TroughType public const int TwoCoilsOneSwitch = 3; public const int ClassicSingleBall = 4; } - - } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs index 56705dd0d..0642cecde 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -19,13 +19,11 @@ using System.Linq; using NLog; using Unity.Mathematics; -using UnityEditor; using UnityEngine; using UnityEngine.InputSystem; using VisualPinball.Engine.Common; using VisualPinball.Engine.Game; using VisualPinball.Engine.Game.Engines; -using VisualPinball.Engine.VPT; using VisualPinball.Engine.VPT.Trigger; using Color = VisualPinball.Engine.Math.Color; using Logger = NLog.Logger; @@ -181,13 +179,6 @@ private void Start() OnPlayerStarted?.Invoke(this, EventArgs.Empty); } - private void Component_OnSound(object sender, SoundEventArgs e) - { - //throw new NotImplementedException(); - Debug.Log("test"); - - } - private void Update() { OnUpdate?.Invoke(this, EventArgs.Empty); @@ -463,6 +454,4 @@ public BallEvent(int ballId, GameObject ball) Ball = ball; } } - - } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundTrigger.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundTrigger.cs index 865bedd77..ff5c3c506 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundTrigger.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundTrigger.cs @@ -17,7 +17,7 @@ namespace VisualPinball.Unity { /// - /// A sound trigger is describes how a mechanical sound is triggered. + /// A sound trigger describes how a mechanical sound is triggered. /// /// During edit time, sound triggers are declared by game items so they /// can be linked to a . During runtime, they diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/VolumeEmitter.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/VolumeEmitter.cs deleted file mode 100644 index 2c2ec4bb4..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/VolumeEmitter.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Visual Pinball Engine -// Copyright (C) 2023 freezy and VPE Team -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -namespace VisualPinball.Unity -{ - /// - /// Volume emitters are defined by . They allow for - /// certain triggers (i.e. collision) to pass an additional volume argument - /// when playing the sound. - /// - public struct VolumeEmitter - { - public string Id; - public string Name; - - public static VolumeEmitter Static => new() { Id = "static", Name = "Static" }; - } -} diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/VolumeEmitter.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Sound/VolumeEmitter.cs.meta deleted file mode 100644 index e8071b339..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/VolumeEmitter.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 0d97aa9171d647e1924074d77b882d95 -timeCreated: 1677678737 \ No newline at end of file From 54351aae0bec697f699be5546d82b432568ff00b Mon Sep 17 00:00:00 2001 From: freezy Date: Thu, 2 Mar 2023 00:29:51 +0100 Subject: [PATCH 10/76] style: Some comments and re-ordering. --- .../Sound/SoundAssetInspector.cs | 19 +++++++++---------- .../VPT/Flipper/FlipperComponent.cs | 4 ++++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundAssetInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundAssetInspector.cs index b9d2c3d90..9713d2922 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundAssetInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundAssetInspector.cs @@ -21,7 +21,6 @@ namespace VisualPinball.Unity.Editor { - [CustomEditor(typeof(SoundAsset)), CanEditMultipleObjects] public class SoundAssetInspector : UnityEditor.Editor { @@ -90,15 +89,6 @@ public override void OnInspectorGUI() GUILayout.EndHorizontal(); } - private bool PlayStopButton() - { - return _editorAudioSource.isPlaying - ? GUILayout.Button(new GUIContent("Stop", Icons.StopButton(IconSize.Small, IconColor.Orange)), - GUILayout.Height(ButtonHeight), GUILayout.Width(ButtonWidth)) - : GUILayout.Button(new GUIContent("Play", Icons.PlayButton(IconSize.Small, IconColor.Orange)), - GUILayout.Height(ButtonHeight), GUILayout.Width(ButtonWidth)); - } - private void PlayStop() { if (_editorAudioSource.isPlaying) { @@ -107,6 +97,15 @@ private void PlayStop() _soundAsset.Play(_editorAudioSource); } } + + private bool PlayStopButton() + { + return _editorAudioSource.isPlaying + ? GUILayout.Button(new GUIContent("Stop", Icons.StopButton(IconSize.Small, IconColor.Orange)), + GUILayout.Height(ButtonHeight), GUILayout.Width(ButtonWidth)) + : GUILayout.Button(new GUIContent("Play", Icons.PlayButton(IconSize.Small, IconColor.Orange)), + GUILayout.Height(ButtonHeight), GUILayout.Width(ButtonWidth)); + } /// /// Gets or creates the editor GameObject for playing sounds in the editor. diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs index c9ea7beb9..3e5d09cb2 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -151,6 +151,10 @@ internal void EmitSound(string triggerId, float volume = 1) OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume)); } + /// + /// Returns the current position of the flipper between 0 and 1, where 0 is the + /// start position, and 1 the end position. + /// public float RotatePosition { get { var start = (_startAngle + 360) % 360; From 2d5049b2757550c293bc70996f6d286546416f97 Mon Sep 17 00:00:00 2001 From: Mr-Jay-Gatsby Date: Fri, 3 Mar 2023 11:12:36 -0500 Subject: [PATCH 11/76] Minor fix to sound asset, in order to handle when audio clips length changes and then playing the clips under round robin selection. --- VisualPinball.Unity/VisualPinball.Unity/Sound/SoundAsset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundAsset.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundAsset.cs index 304eaf835..888f2db37 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundAsset.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/SoundAsset.cs @@ -60,7 +60,7 @@ public enum Selection #region Runtime [NonSerialized] - private int _clipIndex; + private int _clipIndex = 0; #endregion From 489705d6fcabde8ea7be041a753e4763e2be7140 Mon Sep 17 00:00:00 2001 From: Mr-Jay-Gatsby Date: Tue, 21 Mar 2023 10:20:19 -0400 Subject: [PATCH 12/76] Initial audio fade implementation. --- .../Sound/FadeMixerGroup.cs | 39 +++++++++++++++++ .../Sound/FadeMixerGroup.cs.meta | 11 +++++ .../Sound/ISoundEmitter.cs | 6 ++- .../VisualPinball.Unity/Sound/MechSound.cs | 2 +- .../Sound/MechSoundsComponent.cs | 42 ++++++++++++++++++- .../VPT/Flipper/FlipperComponent.cs | 4 +- 6 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Sound/FadeMixerGroup.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Sound/FadeMixerGroup.cs.meta diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/FadeMixerGroup.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/FadeMixerGroup.cs new file mode 100644 index 000000000..5625bb706 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/FadeMixerGroup.cs @@ -0,0 +1,39 @@ +// Visual Pinball Engine +// Copyright (C) 2023 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System.Collections; +using System.Collections.Generic; +using UnityEngine.Audio; +using UnityEngine; +public static class FadeMixerGroup +{ + public static IEnumerator StartFade(AudioMixer audioMixer, string exposedParam, float duration, float targetVolume) + { + float currentTime = 0; + float currentVol; + audioMixer.GetFloat(exposedParam, out currentVol); + currentVol = Mathf.Pow(10, currentVol / 20); + float targetValue = Mathf.Clamp(targetVolume, 0.0001f, 1); + while (currentTime < duration) + { + currentTime += Time.deltaTime; + float newVol = Mathf.Lerp(currentVol, targetValue, currentTime / duration); + audioMixer.SetFloat(exposedParam, Mathf.Log10(newVol) * 20); + yield return null; + } + yield break; + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/FadeMixerGroup.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Sound/FadeMixerGroup.cs.meta new file mode 100644 index 000000000..aba75f0dd --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/FadeMixerGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 39c1161b40f535947ad995908584d38a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs index a8c67145c..7c511f24d 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs @@ -38,11 +38,13 @@ public readonly struct SoundEventArgs { public readonly string TriggerId; public readonly float Volume; - - public SoundEventArgs(string triggerId, float volume) + public readonly float Fade; + + public SoundEventArgs(string triggerId, float volume, float fade) { TriggerId = triggerId; Volume = volume; + Fade = fade; } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs index afd0ccf01..16c6e974b 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs @@ -29,7 +29,7 @@ public class MechSound public string TriggerId; - [Range(0, 1)] + [Range(0.0001f, 1)] public float Volume = 1; public MechSoundAction Action = MechSoundAction.Play; diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs index 42e48fce6..dd068d80c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs @@ -21,6 +21,7 @@ using System.Linq; using NLog; using UnityEngine; +using UnityEngine.Audio; using Logger = NLog.Logger; namespace VisualPinball.Unity @@ -40,6 +41,7 @@ public class MechSoundsComponent : MonoBehaviour private Dictionary _sounds = new(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private Coroutine _co; private void Awake() { @@ -68,8 +70,46 @@ private void OnDestroy() private void EmitSound(object sender, SoundEventArgs e) { + if (_sounds.ContainsKey(e.TriggerId)) { - _sounds[e.TriggerId].Sound.Play(_audioSource, e.Volume); + + float fade = e.Fade; + bool fadeVolume = false; + + //convert fade duration from milliseconds to seconds for use with StartFade method + if (fade > 0) + { + fade = fade / 1000; + fadeVolume = true; + } + + float volume = e.Volume; + + AudioMixer audioMixer = GetComponent().outputAudioMixerGroup.audioMixer; + _sounds[e.TriggerId].Sound.Play(_audioSource, volume); + + /* set audio mixer volume to decibel equivalent of volume slider value + mixer volume is set at 0 dB when added to audiosource + volume of 1 in slider is equivalent to 0 dB + */ + string exposedParameter = "vol1"; + float sliderDBVolume = Mathf.Log10(volume) * 20; + float mixerVolume; + + audioMixer.GetFloat(exposedParameter, out mixerVolume); + + //current coroutine is still fading the audio clip and needs to be stopped and volume reset + if (mixerVolume < sliderDBVolume) + { + StopCoroutine(_co); + audioMixer.SetFloat(exposedParameter, sliderDBVolume); + + } + + if (fadeVolume) + { _co = StartCoroutine(FadeMixerGroup.StartFade(audioMixer, exposedParameter, fade, 0)); } + + Debug.Log($"Playing sound {e.TriggerId} for {name}"); } else { diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs index 3e5d09cb2..23cf88a67 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -146,9 +146,9 @@ public class FlipperComponent : MainRenderableComponent, public event EventHandler OnSound; - internal void EmitSound(string triggerId, float volume = 1) + internal void EmitSound(string triggerId, float volume = 1, float fade = 5) { - OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume)); + OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume, fade)); } /// From 207416d80fd3cc832476a0308a84ce7088c4ca7c Mon Sep 17 00:00:00 2001 From: Mr-Jay-Gatsby Date: Thu, 30 Mar 2023 13:52:56 -0400 Subject: [PATCH 13/76] Updated fade handling. Added sound handling to the following components: plunger, drain, bumper. --- .../Sound/FadeMixerGroup.cs | 1 + .../Sound/ISoundEmitter.cs | 4 +--- .../VisualPinball.Unity/Sound/MechSound.cs | 3 ++- .../Sound/MechSoundsComponent.cs | 12 +++++++---- .../VPT/Bumper/BumperApi.cs | 2 ++ .../VPT/Bumper/BumperComponent.cs | 18 +++++++++++++++- .../VPT/Flipper/FlipperComponent.cs | 4 ++-- .../VPT/Kicker/KickerApi.cs | 2 ++ .../VPT/Kicker/KickerComponent.cs | 21 ++++++++++++++++++- .../VPT/Plunger/PlungerApi.cs | 4 ++++ .../VPT/Plunger/PlungerComponent.cs | 21 ++++++++++++++++++- 11 files changed, 79 insertions(+), 13 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/FadeMixerGroup.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/FadeMixerGroup.cs index 5625bb706..5004059be 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/FadeMixerGroup.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/FadeMixerGroup.cs @@ -20,6 +20,7 @@ using UnityEngine; public static class FadeMixerGroup { + //note: fade duration below 1 second causes a breakdown of this method public static IEnumerator StartFade(AudioMixer audioMixer, string exposedParam, float duration, float targetVolume) { float currentTime = 0; diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs index 7c511f24d..306387eb7 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/ISoundEmitter.cs @@ -38,13 +38,11 @@ public readonly struct SoundEventArgs { public readonly string TriggerId; public readonly float Volume; - public readonly float Fade; - public SoundEventArgs(string triggerId, float volume, float fade) + public SoundEventArgs(string triggerId, float volume) { TriggerId = triggerId; Volume = volume; - Fade = fade; } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs index 16c6e974b..3f328045b 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs @@ -33,7 +33,8 @@ public class MechSound public float Volume = 1; public MechSoundAction Action = MechSoundAction.Play; - + + [Tooltip("Increments of 1000")] [Min(0)] [Unit("ms")] public float Fade; diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs index dd068d80c..96b1948fa 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs @@ -73,12 +73,17 @@ private void EmitSound(object sender, SoundEventArgs e) if (_sounds.ContainsKey(e.TriggerId)) { - float fade = e.Fade; + float fade = _sounds[e.TriggerId].Fade; bool fadeVolume = false; //convert fade duration from milliseconds to seconds for use with StartFade method if (fade > 0) - { + { + //dont have a fade minimum of less than 1 second, if there is a fade. Less than 1 second fade, + //and the underlying method 'FadeMixerGroup.StartFade' will break down and not work correctly + if (fade < 1000) + { fade = 1000; } + fade = fade / 1000; fadeVolume = true; } @@ -103,11 +108,10 @@ volume of 1 in slider is equivalent to 0 dB { StopCoroutine(_co); audioMixer.SetFloat(exposedParameter, sliderDBVolume); - } if (fadeVolume) - { _co = StartCoroutine(FadeMixerGroup.StartFade(audioMixer, exposedParameter, fade, 0)); } + { _co = StartCoroutine(FadeMixerGroup.StartFade(audioMixer, exposedParameter, 1, 0)); } Debug.Log($"Playing sound {e.TriggerId} for {name}"); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs index b8eeffd50..98399a238 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs @@ -165,6 +165,8 @@ void IApiHittable.OnHit(int ballId, bool isUnHit) OnSwitch(true); } } + + MainComponent.EmitSound(BumperComponent.SoundBumperHit); } #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperComponent.cs index b5c66b8db..9225fdaa1 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperComponent.cs @@ -34,7 +34,7 @@ namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Game Item/Bumper")] public class BumperComponent : MainRenderableComponent, - ISwitchDeviceComponent, ICoilDeviceComponent, IOnSurfaceComponent + ISwitchDeviceComponent, ICoilDeviceComponent, IOnSurfaceComponent, ISoundEmitter { #region Data @@ -81,6 +81,22 @@ public class BumperComponent : MainRenderableComponent, public const float DataMeshScale = 100f; public const string SocketSwitchItem = "socket_switch"; + public const string SoundBumperHit = "sound_bumper_hit"; + + #endregion + + #region ISoundEmitter + + public SoundTrigger[] AvailableTriggers => new[] { + new SoundTrigger { Id = SoundBumperHit, Name = "Bumper Hit" } + }; + + public event EventHandler OnSound; + + internal void EmitSound(string triggerId, float volume = 1) + { + OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume)); + } #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs index 23cf88a67..3e5d09cb2 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -146,9 +146,9 @@ public class FlipperComponent : MainRenderableComponent, public event EventHandler OnSound; - internal void EmitSound(string triggerId, float volume = 1, float fade = 5) + internal void EmitSound(string triggerId, float volume = 1) { - OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume, fade)); + OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume)); } /// diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs index b78b51141..292416ec2 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs @@ -158,6 +158,8 @@ private void OnBallDestroyed() if (kickerState.Collision.HasBall) { kickerState.Collision.BallId = 0; } + + MainComponent.EmitSound(KickerComponent.SoundKickerDrain); } #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs index 872819dde..c0bfcdbc2 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs @@ -41,7 +41,7 @@ namespace VisualPinball.Unity [AddComponentMenu("Visual Pinball/Game Item/Kicker")] public class KickerComponent : MainRenderableComponent, ICoilDeviceComponent, ITriggerComponent, IBallCreationPosition, IOnSurfaceComponent, - IRotatableComponent, ISerializationCallbackReceiver + IRotatableComponent, ISerializationCallbackReceiver, ISoundEmitter { #region Data @@ -85,6 +85,25 @@ public class KickerComponent : MainRenderableComponent, public const string SwitchItem = "kicker_switch"; + public const string SoundKickerDrain = "sound_kicker_drain"; + public const string SoundKickerBallRelease = "sound_kicker_ball_release"; + + #endregion + + #region ISoundEmitter + + public SoundTrigger[] AvailableTriggers => new[] { + new SoundTrigger { Id = SoundKickerDrain, Name = "Ball Drain" }, + new SoundTrigger { Id = SoundKickerBallRelease, Name = "Ball Release" }, + }; + + public event EventHandler OnSound; + + internal void EmitSound(string triggerId, float volume = 1) + { + OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume)); + } + #endregion #region Wiring diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerApi.cs index 328d9f430..2e67d87a9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerApi.cs @@ -97,6 +97,8 @@ public void PullBack() } else { PlungerCommands.PullBack(collComponent.SpeedPull, ref plungerState.Velocity, ref plungerState.Movement); } + + MainComponent.EmitSound(PlungerComponent.SoundPlungerPull); } public void Fire() @@ -125,6 +127,8 @@ public void Fire() var pos = (plungerState.Movement.Position - plungerState.Static.FrameEnd) / (plungerState.Static.FrameStart - plungerState.Static.FrameEnd); PlungerCommands.Fire(pos, ref plungerState.Velocity, ref plungerState.Movement, in plungerState.Static); } + + MainComponent.EmitSound(PlungerComponent.SoundPlungerRelease); } IApiCoil IApiCoilDevice.Coil(string deviceItem) => Coil(deviceItem); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerComponent.cs index 6bee71fe9..df660454a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerComponent.cs @@ -32,7 +32,7 @@ namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Game Item/Plunger")] public class PlungerComponent : MainRenderableComponent, - ICoilDeviceComponent, IOnSurfaceComponent + ICoilDeviceComponent, IOnSurfaceComponent, ISoundEmitter { #region Data @@ -70,6 +70,25 @@ public class PlungerComponent : MainRenderableComponent, public const string PullCoilId = "c_pull"; public const string FireCoilId = "c_autofire"; + public const string SoundPlungerPull = "sound_plunger_pull"; + public const string SoundPlungerRelease = "sound_plunger_release"; + + #endregion + + #region ISoundEmitter + + public SoundTrigger[] AvailableTriggers => new[] { + new SoundTrigger { Id = SoundPlungerPull, Name = "Plunger Pull" }, + new SoundTrigger { Id = SoundPlungerRelease, Name = "Plunger Release"} + }; + + public event EventHandler OnSound; + + internal void EmitSound(string triggerId, float volume = 1) + { + OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume)); + } + #endregion #region Runtime From 09f41581a07795fff22a72575c7a6e48158ea7a4 Mon Sep 17 00:00:00 2001 From: freezy Date: Wed, 3 Jan 2024 02:22:41 +0100 Subject: [PATCH 14/76] fix: UnityEditor reference. --- .../VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs index 3e5d09cb2..e2b91f640 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -393,7 +393,7 @@ protected void OnDrawGizmosSelected() } Gizmos.matrix = Matrix4x4.identity; - Handles.matrix = Matrix4x4.identity; + UnityEditor.Handles.matrix = Matrix4x4.identity; // Draw enclosing polygon Gizmos.color = Color.cyan; From 7b02f2ab8bd3b6a98d5899046e3c7f82f2729e4b Mon Sep 17 00:00:00 2001 From: Jonathan Linowes Date: Thu, 4 Jan 2024 11:51:05 -0500 Subject: [PATCH 15/76] implement Stop action --- .../Sound/MechSoundsComponent.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs index 96b1948fa..2064a6005 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs @@ -72,7 +72,17 @@ private void EmitSound(object sender, SoundEventArgs e) { if (_sounds.ContainsKey(e.TriggerId)) { + MechSound sound = _sounds[e.TriggerId]; + if (sound.Action == MechSoundAction.Stop) + { + sound.Sound.Stop(_audioSource); + Debug.Log($"Stopping sound {e.TriggerId} for {name}"); + // we're done + return; + } + + // else sound.Action == MechSoundAction.Play float fade = _sounds[e.TriggerId].Fade; bool fadeVolume = false; @@ -91,7 +101,7 @@ private void EmitSound(object sender, SoundEventArgs e) float volume = e.Volume; AudioMixer audioMixer = GetComponent().outputAudioMixerGroup.audioMixer; - _sounds[e.TriggerId].Sound.Play(_audioSource, volume); + sound.Sound.Play(_audioSource, volume); /* set audio mixer volume to decibel equivalent of volume slider value mixer volume is set at 0 dB when added to audiosource @@ -117,6 +127,7 @@ volume of 1 in slider is equivalent to 0 dB Debug.Log($"Playing sound {e.TriggerId} for {name}"); } else { + // JL: doenst need to be an error, change to Debug.Log? Debug.LogError($"Unknown trigger {e.TriggerId} for {name}"); } } From a5fecdfde12d157303e3243c2886c3a62b4aca14 Mon Sep 17 00:00:00 2001 From: Jonathan Linowes Date: Thu, 4 Jan 2024 13:37:00 -0500 Subject: [PATCH 16/76] Dynamic audiosource for mechanical sound clips --- .../Sound/MechSoundsInspector.cs | 14 ++---- .../Sound/MechSoundsComponent.cs | 46 +++++++++++-------- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs index 66e38ca81..daa5985c2 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs @@ -22,17 +22,15 @@ namespace VisualPinball.Unity.Editor [CustomEditor(typeof(MechSoundsComponent)), CanEditMultipleObjects] public class MechanicalSoundInspector : UnityEditor.Editor { + private SerializedProperty _audioMixerProperty; private SerializedProperty _soundsProperty; private void OnEnable() { + _audioMixerProperty = serializedObject.FindProperty(nameof(MechSoundsComponent.AudioMixer)); _soundsProperty = serializedObject.FindProperty(nameof(MechSoundsComponent.Sounds)); var comp = target as MechSoundsComponent; - var audioSource = comp!.GetComponent(); - if (audioSource != null) { - audioSource.playOnAwake = false; - } } public override void OnInspectorGUI() @@ -45,13 +43,9 @@ public override void OnInspectorGUI() return; } - var audioSource = comp.GetComponent(); - if (audioSource == null) { - EditorGUILayout.HelpBox("Cannot find audio source. This component only works with an audio source on the same GameObject.", MessageType.Error); - return; - } - serializedObject.Update(); + + EditorGUILayout.PropertyField(_audioMixerProperty); EditorGUILayout.PropertyField(_soundsProperty); diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs index 2064a6005..49fc1f93a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs @@ -27,18 +27,19 @@ namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Sounds/Mechanical Sounds")] - [RequireComponent(typeof(AudioSource))] + //[RequireComponent(typeof(AudioSource))] public class MechSoundsComponent : MonoBehaviour { + [SerializeField] + public AudioMixerGroup AudioMixer; + [SerializeField] public List Sounds = new(); [NonSerialized] private ISoundEmitter _soundEmitter; [NonSerialized] - private AudioSource _audioSource; - [NonSerialized] - private Dictionary _sounds = new(); + private SerializableDictionary _audioSources = new SerializableDictionary(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private Coroutine _co; @@ -46,14 +47,11 @@ public class MechSoundsComponent : MonoBehaviour private void Awake() { _soundEmitter = GetComponent(); - _audioSource = GetComponent(); - - _sounds = Sounds.ToDictionary(s => s.TriggerId, s => s); } private void Start() { - if (_soundEmitter != null && _audioSource) { + if (_soundEmitter != null) { _soundEmitter.OnSound += EmitSound; } else { @@ -70,20 +68,36 @@ private void OnDestroy() private void EmitSound(object sender, SoundEventArgs e) { + int clipCount = 0; - if (_sounds.ContainsKey(e.TriggerId)) { - MechSound sound = _sounds[e.TriggerId]; + foreach(MechSound sound in Sounds) + { + // filter for the TriggerId + if (sound.TriggerId != e.TriggerId) continue; + + // get or create the AudioSource + AudioSource audioSource; + if (_audioSources.ContainsKey(sound.Sound)) + { + audioSource = _audioSources[sound.Sound]; + } + else + { + audioSource = gameObject.AddComponent(); + audioSource.outputAudioMixerGroup = AudioMixer; + _audioSources.Add(sound.Sound, audioSource); + } if (sound.Action == MechSoundAction.Stop) { - sound.Sound.Stop(_audioSource); + sound.Sound.Stop(audioSource); Debug.Log($"Stopping sound {e.TriggerId} for {name}"); // we're done - return; + continue; } // else sound.Action == MechSoundAction.Play - float fade = _sounds[e.TriggerId].Fade; + float fade = sound.Fade; bool fadeVolume = false; //convert fade duration from milliseconds to seconds for use with StartFade method @@ -101,7 +115,7 @@ private void EmitSound(object sender, SoundEventArgs e) float volume = e.Volume; AudioMixer audioMixer = GetComponent().outputAudioMixerGroup.audioMixer; - sound.Sound.Play(_audioSource, volume); + sound.Sound.Play(audioSource, volume); /* set audio mixer volume to decibel equivalent of volume slider value mixer volume is set at 0 dB when added to audiosource @@ -125,10 +139,6 @@ volume of 1 in slider is equivalent to 0 dB Debug.Log($"Playing sound {e.TriggerId} for {name}"); - - } else { - // JL: doenst need to be an error, change to Debug.Log? - Debug.LogError($"Unknown trigger {e.TriggerId} for {name}"); } } } From 761202732870a8e44b8de5d6cad6dacd36352db0 Mon Sep 17 00:00:00 2001 From: Jonathan Linowes Date: Thu, 4 Jan 2024 19:06:03 -0500 Subject: [PATCH 17/76] populate AudioMixer prop by searching parents convenient especially prefabs. --- .../Sound/MechSoundsInspector.cs | 4 +-- .../Sound/MechSoundsComponent.cs | 25 +++++++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs index daa5985c2..63d064a91 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs @@ -44,11 +44,11 @@ public override void OnInspectorGUI() } serializedObject.Update(); - - EditorGUILayout.PropertyField(_audioMixerProperty); EditorGUILayout.PropertyField(_soundsProperty); + EditorGUILayout.PropertyField(_audioMixerProperty); + serializedObject.ApplyModifiedProperties(); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs index 49fc1f93a..2abd13b4b 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSoundsComponent.cs @@ -27,19 +27,19 @@ namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Sounds/Mechanical Sounds")] - //[RequireComponent(typeof(AudioSource))] public class MechSoundsComponent : MonoBehaviour { [SerializeField] - public AudioMixerGroup AudioMixer; + public List Sounds = new(); [SerializeField] - public List Sounds = new(); - + [Tooltip("If left blank, looks for an Audio Mixer in closest parent up the hierarchy.")] + public AudioMixerGroup AudioMixer; + [NonSerialized] private ISoundEmitter _soundEmitter; [NonSerialized] - private SerializableDictionary _audioSources = new SerializableDictionary(); + private SerializableDictionary _audioSources = new SerializableDictionary(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private Coroutine _co; @@ -51,10 +51,25 @@ private void Awake() private void Start() { + if (AudioMixer == null) + { + // find an Audio Mixer by searching up the hierarchy + AudioSource audioSource = GetComponentInParent(); + if (audioSource != null) + { + AudioMixer = audioSource.outputAudioMixerGroup; + } + else + { + Logger.Warn($"Sounds will not play without an Audio Mixer."); + } + } + if (_soundEmitter != null) { _soundEmitter.OnSound += EmitSound; } else { + // ? is AudioSource really a dependency here?? Logger.Warn($"Cannot initialize mech sound for {name} due to missing ISoundEmitter or AudioSource."); } } From 3a746d986e380373d965f81f6b5f012e125d92de Mon Sep 17 00:00:00 2001 From: Jonathan Linowes Date: Fri, 5 Jan 2024 12:18:25 -0500 Subject: [PATCH 18/76] added target sounds --- .../VPT/Flipper/FlipperComponent.cs | 26 ++++++++++--------- .../VPT/HitTarget/DropTargetApi.cs | 2 ++ .../VPT/HitTarget/HitTargetApi.cs | 2 ++ .../VPT/HitTarget/TargetComponent.cs | 19 +++++++++++++- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs index e2b91f640..22f85af3c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -151,21 +151,11 @@ internal void EmitSound(string triggerId, float volume = 1) OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume)); } - /// - /// Returns the current position of the flipper between 0 and 1, where 0 is the - /// start position, and 1 the end position. - /// - public float RotatePosition { - get { - var start = (_startAngle + 360) % 360; - var end = (EndAngle + 360) % 360; - return 1 - (transform.localEulerAngles.y - start) / (end - start); - } - } - #endregion + + #region Wiring public IEnumerable AvailableSwitches => new[] { @@ -229,6 +219,18 @@ public float2 RotatedPosition { } } + /// + /// Returns the current position of the flipper between 0 and 1, where 0 is the + /// start position, and 1 the end position. + /// + public float RotatePosition { + get { + var start = (_startAngle + 360) % 360; + var end = (EndAngle + 360) % 360; + return 1 - (transform.localEulerAngles.y - start) / (end - start); + } + } + #endregion #region Conversion diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetApi.cs index 1764216ce..f035076ca 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetApi.cs @@ -126,6 +126,8 @@ void IApi.OnDestroy() void IApiHittable.OnHit(int ballId, bool _) { Hit?.Invoke(this, new HitEventArgs(ballId)); + + MainComponent.EmitSound(TargetComponent.SoundTargetHit); } #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetApi.cs index 65b8c2b21..9ad68e2e2 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetApi.cs @@ -85,6 +85,8 @@ void IApiHittable.OnHit(int ballId, bool _) Hit?.Invoke(this, new HitEventArgs(ballId)); Switch?.Invoke(this, new SwitchEventArgs(true, ballId)); OnSwitch(true); + + MainComponent.EmitSound(TargetComponent.SoundTargetHit); } #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/TargetComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/TargetComponent.cs index a9ff6a93d..172bfd08e 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/TargetComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/TargetComponent.cs @@ -35,7 +35,7 @@ namespace VisualPinball.Unity { public abstract class TargetComponent : MainRenderableComponent, - ISwitchDeviceComponent, ITargetData, IMeshGenerator + ISwitchDeviceComponent, ITargetData, IMeshGenerator, ISoundEmitter { #region Data @@ -94,6 +94,8 @@ public Matrix3D GetTransformationMatrix() public const string SwitchItem = "target_switch"; + public const string SoundTargetHit = "sound_target_hit"; + #endregion #region Wiring @@ -193,5 +195,20 @@ public override void CopyFromObject(GameObject go) public override void SetEditorScale(Vector3 scale) => Size = scale; #endregion + + #region ISoundEmitter + + public SoundTrigger[] AvailableTriggers => new[] { + new SoundTrigger { Id = SoundTargetHit, Name = "Target Hit" }, + }; + + public event EventHandler OnSound; + + internal virtual void EmitSound(string triggerId, float volume = 1) + { + OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume)); + } + + #endregion } } From 0ed3e816161a6f0e0d0d43c62e78dd2ea918ae81 Mon Sep 17 00:00:00 2001 From: Jonathan Linowes Date: Fri, 5 Jan 2024 12:28:24 -0500 Subject: [PATCH 19/76] Q to exit play mode --- VisualPinball.Engine/Common/Constants.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/VisualPinball.Engine/Common/Constants.cs b/VisualPinball.Engine/Common/Constants.cs index 4bb1623e1..f43331372 100644 --- a/VisualPinball.Engine/Common/Constants.cs +++ b/VisualPinball.Engine/Common/Constants.cs @@ -160,5 +160,6 @@ public static class InputConstants public const string ActionSelfTest = "Self Test"; public const string ActionLeftAdvance = "Left Advance"; public const string ActionRightAdvance = "Right Advance"; + public const string ActionExitTable = "Exit Table"; } } From 86bc749664dd39213e5360979289116b797e7e47 Mon Sep 17 00:00:00 2001 From: Jonathan Linowes Date: Mon, 8 Jan 2024 12:55:25 -0500 Subject: [PATCH 20/76] wip drop targets reset and score motor sounds --- VisualPinball.Engine/Common/Constants.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/VisualPinball.Engine/Common/Constants.cs b/VisualPinball.Engine/Common/Constants.cs index f43331372..4bb1623e1 100644 --- a/VisualPinball.Engine/Common/Constants.cs +++ b/VisualPinball.Engine/Common/Constants.cs @@ -160,6 +160,5 @@ public static class InputConstants public const string ActionSelfTest = "Self Test"; public const string ActionLeftAdvance = "Left Advance"; public const string ActionRightAdvance = "Right Advance"; - public const string ActionExitTable = "Exit Table"; } } From c436b1aa57cd0a25ece0846e701c72326c48c95a Mon Sep 17 00:00:00 2001 From: Jonathan Linowes Date: Fri, 5 Jan 2024 13:41:36 -0500 Subject: [PATCH 21/76] trigger switch sounds --- .../VPT/Trigger/TriggerApi.cs | 4 ++++ .../VPT/Trigger/TriggerComponent.cs | 20 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerApi.cs index a867681e5..d1935877c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerApi.cs @@ -92,10 +92,14 @@ void IApiHittable.OnHit(int ballId, bool isUnHit) Switch?.Invoke(this, new SwitchEventArgs(false, ballId)); OnSwitch(false); + MainComponent.EmitSound(TriggerComponent.SoundTriggerUnhit); + } else { Hit?.Invoke(this, new HitEventArgs(ballId)); Switch?.Invoke(this, new SwitchEventArgs(true, ballId)); OnSwitch(true); + + MainComponent.EmitSound(TriggerComponent.SoundTriggerHit); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerComponent.cs index 2205b7d63..8cabfe900 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerComponent.cs @@ -37,7 +37,7 @@ namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Game Item/Trigger")] public class TriggerComponent : MainRenderableComponent, - ITriggerComponent, IOnSurfaceComponent + ITriggerComponent, IOnSurfaceComponent, ISoundEmitter { #region Data @@ -77,6 +77,8 @@ public class TriggerComponent : MainRenderableComponent, protected override Type ColliderComponentType { get; } = typeof(ColliderComponent); public const string SwitchItem = "trigger_switch"; + public const string SoundTriggerHit = "sound_trigger_hit"; + public const string SoundTriggerUnhit = "sound_trigger_unhit"; #endregion @@ -324,5 +326,21 @@ public override ItemDataTransformType EditorRotationType{ public override void SetEditorRotation(Vector3 rot) => Rotation = ClampDegrees(rot.x); #endregion + + #region ISoundEmitter + + public SoundTrigger[] AvailableTriggers => new[] { + new SoundTrigger { Id = SoundTriggerHit, Name = "Sound Trigger Hit" }, + new SoundTrigger { Id = SoundTriggerUnhit, Name = "Sound Trigger Unhit" } + }; + + public event EventHandler OnSound; + + internal void EmitSound(string triggerId, float volume = 1) + { + OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume)); + } + + #endregion } } From d5cb3505f5de7b41c18ba34b5c0a6310810a38e8 Mon Sep 17 00:00:00 2001 From: Jonathan Linowes Date: Fri, 5 Jan 2024 14:14:04 -0500 Subject: [PATCH 22/76] attempt to do target bank reset sound --- .../DropTargetBank/DropTargetBankComponent.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankComponent.cs index b030c0a8e..c52bc7133 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankComponent.cs @@ -24,12 +24,14 @@ namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Mechs/Drop Target Bank")] [HelpURL("https://docs.visualpinball.org/creators-guide/manual/mechanisms/drop-target-banks.html")] - public class DropTargetBankComponent : MonoBehaviour, ICoilDeviceComponent, ISwitchDeviceComponent + public class DropTargetBankComponent : MonoBehaviour, ICoilDeviceComponent, ISwitchDeviceComponent, ISoundEmitter { public const string ResetCoilItem = "reset_coil"; public const string SequenceCompletedSwitchItem = "sequence_completed_switch"; + public const string SoundTargetBankReset = "sound_target_bank_reset"; + [ToolboxItem("The number of the drop targets. See documentation of a description of each type.")] public int BankSize = 1; @@ -71,5 +73,20 @@ private void Awake() } #endregion + + #region ISoundEmitter + + public SoundTrigger[] AvailableTriggers => new[] { + new SoundTrigger { Id = SoundTargetBankReset, Name = "Sound Target Bank Reset" } + }; + + public event EventHandler OnSound; + + internal void EmitSound(string triggerId, float volume = 1) + { + OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume)); + } + + #endregion } } From 3b895c2ed411802d4cf9557725c2942dd0c5bf49 Mon Sep 17 00:00:00 2001 From: Jonathan Linowes Date: Fri, 5 Jan 2024 14:15:24 -0500 Subject: [PATCH 23/76] Update DropTargetBankApi.cs --- .../VPT/DropTargetBank/DropTargetBankApi.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankApi.cs index 498b264db..6fe87d5a8 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankApi.cs @@ -102,6 +102,10 @@ private void OnResetCoilEnabled() foreach (var dropTargetApi in _dropTargetApis) { dropTargetApi.IsDropped = false; } + + // ? is this where this goes? + // ! MainComponent local var is not exposed in (from ItemApi?) + //MainComponent.EmitSound(DropTargetBankComponent.SoundTargetBankReset); } void IApi.OnDestroy() From d25ec193d970979704b718ce9a432178f33c78b7 Mon Sep 17 00:00:00 2001 From: Jonathan Linowes Date: Mon, 8 Jan 2024 12:55:25 -0500 Subject: [PATCH 24/76] wip drop targets reset and score motor sounds --- .../VPT/DropTargetBank/DropTargetBankApi.cs | 8 ++-- .../DropTargetBank/DropTargetBankComponent.cs | 41 ++++++++++++++++++- .../VPT/HitTarget/DropTargetApi.cs | 7 ++++ .../VPT/HitTarget/DropTargetComponent.cs | 15 +++++++ .../VPT/HitTarget/TargetComponent.cs | 2 +- .../VPT/Mech/ScoreMotorApi.cs | 12 ++++++ .../VPT/Mech/ScoreMotorComponent.cs | 22 ++++++++++ 7 files changed, 101 insertions(+), 6 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankApi.cs index 6fe87d5a8..13a283900 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankApi.cs @@ -18,11 +18,12 @@ using Logger = NLog.Logger; using NLog; using UnityEngine; +using VisualPinball.Engine.VPT; using System.Collections.Generic; namespace VisualPinball.Unity { - public class DropTargetBankApi : IApi, IApiCoilDevice, IApiSwitchDevice + public class DropTargetBankApi : ItemApi, IApi, IApiCoilDevice, IApiSwitchDevice { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); @@ -57,7 +58,7 @@ private IApiCoil Coil(string deviceItem) }; } - internal DropTargetBankApi(GameObject go, Player player, PhysicsEngine physicsEngine) + internal DropTargetBankApi(GameObject go, Player player, PhysicsEngine physicsEngine) : base(go, player, physicsEngine) { _dropTargetBankComponent = go.GetComponentInChildren(); _player = player; @@ -104,8 +105,7 @@ private void OnResetCoilEnabled() } // ? is this where this goes? - // ! MainComponent local var is not exposed in (from ItemApi?) - //MainComponent.EmitSound(DropTargetBankComponent.SoundTargetBankReset); + MainComponent.EmitSound(DropTargetBankComponent.SoundTargetBankReset); } void IApi.OnDestroy() diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankComponent.cs index c52bc7133..a2d85d315 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankComponent.cs @@ -17,14 +17,18 @@ using System.Collections.Generic; using UnityEngine; using VisualPinball.Engine.Game.Engines; +using VisualPinball.Engine.VPT; using System.ComponentModel; using System; +using VisualPinball.Engine.VPT.HitTarget; +using VisualPinball.Engine.IO; +using VisualPinball.Engine.VPT.Table; namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Mechs/Drop Target Bank")] [HelpURL("https://docs.visualpinball.org/creators-guide/manual/mechanisms/drop-target-banks.html")] - public class DropTargetBankComponent : MonoBehaviour, ICoilDeviceComponent, ISwitchDeviceComponent, ISoundEmitter + public class DropTargetBankComponent : MainRenderableComponent, ICoilDeviceComponent, ISwitchDeviceComponent, ISoundEmitter { public const string ResetCoilItem = "reset_coil"; @@ -80,6 +84,16 @@ private void Awake() new SoundTrigger { Id = SoundTargetBankReset, Name = "Sound Target Bank Reset" } }; + protected override Type MeshComponentType => throw new NotImplementedException(); + + protected override Type ColliderComponentType => throw new NotImplementedException(); + + public override bool HasProceduralMesh => throw new NotImplementedException(); + + public override ItemType ItemType => throw new NotImplementedException(); + + public override string ItemName => throw new NotImplementedException(); + public event EventHandler OnSound; internal void EmitSound(string triggerId, float volume = 1) @@ -87,6 +101,31 @@ internal void EmitSound(string triggerId, float volume = 1) OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume)); } + public override void CopyFromObject(GameObject go) + { + throw new NotImplementedException(); + } + + public override IEnumerable SetData(ItemData data) + { + throw new NotImplementedException(); + } + + public override IEnumerable SetReferencedData(ItemData data, Table table, IMaterialProvider materialProvider, ITextureProvider textureProvider, Dictionary components) + { + throw new NotImplementedException(); + } + + public override ItemData CopyDataTo(ItemData data, string[] materialNames, string[] textureNames, bool forExport) + { + throw new NotImplementedException(); + } + + public override ItemData InstantiateData() + { + throw new NotImplementedException(); + } + #endregion } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetApi.cs index f035076ca..0d7b1f163 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetApi.cs @@ -129,6 +129,13 @@ void IApiHittable.OnHit(int ballId, bool _) MainComponent.EmitSound(TargetComponent.SoundTargetHit); } + void IApiDroppable.OnDropStatusChanged(bool isDropped, int ballId) + { + if (!isDropped) + { + MainComponent.EmitSound(DropTargetComponent.SoundTargetReset); + } + } #endregion } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetComponent.cs index 39d88c32e..8662ce5ab 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetComponent.cs @@ -42,6 +42,12 @@ protected override float ZOffset { } } + #region Overrides and Constants + + public const string SoundTargetReset = "sound_target_reset"; + + #endregion + #region Conversion public override IEnumerable SetData(HitTargetData data) @@ -175,5 +181,14 @@ internal DropTargetState CreateState() } #endregion + + #region ISoundEmitter + + public override SoundTrigger[] AvailableTriggers => new[] { + new SoundTrigger { Id = SoundTargetHit, Name = "Target Drop" }, + new SoundTrigger { Id = SoundTargetReset, Name = "Target Reset" }, + }; + + #endregion } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/TargetComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/TargetComponent.cs index 172bfd08e..bbe593584 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/TargetComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/TargetComponent.cs @@ -198,7 +198,7 @@ public override void CopyFromObject(GameObject go) #region ISoundEmitter - public SoundTrigger[] AvailableTriggers => new[] { + public virtual SoundTrigger[] AvailableTriggers => new[] { new SoundTrigger { Id = SoundTargetHit, Name = "Target Hit" }, }; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/ScoreMotorApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/ScoreMotorApi.cs index dceb394d8..5c658b24a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/ScoreMotorApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/ScoreMotorApi.cs @@ -17,10 +17,12 @@ using System; using NLog; using UnityEngine; +using VisualPinball.Engine.VPT; using Logger = NLog.Logger; namespace VisualPinball.Unity { + // public class ScoreMotorApi : ItemApi, IApi, IApiSwitchDevice public class ScoreMotorApi : IApi, IApiSwitchDevice { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); @@ -48,6 +50,7 @@ public IApiSwitch Switch(string deviceItem) }; } + // internal ScoreMotorApi(GameObject go, Player player, PhysicsEngine physicsEngine) : base(go, player, physicsEngine) internal ScoreMotorApi(GameObject go, Player player, PhysicsEngine physicsEngine) { _scoreMotorComponent = go.GetComponentInChildren(); @@ -57,6 +60,8 @@ internal ScoreMotorApi(GameObject go, Player player, PhysicsEngine physicsEngine _scoreMotorComponent.OnSwitchChanged += HandleSwitchChanged; } + #region Events + void IApi.OnInit(BallManager ballManager) { _motorRunningSwitch = new DeviceSwitch(ScoreMotorComponent.MotorRunningSwitchItem, false, SwitchDefault.NormallyOpen, _player, _physicsEngine); @@ -68,6 +73,11 @@ void IApi.OnInit(BallManager ballManager) private void HandleSwitchChanged(object sender, SwitchEventArgs2 e) { ((DeviceSwitch)Switch(e.Id)).SetSwitch(e.IsEnabled); + + //if (e.IsEnabled) + //{ + // MainComponent.EmitSound(ScoreMotorComponent.SoundScoreClear); + //} } void IApi.OnDestroy() @@ -76,5 +86,7 @@ void IApi.OnDestroy() Logger.Info($"Destroying {_scoreMotorComponent.name}"); } + + #endregion } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/ScoreMotorComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/ScoreMotorComponent.cs index 9cf911f60..0e7ab0886 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/ScoreMotorComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/ScoreMotorComponent.cs @@ -22,6 +22,7 @@ using UnityEngine; using VisualPinball.Engine.Game.Engines; using VisualPinball.Engine.VPT.Gate; +using VisualPinball.Engine.VPT.Sound; using Logger = NLog.Logger; namespace VisualPinball.Unity @@ -31,6 +32,7 @@ namespace VisualPinball.Unity [AddComponentMenu("Visual Pinball/Mechs/Score Motor")] [HelpURL("https://docs.visualpinball.org/creators-guide/manual/mechanisms/score-motors.html")] + // public class ScoreMotorComponent : MonoBehaviour, ISwitchDeviceComponent, ISoundEmitter public class ScoreMotorComponent : MonoBehaviour, ISwitchDeviceComponent { public const int MaxIncrease = 5; @@ -57,6 +59,9 @@ public class ScoreMotorComponent : MonoBehaviour, ISwitchDeviceComponent public const string MotorRunningSwitchItem = "motor_running_switch"; public const string MotorStepSwitchItem = "motor_step_switch"; + public const string SoundScoreClear = "sound_score_clear"; + //public const string SoundScoreMotorStep = "sound_score_step"; + public IEnumerable AvailableSwitches => new[] { new GamelogicEngineSwitch(MotorRunningSwitchItem) { @@ -266,6 +271,23 @@ private float ResetScore(float score) } #endregion + + + + //#region ISoundEmitter + + //public SoundTrigger[] AvailableTriggers => new[] { + // new SoundTrigger { Id = SoundScoreClear, Name = "Sound Score Clear" } + //}; + + //public event EventHandler OnSound; + + //internal void EmitSound(string triggerId, float volume = 1) + //{ + // OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume)); + //} + + //#endregion } [Serializable] From 491b5c3ac50f80bf7c81870b77b60de24fdafba5 Mon Sep 17 00:00:00 2001 From: Jonathan Linowes Date: Mon, 29 Jan 2024 20:22:29 -0500 Subject: [PATCH 25/76] button to add MechSound with default volume=1 --- .../Sound/MechSoundsInspector.cs | 7 +++++++ VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs | 1 + 2 files changed, 8 insertions(+) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs index 63d064a91..9a7d983ef 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs @@ -47,6 +47,13 @@ public override void OnInspectorGUI() EditorGUILayout.PropertyField(_soundsProperty); + // unity doesnt use default values when adding items in a list so force it (Volume=1) + if (GUILayout.Button("Add New MechSound")) + { + comp.Sounds.Add(new MechSound()); + EditorUtility.SetDirty(comp); + } + EditorGUILayout.PropertyField(_audioMixerProperty); serializedObject.ApplyModifiedProperties(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs index 3f328045b..ca84b8fe8 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs @@ -30,6 +30,7 @@ public class MechSound public string TriggerId; [Range(0.0001f, 1)] + // this initialization doesnt work in inspector https://www.reddit.com/r/Unity3D/comments/j5i6cj/inspector_struct_default_values/ public float Volume = 1; public MechSoundAction Action = MechSoundAction.Play; From 25b528a42fb6ba8c34abce6bc6cbd963efbb0180 Mon Sep 17 00:00:00 2001 From: arthurkehrwald <50906979+arthurkehrwald@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:40:06 +0100 Subject: [PATCH 26/76] Apply default values to MechSound instances --- .../Sound/MechSoundsInspector.cs | 8 ------ .../VisualPinball.Unity/Sound/MechSound.cs | 28 ++++++++++++++++--- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs index 9a7d983ef..23434d978 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs @@ -46,14 +46,6 @@ public override void OnInspectorGUI() serializedObject.Update(); EditorGUILayout.PropertyField(_soundsProperty); - - // unity doesnt use default values when adding items in a list so force it (Volume=1) - if (GUILayout.Button("Add New MechSound")) - { - comp.Sounds.Add(new MechSound()); - EditorUtility.SetDirty(comp); - } - EditorGUILayout.PropertyField(_audioMixerProperty); serializedObject.ApplyModifiedProperties(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs index ca84b8fe8..7cb35ef03 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Sound/MechSound.cs @@ -22,7 +22,7 @@ namespace VisualPinball.Unity { [Serializable] - public class MechSound + public class MechSound : ISerializationCallbackReceiver { [SerializeReference] public SoundAsset Sound; @@ -30,16 +30,36 @@ public class MechSound public string TriggerId; [Range(0.0001f, 1)] - // this initialization doesnt work in inspector https://www.reddit.com/r/Unity3D/comments/j5i6cj/inspector_struct_default_values/ + // This initialization doesnt work in inspector public float Volume = 1; - + public MechSoundAction Action = MechSoundAction.Play; [Tooltip("Increments of 1000")] [Min(0)] [Unit("ms")] public float Fade; + + #region DefaultValuesWorkaround + // When an instance is created by pressing the + icon on a list in the inspector, + // Unity does not apply default values (such as Volume = 1) and no constructor is called. + // See https://www.reddit.com/r/Unity3D/comments/j5i6cj/inspector_struct_default_values/. + // This workaround applies default values the first time the struct is serialized instead. + // It only works for the first instance in the list, because for any subsequent instance Unity + // clones the field values of the previous instance, including the areDefaultsApplied flag. + [SerializeField] + private bool areDefaultsApplied = false; + + public void OnAfterDeserialize() { } + public void OnBeforeSerialize() + { + if (!areDefaultsApplied) { + Volume = 1; + areDefaultsApplied = true; + } + } + #endregion } - + public enum MechSoundAction { Play, Stop }; } From b38ae1d5eb98ed293618228c8d258b03ad08ce44 Mon Sep 17 00:00:00 2001 From: arthurkehrwald <50906979+arthurkehrwald@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:58:35 +0100 Subject: [PATCH 27/76] Fix UNT0026 warning --- .../VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs index 23434d978..592452cec 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs @@ -36,9 +36,7 @@ private void OnEnable() public override void OnInspectorGUI() { var comp = target as MechSoundsComponent; - - var soundEmitter = comp!.GetComponent(); - if (soundEmitter == null) { + if (!comp!.TryGetComponent(out _)) { EditorGUILayout.HelpBox("Cannot find sound emitter. This component only works with a sound emitter on the same GameObject.", MessageType.Error); return; } From 5416805266c33cf18c3540b2bc7aacc70ecd9d09 Mon Sep 17 00:00:00 2001 From: arthurkehrwald <50906979+arthurkehrwald@users.noreply.github.com> Date: Mon, 25 Nov 2024 12:00:11 +0100 Subject: [PATCH 28/76] Remove unused variable --- .../VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs index 592452cec..b70f9d2d0 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs @@ -29,8 +29,6 @@ private void OnEnable() { _audioMixerProperty = serializedObject.FindProperty(nameof(MechSoundsComponent.AudioMixer)); _soundsProperty = serializedObject.FindProperty(nameof(MechSoundsComponent.Sounds)); - - var comp = target as MechSoundsComponent; } public override void OnInspectorGUI() From 01879957b922225463c251a3f2702471f3a9b166 Mon Sep 17 00:00:00 2001 From: arthurkehrwald <50906979+arthurkehrwald@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:51:12 +0100 Subject: [PATCH 29/76] Do not change open scene when previewing sounds --- .../Sound/SoundAssetInspector.cs | 66 ++++++++++--------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundAssetInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundAssetInspector.cs index 9713d2922..894fd7253 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundAssetInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundAssetInspector.cs @@ -16,6 +16,7 @@ using System.Linq; using UnityEditor; +using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; @@ -108,48 +109,49 @@ private bool PlayStopButton() } /// - /// Gets or creates the editor GameObject for playing sounds in the editor. - /// - /// The hierarchy looks like that: - /// - /// [scene root] - /// | - /// -- EditorScene - /// | - /// -- EditorAudio (with AudioSource component) + /// Gets or creates the AudioSource for playing sounds in the editor. + /// The object containing the AudioSource is created in a new, additively loaded scene + /// to avoid making changes to the user's currently open scene. /// - /// AudioSource of the editor GameObject for test playing audio. + /// AudioSource for previewing audio assets in the editor private static AudioSource GetOrCreateAudioSource() { - // todo check whether we'll instantiate those live in the future or rely on a provided prefab - var editorSceneGo = SceneManager.GetActiveScene().GetRootGameObjects() - .FirstOrDefault(go => go.name == "EditorScene"); - - if (editorSceneGo == null) { - editorSceneGo = new GameObject("EditorScene"); + Scene editorScene = GetOrCreatePreviewScene(); + GameObject editorAudio = GetOrCreatePreviewAudioObject(editorScene); + if (!editorAudio.TryGetComponent(out var audioSource)) { + audioSource = editorAudio.AddComponent(); } + return audioSource; + } - GameObject editorAudioGo = null; - for (var i = 0; i < editorSceneGo.transform.childCount; i++) { - var go = editorSceneGo.transform.GetChild(i).gameObject; - if (go.name != "EditorAudio") { - continue; - } - editorAudioGo = go; - break; - } + private static Scene GetOrCreatePreviewScene() + { + const string sceneName = "VpeEditorScene"; - if (editorAudioGo == null) { - editorAudioGo = new GameObject("EditorAudio"); - editorAudioGo.transform.SetParent(editorSceneGo.transform); + for (int i = 0; i < SceneManager.loadedSceneCount; i++) { + Scene scene = SceneManager.GetSceneAt(i); + if (scene.name == sceneName) + return scene; } - var audioSource = editorAudioGo.GetComponent(); - if (!audioSource) { - audioSource = editorAudioGo.AddComponent(); + Scene previewScene = EditorSceneManager.NewPreviewScene(); + previewScene.name = sceneName; + return previewScene; + } + + private static GameObject GetOrCreatePreviewAudioObject(Scene previewScene) + { + const string audioObjName = "AudioPreview"; + + var audioObj = previewScene.GetRootGameObjects() + .FirstOrDefault(go => go.name == audioObjName); + + if (audioObj == null) { + audioObj = new GameObject(audioObjName); + SceneManager.MoveGameObjectToScene(audioObj, previewScene); } - return audioSource; + return audioObj; } } } From 0ffc529bd59dc7cd3ace56a855fab78cba9fc2c9 Mon Sep 17 00:00:00 2001 From: arthurkehrwald <50906979+arthurkehrwald@users.noreply.github.com> Date: Mon, 25 Nov 2024 20:15:22 +0100 Subject: [PATCH 30/76] DropTargetBankComponent doesn't inherit MainRenderableComponent --- .../VPT/DropTargetBank/DropTargetBankApi.cs | 8 ++-- .../DropTargetBank/DropTargetBankComponent.cs | 41 +------------------ 2 files changed, 4 insertions(+), 45 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankApi.cs index 13a283900..b8c27b0c0 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankApi.cs @@ -18,12 +18,11 @@ using Logger = NLog.Logger; using NLog; using UnityEngine; -using VisualPinball.Engine.VPT; using System.Collections.Generic; namespace VisualPinball.Unity { - public class DropTargetBankApi : ItemApi, IApi, IApiCoilDevice, IApiSwitchDevice + public class DropTargetBankApi : IApi, IApiCoilDevice, IApiSwitchDevice { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); @@ -58,7 +57,7 @@ private IApiCoil Coil(string deviceItem) }; } - internal DropTargetBankApi(GameObject go, Player player, PhysicsEngine physicsEngine) : base(go, player, physicsEngine) + internal DropTargetBankApi(GameObject go, Player player, PhysicsEngine physicsEngine) { _dropTargetBankComponent = go.GetComponentInChildren(); _player = player; @@ -104,8 +103,7 @@ private void OnResetCoilEnabled() dropTargetApi.IsDropped = false; } - // ? is this where this goes? - MainComponent.EmitSound(DropTargetBankComponent.SoundTargetBankReset); + _dropTargetBankComponent.EmitSound(DropTargetBankComponent.SoundTargetBankReset); } void IApi.OnDestroy() diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankComponent.cs index a2d85d315..c52bc7133 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/DropTargetBank/DropTargetBankComponent.cs @@ -17,18 +17,14 @@ using System.Collections.Generic; using UnityEngine; using VisualPinball.Engine.Game.Engines; -using VisualPinball.Engine.VPT; using System.ComponentModel; using System; -using VisualPinball.Engine.VPT.HitTarget; -using VisualPinball.Engine.IO; -using VisualPinball.Engine.VPT.Table; namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Mechs/Drop Target Bank")] [HelpURL("https://docs.visualpinball.org/creators-guide/manual/mechanisms/drop-target-banks.html")] - public class DropTargetBankComponent : MainRenderableComponent, ICoilDeviceComponent, ISwitchDeviceComponent, ISoundEmitter + public class DropTargetBankComponent : MonoBehaviour, ICoilDeviceComponent, ISwitchDeviceComponent, ISoundEmitter { public const string ResetCoilItem = "reset_coil"; @@ -84,16 +80,6 @@ private void Awake() new SoundTrigger { Id = SoundTargetBankReset, Name = "Sound Target Bank Reset" } }; - protected override Type MeshComponentType => throw new NotImplementedException(); - - protected override Type ColliderComponentType => throw new NotImplementedException(); - - public override bool HasProceduralMesh => throw new NotImplementedException(); - - public override ItemType ItemType => throw new NotImplementedException(); - - public override string ItemName => throw new NotImplementedException(); - public event EventHandler OnSound; internal void EmitSound(string triggerId, float volume = 1) @@ -101,31 +87,6 @@ internal void EmitSound(string triggerId, float volume = 1) OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume)); } - public override void CopyFromObject(GameObject go) - { - throw new NotImplementedException(); - } - - public override IEnumerable SetData(ItemData data) - { - throw new NotImplementedException(); - } - - public override IEnumerable SetReferencedData(ItemData data, Table table, IMaterialProvider materialProvider, ITextureProvider textureProvider, Dictionary components) - { - throw new NotImplementedException(); - } - - public override ItemData CopyDataTo(ItemData data, string[] materialNames, string[] textureNames, bool forExport) - { - throw new NotImplementedException(); - } - - public override ItemData InstantiateData() - { - throw new NotImplementedException(); - } - #endregion } } From 3bdfed2fdc177f5ef924d4d22f2496f856eab1d6 Mon Sep 17 00:00:00 2001 From: arthurkehrwald <50906979+arthurkehrwald@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:53:49 +0100 Subject: [PATCH 31/76] Complete score motor sound support --- .../VPT/Mech/ScoreMotorApi.cs | 24 ++++++++------- .../VPT/Mech/ScoreMotorComponent.cs | 30 ++++++++++--------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/ScoreMotorApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/ScoreMotorApi.cs index 5c658b24a..40da3b6f8 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/ScoreMotorApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/ScoreMotorApi.cs @@ -17,12 +17,10 @@ using System; using NLog; using UnityEngine; -using VisualPinball.Engine.VPT; using Logger = NLog.Logger; namespace VisualPinball.Unity { - // public class ScoreMotorApi : ItemApi, IApi, IApiSwitchDevice public class ScoreMotorApi : IApi, IApiSwitchDevice { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); @@ -45,12 +43,11 @@ public IApiSwitch Switch(string deviceItem) ScoreMotorComponent.MotorRunningSwitchItem => _motorRunningSwitch, ScoreMotorComponent.MotorStepSwitchItem => _motorStepSwitch, _ => throw new ArgumentException($"Unknown switch \"{deviceItem}\". " - + "Valid names are \"{ScoreReelDisplayComponent.MotorRunningSwitchItem}\", and " - + "\"{ScoreReelDisplayComponent.MotorStepSwitchItem}\".") + + $"Valid names are \"{ScoreMotorComponent.MotorRunningSwitchItem}\", and " + + $"\"{ScoreMotorComponent.MotorStepSwitchItem}\".") }; } - // internal ScoreMotorApi(GameObject go, Player player, PhysicsEngine physicsEngine) : base(go, player, physicsEngine) internal ScoreMotorApi(GameObject go, Player player, PhysicsEngine physicsEngine) { _scoreMotorComponent = go.GetComponentInChildren(); @@ -72,12 +69,17 @@ void IApi.OnInit(BallManager ballManager) private void HandleSwitchChanged(object sender, SwitchEventArgs2 e) { - ((DeviceSwitch)Switch(e.Id)).SetSwitch(e.IsEnabled); - - //if (e.IsEnabled) - //{ - // MainComponent.EmitSound(ScoreMotorComponent.SoundScoreClear); - //} + var deviceSwitch = (DeviceSwitch)Switch(e.Id); + deviceSwitch.SetSwitch(e.IsEnabled); + + if (deviceSwitch == _motorStepSwitch && e.IsEnabled) { + _scoreMotorComponent.EmitSound(ScoreMotorComponent.SoundScoreMotorStep); + } else if (deviceSwitch == _motorRunningSwitch) { + if (e.IsEnabled) + _scoreMotorComponent.EmitSound(ScoreMotorComponent.SoundScoreMotorStart); + else + _scoreMotorComponent.EmitSound(ScoreMotorComponent.SoundScoreMotorStop); + } } void IApi.OnDestroy() diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/ScoreMotorComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/ScoreMotorComponent.cs index 0e7ab0886..a412a737b 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/ScoreMotorComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Mech/ScoreMotorComponent.cs @@ -32,8 +32,7 @@ namespace VisualPinball.Unity [AddComponentMenu("Visual Pinball/Mechs/Score Motor")] [HelpURL("https://docs.visualpinball.org/creators-guide/manual/mechanisms/score-motors.html")] - // public class ScoreMotorComponent : MonoBehaviour, ISwitchDeviceComponent, ISoundEmitter - public class ScoreMotorComponent : MonoBehaviour, ISwitchDeviceComponent + public class ScoreMotorComponent : MonoBehaviour, ISwitchDeviceComponent, ISoundEmitter { public const int MaxIncrease = 5; @@ -59,8 +58,9 @@ public class ScoreMotorComponent : MonoBehaviour, ISwitchDeviceComponent public const string MotorRunningSwitchItem = "motor_running_switch"; public const string MotorStepSwitchItem = "motor_step_switch"; - public const string SoundScoreClear = "sound_score_clear"; - //public const string SoundScoreMotorStep = "sound_score_step"; + public const string SoundScoreMotorStart = "sound_score_motor_start"; + public const string SoundScoreMotorStop = "sound_score_motor_stop"; + public const string SoundScoreMotorStep = "sound_score_motor_step"; public IEnumerable AvailableSwitches => new[] { new GamelogicEngineSwitch(MotorRunningSwitchItem) @@ -274,20 +274,22 @@ private float ResetScore(float score) - //#region ISoundEmitter + #region ISoundEmitter - //public SoundTrigger[] AvailableTriggers => new[] { - // new SoundTrigger { Id = SoundScoreClear, Name = "Sound Score Clear" } - //}; + public SoundTrigger[] AvailableTriggers => new[] { + new SoundTrigger { Id = SoundScoreMotorStart, Name = "Sound Score Motor Start" }, + new SoundTrigger { Id = SoundScoreMotorStop, Name = "Sound Score Motor Stop" }, + new SoundTrigger { Id = SoundScoreMotorStep, Name = "Sound Score Motor Step" } + }; - //public event EventHandler OnSound; + public event EventHandler OnSound; - //internal void EmitSound(string triggerId, float volume = 1) - //{ - // OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume)); - //} + internal void EmitSound(string triggerId, float volume = 1) + { + OnSound?.Invoke(this, new SoundEventArgs(triggerId, volume)); + } - //#endregion + #endregion } [Serializable] From b2fb22b8f1aec74d9593b6158ad61fef4b6383f2 Mon Sep 17 00:00:00 2001 From: arthurkehrwald <50906979+arthurkehrwald@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:31:39 +0100 Subject: [PATCH 32/76] Rewrite inspectors and sound playback --- .../Sound/MechSoundDrawer.cs | 128 +++++++++------- .../Sound/MechSoundsInspector.cs | 50 ------- .../Sound/SoundDrawer.uxml | 6 + .../Sound/SoundDrawer.uxml.meta | 10 ++ .../Sound/SoundsComponentInspector.cs | 25 ++++ ....meta => SoundsComponentInspector.cs.meta} | 24 +-- .../Sound/SoundsComponentInspector.uss | 5 + .../Sound/SoundsComponentInspector.uss.meta | 11 ++ .../Sound/SoundsComponentInspector.uxml | 5 + .../Sound/SoundsComponentInspector.uxml.meta | 10 ++ .../VisualPinball.Unity/Sound/MechSound.cs | 9 +- .../Sound/MechSoundsComponent.cs | 138 +++++------------- .../VisualPinball.Unity/Sound/SoundAsset.cs | 6 +- 13 files changed, 202 insertions(+), 225 deletions(-) delete mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundDrawer.uxml create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundDrawer.uxml.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.cs rename VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/{MechSoundsInspector.cs.meta => SoundsComponentInspector.cs.meta} (52%) create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.uss create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.uss.meta create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.uxml create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.uxml.meta diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundDrawer.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundDrawer.cs index 7194aedd6..84bac4b74 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundDrawer.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundDrawer.cs @@ -14,71 +14,99 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +using System; using System.Linq; using UnityEditor; -using UnityEngine; +using UnityEditor.UIElements; +using UnityEngine.UIElements; namespace VisualPinball.Unity.Editor { [CustomPropertyDrawer(typeof(MechSound))] public class MechSoundDrawer : PropertyDrawer { - public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + public override VisualElement CreatePropertyGUI(SerializedProperty property) { - return (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing) * 5 + 4f; + var container = new VisualElement(); + var treeAsset = AssetDatabase.LoadAssetAtPath( + "Packages/org.visualpinball.engine.unity/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundDrawer.uxml"); + treeAsset.CloneTree(container); + var triggerDropdown = container.Q("trigger-id"); + var stopTriggerDropdown = container.Q("stop-trigger-id"); + var hasStopTriggerToggle = container.Q("has-stop-trigger"); + var availableTriggers = GetAvailableTriggers(property); + if (availableTriggers.Length > 0) { + var triggerIdProp = property.FindPropertyRelative("TriggerId"); + var stopTriggerIdProp = property.FindPropertyRelative("StopTriggerId"); + ConfigureTriggerDropdown(triggerIdProp, triggerDropdown, availableTriggers); + ConfigureTriggerDropdown(stopTriggerIdProp, stopTriggerDropdown, availableTriggers); + hasStopTriggerToggle.RegisterValueChangedCallback( + e => stopTriggerDropdown.style.display = e.newValue ? DisplayStyle.Flex : DisplayStyle.None); + var hasStopTriggerProp = property.FindPropertyRelative("HasStopTrigger"); + ConfigureInfiniteLoopHelpBox(property, container, hasStopTriggerToggle, hasStopTriggerProp); + } else { + AddNoTriggersHelpBox(container, triggerDropdown, stopTriggerDropdown, hasStopTriggerToggle); + } + property.serializedObject.ApplyModifiedProperties(); + return container; } - public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) - { - // retrieve reference to GO and component - var mechSoundsComponent = (MechSoundsComponent)property.serializedObject.targetObject; - var soundEmitter = mechSoundsComponent.GetComponent(); + private static void ConfigureTriggerDropdown(SerializedProperty triggerIdProp, DropdownField triggerDropdown, SoundTrigger[] availableTriggers) + { + var availableTriggerNames = availableTriggers.Select(t => t.Name).ToList(); + triggerDropdown.choices = availableTriggerNames; - EditorGUI.BeginProperty(position, label, property); - - // init height - position.height = EditorGUIUtility.singleLineHeight; + var isSelectedTriggerValid = availableTriggers.Any(t => t.Id == triggerIdProp.stringValue); + if (isSelectedTriggerValid) { + triggerDropdown.value = availableTriggers.First(t => t.Id == triggerIdProp.stringValue).Name; + } else { + triggerDropdown.value = availableTriggerNames[0]; + triggerIdProp.stringValue = availableTriggers[0].Id; + } - // trigger drop-down - var triggerIdProperty = property.FindPropertyRelative(nameof(MechSound.TriggerId)); - var triggers = soundEmitter.AvailableTriggers; - if (triggers.Length > 0) { - var triggerIndex = triggers.ToList().FindIndex(t => t.Id == triggerIdProperty.stringValue); - if (triggerIndex == -1) { // pre-select first trigger in list, if none set. - triggerIndex = 0; - } - triggerIndex = EditorGUI.Popup(position, "Trigger on", triggerIndex, triggers.Select(t => t.Name).ToArray()); - triggerIdProperty.stringValue = triggers[triggerIndex].Id; - } else { - EditorGUI.LabelField(position, "No Triggers found."); - } - position.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; - - // sound object picker - var soundProperty = property.FindPropertyRelative(nameof(MechSound.Sound)); - EditorGUI.BeginChangeCheck(); - var soundValue = EditorGUI.ObjectField(position, "Sound", soundProperty.objectReferenceValue, typeof(SoundAsset), true); - if (EditorGUI.EndChangeCheck()) { - soundProperty.objectReferenceValue = soundValue; - } - position.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + triggerDropdown.RegisterValueChangedCallback( + e => { + triggerIdProp.stringValue = availableTriggers.FirstOrDefault(t => t.Name == e.newValue).Id; + triggerIdProp.serializedObject.ApplyModifiedProperties(); + }); + } - // volume - var volumeProperty = property.FindPropertyRelative(nameof(MechSound.Volume)); - EditorGUI.PropertyField(position, volumeProperty); - position.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; - - // action - var actionProperty = property.FindPropertyRelative(nameof(MechSound.Action)); - EditorGUI.PropertyField(position, actionProperty); - position.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; - - // fade - var fadeProperty = property.FindPropertyRelative(nameof(MechSound.Fade)); - EditorGUI.PropertyField(position, fadeProperty); - position.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + private static void AddNoTriggersHelpBox(VisualElement container, DropdownField triggerDropdown, DropdownField stopTriggerDropdown, Toggle hasStopTriggerToggle) + { + container.Insert(0, new HelpBox("There are no triggers to choose from", HelpBoxMessageType.Info)); + triggerDropdown.style.display = DisplayStyle.None; + stopTriggerDropdown.style.display = DisplayStyle.None; + hasStopTriggerToggle.style.display = DisplayStyle.None; + } - EditorGUI.EndProperty(); + private static void ConfigureInfiniteLoopHelpBox(SerializedProperty rootProp, VisualElement container, Toggle hasStopTriggerToggle, SerializedProperty hasStopTriggerProp) + { + var soundAssetProp = rootProp.FindPropertyRelative("Sound"); + var infiniteLoopHelpBox = new HelpBox("The selected sound asset loops and no stop trigger is set, so the sound will loop forever once started.", HelpBoxMessageType.Warning); + infiniteLoopHelpBox.style.display = DisplayStyle.None; + container.Insert(0, infiniteLoopHelpBox); + var soundAssetField = container.Q("sound-asset"); + soundAssetField.RegisterValueChangedCallback( + e => UpdateInfiniteLoopHelpBoxVisbility(soundAssetProp.objectReferenceValue as SoundAsset, hasStopTriggerProp.boolValue, infiniteLoopHelpBox)); + hasStopTriggerToggle.RegisterValueChangedCallback( + e => UpdateInfiniteLoopHelpBoxVisbility(soundAssetProp.objectReferenceValue as SoundAsset, hasStopTriggerProp.boolValue, infiniteLoopHelpBox)); + } + + private static void UpdateInfiniteLoopHelpBoxVisbility(SoundAsset soundAsset, bool hasStopTrigger, VisualElement box) + { + if (soundAsset && soundAsset.Loop && !hasStopTrigger) + box.style.display = DisplayStyle.Flex; + else + box.style.display = DisplayStyle.None; + } + + private static SoundTrigger[] GetAvailableTriggers(SerializedProperty property) + { + var mechSoundsComponent = (MechSoundsComponent)property.serializedObject.targetObject; + if (mechSoundsComponent.TryGetComponent(out var emitter)) + return emitter.AvailableTriggers; + else + return Array.Empty(); } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs deleted file mode 100644 index b70f9d2d0..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Visual Pinball Engine -// Copyright (C) 2023 freezy and VPE Team -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -using UnityEditor; -using UnityEngine; - -namespace VisualPinball.Unity.Editor -{ - [CustomEditor(typeof(MechSoundsComponent)), CanEditMultipleObjects] - public class MechanicalSoundInspector : UnityEditor.Editor - { - private SerializedProperty _audioMixerProperty; - private SerializedProperty _soundsProperty; - - private void OnEnable() - { - _audioMixerProperty = serializedObject.FindProperty(nameof(MechSoundsComponent.AudioMixer)); - _soundsProperty = serializedObject.FindProperty(nameof(MechSoundsComponent.Sounds)); - } - - public override void OnInspectorGUI() - { - var comp = target as MechSoundsComponent; - if (!comp!.TryGetComponent(out _)) { - EditorGUILayout.HelpBox("Cannot find sound emitter. This component only works with a sound emitter on the same GameObject.", MessageType.Error); - return; - } - - serializedObject.Update(); - - EditorGUILayout.PropertyField(_soundsProperty); - EditorGUILayout.PropertyField(_audioMixerProperty); - - serializedObject.ApplyModifiedProperties(); - } - } -} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundDrawer.uxml b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundDrawer.uxml new file mode 100644 index 000000000..3229f55ee --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundDrawer.uxml @@ -0,0 +1,6 @@ + + + + + + diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundDrawer.uxml.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundDrawer.uxml.meta new file mode 100644 index 000000000..01cf6511b --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundDrawer.uxml.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 5edbe5d4628bdb545a05bfa87ed2f700 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.cs new file mode 100644 index 000000000..d9dc09694 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.cs @@ -0,0 +1,25 @@ +using UnityEditor; +using UnityEngine; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + + +namespace VisualPinball.Unity.Editor +{ + [CustomEditor(typeof(MechSoundsComponent)), CanEditMultipleObjects] + public class SoundsComponentInspector : UnityEditor.Editor + { + [SerializeField] + private VisualTreeAsset inspectorXml; + + public override VisualElement CreateInspectorGUI() + { + VisualElement inspector = new VisualElement(); + var comp = target as MechSoundsComponent; + if (!comp!.TryGetComponent(out var _)) + inspector.Add(new HelpBox("Cannot find sound emitter. This component only works with a sound emitter on the same GameObject.", HelpBoxMessageType.Warning)); + inspectorXml.CloneTree(inspector); + return inspector; + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.cs.meta similarity index 52% rename from VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.cs.meta index 3c428cf24..80e6f578c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/MechSoundsInspector.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.cs.meta @@ -1,11 +1,13 @@ -fileFormatVersion: 2 -guid: 8dca340821f92c74e8cf97cd3fff29df -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: +fileFormatVersion: 2 +guid: b54f8fa6e77f2104a898dca7e163c4e6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: + - inspectorXml: {fileID: 9197481963319205126, guid: a6e8546981c540948ae9851157602432, + type: 3} + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.uss b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.uss new file mode 100644 index 000000000..218263a81 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.uss @@ -0,0 +1,5 @@ +.custom-label { + font-size: 20px; + -unity-font-style: bold; + color: rgb(68, 138, 255); +} \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.uss.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.uss.meta new file mode 100644 index 000000000..ef1e9b51a --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 30f22b4450bbae84eb18d9bc7f656e8c +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.uxml b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.uxml new file mode 100644 index 000000000..56f8b2094 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Sound/SoundsComponentInspector.uxml @@ -0,0 +1,5 @@ + +