From 45fb977b96f757ae4d02c66c0a3c97a47afb31ed Mon Sep 17 00:00:00 2001 From: Mr-Jay-Gatsby Date: Mon, 13 Feb 2023 11:35:45 -0500 Subject: [PATCH 01/13] 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 | 10 + .../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, 1131 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 d47362d73..af0087258 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs @@ -79,9 +79,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"; @@ -92,6 +94,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"; @@ -115,8 +118,8 @@ public IconVariant(string name, IconSize size, IconColor color) private static readonly string[] Names = { AssetLibraryName, BallRollerName, BoltName, BumperName, CalendarName, CannonName, CoilName, DropTargetBankName, DropTargetName, FlasherName, - FlipperName, GateName, GateLifterName, HitTargetName, KeyName, KickerName, LightGroupName, LightName, MechName, MechPinMameName, PlayfieldName, PlugName, - PlungerName, PrimitiveName, RampName, RotatorName, RubberName, ScoreReelName, ScoreReelSingleName, SlingshotName, SpinnerName, SurfaceName, + FlipperName, GateName, GateLifterName, HitTargetName, KeyName, KickerName, LightGroupName, LightName, LoopButtonName, MechName, MechPinMameName, PlayfieldName, PlayButtonName, PlugName, + 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 @@ -179,11 +182,13 @@ 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 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); @@ -194,6 +199,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 47d088618..1d1a8c6ac 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -25,6 +25,8 @@ 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 Logger = NLog.Logger; @@ -182,6 +184,7 @@ private void Start() if (EngineProvider.Exists) { EngineProvider.Get().Init(_tableComponent); } + } private void Update() @@ -266,6 +269,11 @@ public void RegisterLampGroup(LightGroupComponent component) Register(component.GetApi(this), component); } + public void RegisterMechSound(MechSoundsComponent component) + { + + } + public void RegisterStepRotator(StepRotatorMechComponent component) { Register(new StepRotatorMechApi(component.gameObject, this), component); @@ -608,4 +616,6 @@ public BallEvent(Entity ballEntity, 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 fe8fc21e5..b725d0130 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 System.Collections.Generic; using Unity.Entities; @@ -38,7 +39,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 c033d7ca53ebc34adbfd928f580b75bb6720efa0 Mon Sep 17 00:00:00 2001 From: Mr-Jay-Gatsby Date: Wed, 15 Feb 2023 16:28:37 -0500 Subject: [PATCH 02/13] 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 e03fc4839..c1d700adf 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -41,7 +41,7 @@ namespace VisualPinball.Unity [HelpURL("https://docs.visualpinball.org/creators-guide/manual/mechanisms/flippers.html")] public class FlipperComponent : MainRenderableComponent, IFlipperData, ISwitchDeviceComponent, ICoilDeviceComponent, IOnSurfaceComponent, - IRotatableComponent, IConvertGameObjectToEntity + IRotatableComponent, IConvertGameObjectToEntity, ISoundEmitter { #region Data @@ -128,12 +128,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 c34940566e66f58fe9a82dde414388d12a7be299 Mon Sep 17 00:00:00 2001 From: Mr-Jay-Gatsby Date: Thu, 16 Feb 2023 10:14:40 -0500 Subject: [PATCH 03/13] 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 517cb8c4220417613697cae0fb8607fa793d440e Mon Sep 17 00:00:00 2001 From: Mr-Jay-Gatsby Date: Thu, 16 Feb 2023 13:58:55 -0500 Subject: [PATCH 04/13] 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 | 2 + .../Sounds/MechSounds/MechSoundsComponent.cs | 6 +- 8 files changed, 75 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 1d1a8c6ac..83be310f9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -25,8 +25,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 Logger = NLog.Logger; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs index b725d0130..f6ea712d7 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 System.Collections.Generic; using Unity.Entities; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs index c1d700adf..69bb2fdfc 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -26,6 +26,7 @@ using Unity.Collections; using Unity.Entities; using Unity.Mathematics; +using UnityEditor; using UnityEngine; using VisualPinball.Engine.Game.Engines; using VisualPinball.Engine.Math; @@ -165,6 +166,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 25979ae669279725dc5974ca41e54ead1a252ca6 Mon Sep 17 00:00:00 2001 From: Mr-Jay-Gatsby Date: Sat, 18 Feb 2023 14:07:41 -0500 Subject: [PATCH 05/13] 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 83be310f9..b6c134808 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -20,11 +20,13 @@ using NLog; using Unity.Entities; 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 Logger = NLog.Logger; @@ -185,6 +187,13 @@ private void Start() } + private void Component_OnSound(object sender, SoundEventArgs e) + { + //throw new NotImplementedException(); + Debug.Log("test"); + + } + private void Update() { OnUpdate?.Invoke(this, EventArgs.Empty); From 1672d5176ec3d6cb352bb089cc7d3f2656edf062 Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 19 Feb 2023 00:32:16 +0100 Subject: [PATCH 06/13] 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 e68acdc84b2eb31eede1efb0cd17872e9e88c47e Mon Sep 17 00:00:00 2001 From: freezy Date: Wed, 1 Mar 2023 22:31:49 +0100 Subject: [PATCH 07/13] 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 | 17 +- .../VisualPinball.Unity/VPT/Sounds.meta | 8 - .../VPT/Sounds/GameSounds.meta | 8 - .../VPT/Sounds/MechSounds.meta | 8 - 28 files changed, 686 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 69bb2fdfc..3eead0854 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -23,10 +23,10 @@ using System; using System.Collections.Generic; +using System.Linq; using Unity.Collections; using Unity.Entities; using Unity.Mathematics; -using UnityEditor; using UnityEngine; using VisualPinball.Engine.Game.Engines; using VisualPinball.Engine.Math; @@ -149,24 +149,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 f96d8125562bdacff7fb832f202bfbac56b4de3c Mon Sep 17 00:00:00 2001 From: freezy Date: Thu, 2 Mar 2023 00:04:08 +0100 Subject: [PATCH 08/13] 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 | 2 + .../VPT/Flipper/FlipperComponent.cs | 47 +++++++---------- 7 files changed, 77 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 f6ea712d7..e31ca41c6 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperApi.cs @@ -94,6 +94,7 @@ void IApi.OnDestroy() public void RotateToEnd() { EngineProvider.Get().FlipperRotateToEnd(Entity); + MainComponent.EmitSound(FlipperComponent.SoundCoilOn, MainComponent.RotatePosition); } /// @@ -103,6 +104,7 @@ public void RotateToEnd() public void RotateToStart() { EngineProvider.Get().FlipperRotateToStart(Entity); + MainComponent.EmitSound(FlipperComponent.SoundCoilOff); } internal float StartAngle diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs index 3eead0854..00bc0a239 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.Entities; using Unity.Mathematics; @@ -129,49 +128,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 b27156c5be2a8a9fd4f3e2b37badde4ed6cb271c Mon Sep 17 00:00:00 2001 From: freezy Date: Thu, 2 Mar 2023 00:09:42 +0100 Subject: [PATCH 09/13] sounds: Clean up obsolete stuff. --- VisualPinball.Engine/VPT/Enums.cs | 2 -- .../VisualPinball.Unity/Game/Player.cs | 16 ---------- .../VisualPinball.Unity/Sound/SoundTrigger.cs | 2 +- .../Sound/VolumeEmitter.cs | 31 ------------------- .../Sound/VolumeEmitter.cs.meta | 3 -- 5 files changed, 1 insertion(+), 53 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 b6c134808..cf1d8648d 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -20,13 +20,11 @@ using NLog; using Unity.Entities; 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 Logger = NLog.Logger; @@ -187,13 +185,6 @@ private void Start() } - private void Component_OnSound(object sender, SoundEventArgs e) - { - //throw new NotImplementedException(); - Debug.Log("test"); - - } - private void Update() { OnUpdate?.Invoke(this, EventArgs.Empty); @@ -276,11 +267,6 @@ public void RegisterLampGroup(LightGroupComponent component) Register(component.GetApi(this), component); } - public void RegisterMechSound(MechSoundsComponent component) - { - - } - public void RegisterStepRotator(StepRotatorMechComponent component) { Register(new StepRotatorMechApi(component.gameObject, this), component); @@ -623,6 +609,4 @@ public BallEvent(Entity ballEntity, 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 98cbf56535f6abdd2843dd6d0d5b1c3722bcd3a2 Mon Sep 17 00:00:00 2001 From: freezy Date: Thu, 2 Mar 2023 00:29:51 +0100 Subject: [PATCH 10/13] 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 00bc0a239..9443342ac 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -153,6 +153,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 e76254dc41d0d5634a582cc1ab8f9288f84e1334 Mon Sep 17 00:00:00 2001 From: Mr-Jay-Gatsby Date: Fri, 3 Mar 2023 11:12:36 -0500 Subject: [PATCH 11/13] 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 41f7547a90d7e7fb230e01427303c39aec0cb02b Mon Sep 17 00:00:00 2001 From: Mr-Jay-Gatsby Date: Tue, 21 Mar 2023 10:20:19 -0400 Subject: [PATCH 12/13] 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 9443342ac..0049c10b0 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -148,9 +148,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 5c22805bb1a289b0ca89ab07108fd0ffb74b9906 Mon Sep 17 00:00:00 2001 From: Mr-Jay-Gatsby Date: Thu, 30 Mar 2023 13:52:56 -0400 Subject: [PATCH 13/13] 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 496e4441c..ac1244aea 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs @@ -100,6 +100,8 @@ void IApiHittable.OnHit(Entity ballEntity, bool isUnHit) Hit?.Invoke(this, new HitEventArgs(ballEntity)); Switch?.Invoke(this, new SwitchEventArgs(!isUnHit, ballEntity)); 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 326c34b6f..1877f3559 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperComponent.cs @@ -35,7 +35,7 @@ namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Game Item/Bumper")] public class BumperComponent : MainRenderableComponent, - ISwitchDeviceComponent, ICoilDeviceComponent, IOnSurfaceComponent, IConvertGameObjectToEntity + ISwitchDeviceComponent, ICoilDeviceComponent, IOnSurfaceComponent, IConvertGameObjectToEntity, ISoundEmitter { #region Data @@ -81,6 +81,22 @@ public class BumperComponent : MainRenderableComponent, private 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 0049c10b0..9443342ac 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -148,9 +148,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 2d387eacd..e9c7a716c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerApi.cs @@ -170,6 +170,8 @@ private void OnBallDestroyed() kickerCollisionData.BallEntity = Entity.Null; entityManager.SetComponentData(Entity, kickerCollisionData); } + + 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 de586fab7..4698a5524 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Kicker/KickerComponent.cs @@ -39,7 +39,7 @@ namespace VisualPinball.Unity [AddComponentMenu("Visual Pinball/Game Item/Kicker")] public class KickerComponent : MainRenderableComponent, ICoilDeviceComponent, ITriggerComponent, IBallCreationPosition, IOnSurfaceComponent, - IRotatableComponent, IConvertGameObjectToEntity, ISerializationCallbackReceiver + IRotatableComponent, IConvertGameObjectToEntity, ISerializationCallbackReceiver, ISoundEmitter { #region Data @@ -83,6 +83,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 04f363c28..a25a90531 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerApi.cs @@ -105,6 +105,8 @@ public void PullBack() EntityManager.SetComponentData(Entity, movementData); EntityManager.SetComponentData(Entity, velocityData); + + MainComponent.EmitSound(PlungerComponent.SoundPlungerPull); } public void Fire() @@ -138,6 +140,8 @@ public void Fire() EntityManager.SetComponentData(Entity, movementData); EntityManager.SetComponentData(Entity, velocityData); + + 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 fb81a5baf..afcf20935 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerComponent.cs @@ -33,7 +33,7 @@ namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Game Item/Plunger")] public class PlungerComponent : MainRenderableComponent, - ICoilDeviceComponent, IOnSurfaceComponent, IConvertGameObjectToEntity + ICoilDeviceComponent, IOnSurfaceComponent, IConvertGameObjectToEntity, ISoundEmitter { #region Data @@ -71,6 +71,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 Wiring