using System; using System.Collections.Generic; using FMOD.Studio; using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif [CreateAssetMenu(menuName = "KernelPanic/Dialogue/Dialogue System")] public partial class DialogueSystem : ScriptableObject { [SerializeField] private DialogueSettings _settings; // https://stackoverflow.com/questions/2282476/actiont-vs-delegate-event public event EventHandler onDialogueLine; private readonly Dictionary _fmodKeyCache = new Dictionary(); // a list of dialogue keys that have already been spoken private readonly List _usedKeys = new List(); private void OnEnable() { _fmodKeyCache.Clear(); _usedKeys.Clear(); // cache all dialogue keys for FMOD at start to avoid allocations later foreach (var key in DialogueDatabase.Keys) { _fmodKeyCache[key] = $"{_settings.FMODPrefix}{key}"; } } // noRepeat locks this key off from further use. further attempts to use the key will be discarded public void PlayLine(string key, bool noRepeat = true) { if (noRepeat) { if (_usedKeys.Contains(key)) return; _usedKeys.Add(key); } // retrieve cached key var fmodKey = _fmodKeyCache[key]; EventDescription? eventDescription = null; try { eventDescription = FMODUnity.RuntimeManager.GetEventDescription(fmodKey); } catch (FMODUnity.EventNotFoundException e) { Debug.LogWarning($"no FMOD event {fmodKey}"); } DialogueLine dl; dl.text = DialogueDatabase.ReadDialogue(key); // default duration to show ui elements for dl.duration = _settings.HideAfter; // read audio data out of FMOD, check if event exists if (eventDescription.HasValue) { // assign values and play audio // get dialogue line duration from FMOD eventDescription.Value.getLength(out int ms); // get length gives us a value in milliseconds so it needs to be converted to seconds // before assignment dl.duration = ms / 1000f; // event is valid FMODUnity.RuntimeManager.PlayOneShot(fmodKey); } onDialogueLine?.Invoke(this, dl); } } public struct DialogueLine { public string text; public float duration; } #region Editor #if UNITY_EDITOR [CustomEditor(typeof(DialogueSystem))] public class DialogueSystemEditor : Editor { private DialogueSystem _dialogue; private string _key; private void OnEnable() { _dialogue = target as DialogueSystem; } public override void OnInspectorGUI() { base.OnInspectorGUI(); _key = EditorGUILayout.TextField("key", _key); if (GUILayout.Button("Play Line")) { _dialogue.PlayLine(_key); } } } #endif #endregion