Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mechanical sound support. #453

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions VisualPinball.Engine/VPT/MechSounds.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion VisualPinball.Unity/Assets/Presets.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions VisualPinball.Unity/VisualPinball.Unity.Editor/Sound.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// 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 <https://www.gnu.org/licenses/>.

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<ISoundEmitter>();

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;
}
triggerIndex = EditorGUI.Popup(position, "Trigger on", triggerIndex, triggers.Select(t => t.Name).ToArray());
triggerIdProperty.stringValue = triggers[triggerIndex].Id;
} else {
EditorGUI.LabelField(position, "No Triggers found.");
}
position.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;

// sound object picker
var soundProperty = property.FindPropertyRelative(nameof(MechSound.Sound));
EditorGUI.BeginChangeCheck();
var soundValue = EditorGUI.ObjectField(position, "Sound", soundProperty.objectReferenceValue, typeof(SoundAsset), true);
if (EditorGUI.EndChangeCheck()) {
soundProperty.objectReferenceValue = soundValue;
}
position.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;

// 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();
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.

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<AudioSource>();
if (audioSource != null) {
audioSource.playOnAwake = false;
}
}

public override void OnInspectorGUI()
{
var comp = target as MechSoundsComponent;

var soundEmitter = comp!.GetComponent<ISoundEmitter>();
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<AudioSource>();
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();
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// 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 <https://www.gnu.org/licenses/>.

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<AudioMixer>("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 void PlayStop()
{
if (_editorAudioSource.isPlaying) {
_soundAsset.Stop(_editorAudioSource);
} else {
_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));
}

/// <summary>
/// Gets or creates the editor GameObject for playing sounds in the editor.
///
/// The hierarchy looks like that:
///
/// [scene root]
/// |
/// -- EditorScene
/// |
/// -- EditorAudio (with AudioSource component)
/// </summary>
/// <returns>AudioSource of the editor GameObject for test playing audio.</returns>
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<AudioSource>();
if (!audioSource) {
audioSource = editorAudioGo.AddComponent<AudioSource>();
}

return audioSource;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading