diff --git a/Assets/Unheimlich_POC/Scenes/MainScene/MainScene.unity b/Assets/Unheimlich_POC/Scenes/MainScene/MainScene.unity index 5f9e85f7..21f6ed8b 100644 --- a/Assets/Unheimlich_POC/Scenes/MainScene/MainScene.unity +++ b/Assets/Unheimlich_POC/Scenes/MainScene/MainScene.unity @@ -2498,6 +2498,10 @@ PrefabInstance: propertyPath: _diapasonParticles value: objectReference: {fileID: 5090177222934034460} + - target: {fileID: 3142789394009655648, guid: 92d3cde0c88c75f48a98d082c573338b, type: 3} + propertyPath: _diapasonTapVolume + value: 0.3 + objectReference: {fileID: 0} - target: {fileID: 3142789394009655648, guid: 92d3cde0c88c75f48a98d082c573338b, type: 3} propertyPath: _handTouchFeedback value: @@ -2536,7 +2540,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 5383576672370031505, guid: 92d3cde0c88c75f48a98d082c573338b, type: 3} propertyPath: m_LocalPosition.z - value: 23.7 + value: 25.9 objectReference: {fileID: 0} - target: {fileID: 5383576672370031505, guid: 92d3cde0c88c75f48a98d082c573338b, type: 3} propertyPath: m_LocalRotation.w @@ -2649,8 +2653,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 7093688fd39800645bbe00b6d9a3f27b, type: 3} m_Name: m_EditorClassIdentifier: Assembly-CSharp::Unheimlich.Audio.DiapasonDebugger - _showDebugInfo: 1 - _showGizmos: 1 + _showDebugInfo: 0 + _showGizmos: 0 _listeningRangeColor: {r: 0, g: 1, b: 1, a: 1} _activeTargetColor: {r: 0, g: 1, b: 0, a: 1} --- !u!1660057539 &9223372036854775807 diff --git a/Assets/Unheimlich_POC/Scripts/Audio/AudioListenable.cs b/Assets/Unheimlich_POC/Scripts/Audio/AudioListenable.cs index 45e52d11..23721561 100644 --- a/Assets/Unheimlich_POC/Scripts/Audio/AudioListenable.cs +++ b/Assets/Unheimlich_POC/Scripts/Audio/AudioListenable.cs @@ -1,6 +1,7 @@ using System.Collections; using UnityEngine; using UnityEngine.InputSystem; +using Unheimlich.Utility; namespace Unheimlich.Audio { @@ -24,8 +25,8 @@ namespace Unheimlich.Audio private const string CONTINUOUS_AUDIO_SOURCE_NAME = "Continuous Audio Source"; // Volume multipliers for concentration system - private const float BASELINE_VOLUME_MULTIPLIER = 0.3f; - private const float FULL_VOLUME_MULTIPLIER = 1f; + private const float BASELINE_VOLUME_MULTIPLIER = 0.1f; + private const float FULL_VOLUME_MULTIPLIER = 1.5f; private const float CONCENTRATION_FADE_DURATION = 1f; private const float RELEASE_FADE_DURATION = 0.5f; @@ -78,7 +79,8 @@ namespace Unheimlich.Audio private AudioClip _continuousClip; private Coroutine _volumeFadeCoroutine; private Coroutine _startListeningCoroutine; - private Coroutine _concentrationFadeCoroutine; + private int _concentrationFadeTweenId = -1; + private int _crossfadeTweenId = -1; /// /// Returns true if this emitter has been tapped during the current listening session. @@ -147,18 +149,34 @@ namespace Unheimlich.Audio /// public void SetConcentrating(bool concentrating) { + TweenHelper.CancelTween(ref _concentrationFadeTweenId); - if (_concentrationFadeCoroutine != null) - StopCoroutine(_concentrationFadeCoroutine); + if (_continuousAudioSource == null) + return; - if (concentrating) - { - _concentrationFadeCoroutine = StartCoroutine(FadeConcentrationVolume(BASELINE_VOLUME_MULTIPLIER, FULL_VOLUME_MULTIPLIER, CONCENTRATION_FADE_DURATION)); - } - else - { - _concentrationFadeCoroutine = StartCoroutine(FadeConcentrationVolume(FULL_VOLUME_MULTIPLIER, BASELINE_VOLUME_MULTIPLIER, RELEASE_FADE_DURATION)); - } + float baseVolume = _continuousBaseVolume; + float fromMultiplier = concentrating ? BASELINE_VOLUME_MULTIPLIER : FULL_VOLUME_MULTIPLIER; + float toMultiplier = concentrating ? FULL_VOLUME_MULTIPLIER : BASELINE_VOLUME_MULTIPLIER; + float duration = concentrating ? CONCENTRATION_FADE_DURATION : RELEASE_FADE_DURATION; + + float fromVolume = baseVolume * fromMultiplier; + float toVolume = baseVolume * toMultiplier; + + _concentrationFadeTweenId = TweenHelper.FadeVolume( + _continuousAudioSource, + toVolume, + duration); + } + + public void ResetConcentrationVolume() + { + TweenHelper.CancelTween(ref _concentrationFadeTweenId); + + if (_continuousAudioSource == null) + return; + + float baselineVolume = _continuousBaseVolume * BASELINE_VOLUME_MULTIPLIER; + _continuousAudioSource.volume = baselineVolume; } /// @@ -197,8 +215,22 @@ namespace Unheimlich.Audio if (_volumeFadeCoroutine != null) StopCoroutine(_volumeFadeCoroutine); + TweenHelper.CancelTween(ref _crossfadeTweenId); + float crossfadeDuration = 1f; - _volumeFadeCoroutine = StartCoroutine(CrossfadeSounds(crossfadeDuration)); + + if (_continuousAudioSource != null) + { + _crossfadeTweenId = TweenHelper.FadeVolume( + _continuousAudioSource, + 0f, + crossfadeDuration, + () => + { + if (_continuousAudioSource != null) + _continuousAudioSource.Stop(); + }); + } if (_emitterParticles != null) { @@ -231,7 +263,7 @@ namespace Unheimlich.Audio /// This is the only place where _hasBeenTapped is reset to false, /// allowing the emitter to be used again in a future listening session. /// - public void StopListening() + public void StopListening(bool cancelConcentrationFade = true) { bool wasBeenTapped = _hasBeenTapped; @@ -244,8 +276,8 @@ namespace Unheimlich.Audio if (_startListeningCoroutine != null) StopCoroutine(_startListeningCoroutine); - if (_concentrationFadeCoroutine != null) - StopCoroutine(_concentrationFadeCoroutine); + if (cancelConcentrationFade) + TweenHelper.CancelTween(ref _concentrationFadeTweenId); if (_audioSource != null && _audioSource.isPlaying) _audioSource.Stop(); @@ -400,59 +432,6 @@ namespace Unheimlich.Audio Gamepad.current.SetMotorSpeeds(intensity, intensity); } - private IEnumerator FadeConcentrationVolume(float fromMultiplier, float toMultiplier, float duration) - { - float elapsed = 0f; - float baseVolume = _continuousBaseVolume; - - while (elapsed < duration) - { - elapsed += Time.deltaTime; - float t = elapsed / duration; - float currentMultiplier = Mathf.Lerp(fromMultiplier, toMultiplier, t); - - if (_continuousAudioSource != null) - { - _continuousAudioSource.volume = baseVolume * currentMultiplier; - } - - yield return null; - } - - if (_continuousAudioSource != null) - { - _continuousAudioSource.volume = baseVolume * toMultiplier; - } - } - - private IEnumerator CrossfadeSounds(float fadeDuration) - { - if (_continuousAudioSource == null) - yield break; - - float startVolume = _continuousAudioSource.volume; - float elapsed = 0f; - - while (elapsed < fadeDuration) - { - elapsed += Time.deltaTime; - float t = elapsed / fadeDuration; - - if (_continuousAudioSource != null) - { - _continuousAudioSource.volume = Mathf.Lerp(startVolume, 0f, t); - } - - yield return null; - } - - if (_continuousAudioSource != null) - { - _continuousAudioSource.volume = 0f; - _continuousAudioSource.Stop(); - } - } - private void UpdateVibration() { if (!_enableVibration || Gamepad.current == null) diff --git a/Assets/Unheimlich_POC/Scripts/Audio/DiapasonDebugger.cs b/Assets/Unheimlich_POC/Scripts/Audio/DiapasonDebugger.cs index 7832c2f3..bbea0318 100644 --- a/Assets/Unheimlich_POC/Scripts/Audio/DiapasonDebugger.cs +++ b/Assets/Unheimlich_POC/Scripts/Audio/DiapasonDebugger.cs @@ -50,6 +50,8 @@ namespace Unheimlich.Audio private void RefreshListenablesList() { _allListenables = FindObjectsByType(FindObjectsSortMode.None); + if (!_showDebugInfo) + return; Debug.Log($"Found {_allListenables.Length} AudioListenable objects in scene"); } diff --git a/Assets/Unheimlich_POC/Scripts/Core.meta b/Assets/Unheimlich_POC/Scripts/Core.meta new file mode 100644 index 00000000..b93f1288 --- /dev/null +++ b/Assets/Unheimlich_POC/Scripts/Core.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a469312c19fe2fb40baa7642b1e73dcd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Unheimlich_POC/Scripts/Core/Singleton.cs b/Assets/Unheimlich_POC/Scripts/Core/Singleton.cs new file mode 100644 index 00000000..00d01af1 --- /dev/null +++ b/Assets/Unheimlich_POC/Scripts/Core/Singleton.cs @@ -0,0 +1,33 @@ +using UnityEngine; + +namespace Unheimlich.Core +{ + public abstract class Singleton : MonoBehaviour where T : MonoBehaviour + { + public static T Instance { get; private set; } + + protected virtual void Awake() + { + if (Instance != null && Instance != this) + { + Debug.LogWarning($"[{typeof(T).Name}] Multiple instances detected! Destroying duplicate on GameObject '{gameObject.name}'."); + Destroy(this); + return; + } + Instance = this as T; + OnAwake(); + } + + protected virtual void OnDestroy() + { + if (Instance == this) + { + Instance = null; + OnDestroyInstance(); + } + } + + protected virtual void OnAwake() { } + protected virtual void OnDestroyInstance() { } + } +} diff --git a/Assets/Unheimlich_POC/Scripts/Core/Singleton.cs.meta b/Assets/Unheimlich_POC/Scripts/Core/Singleton.cs.meta new file mode 100644 index 00000000..dc09d0af --- /dev/null +++ b/Assets/Unheimlich_POC/Scripts/Core/Singleton.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 654e642c73c2b954fa86e0caac3f08c1 \ No newline at end of file diff --git a/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Environment/DoorInteractable.cs b/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Environment/DoorInteractable.cs index 4132e147..88b2379d 100644 --- a/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Environment/DoorInteractable.cs +++ b/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Environment/DoorInteractable.cs @@ -1,5 +1,6 @@ using UnityEngine; using Unheimlich.Interactions.Core; +using Unheimlich.Utility; namespace Unheimlich.Gameplay.Environment { @@ -29,21 +30,17 @@ namespace Unheimlich.Gameplay.Environment public void ToggleDoor() { - LeanTween.cancel(gameObject); + gameObject.CancelAllTweens(); if (!IsOpen) { - LeanTween.rotate(gameObject, _openRotation.eulerAngles, _animationDuration) - .setEase(_easeType); - + TweenHelper.RotateLocal(gameObject, _openRotation.eulerAngles, _animationDuration, null, _easeType); IsOpen = true; InteractionText = "Close"; } else { - LeanTween.rotate(gameObject, _closedRotation.eulerAngles, _animationDuration) - .setEase(_easeType); - + TweenHelper.RotateLocal(gameObject, _closedRotation.eulerAngles, _animationDuration, null, _easeType); IsOpen = false; InteractionText = "Open"; } diff --git a/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/DiapasonListeningHandler.cs b/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/DiapasonListeningHandler.cs index 15da7b21..33b56ba3 100644 --- a/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/DiapasonListeningHandler.cs +++ b/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/DiapasonListeningHandler.cs @@ -2,6 +2,8 @@ using System.Collections; using UnityEngine; using UnityEngine.InputSystem; using Unheimlich.Audio; +using Unheimlich.Core; +using Unheimlich.Utility; using InputManager = Unheimlich.Managers.PlayerInputManager; namespace Unheimlich.Gameplay.Player @@ -16,14 +18,11 @@ namespace Unheimlich.Gameplay.Player /// - Visual feedback (vignette, cursor, particles) /// - Preventing multiple taps on the same emitter /// - public class DiapasonListeningHandler : MonoBehaviour + public class DiapasonListeningHandler : Singleton { - // Animator parameter names for diapason animations private const string ANIM_TRIGGER_TAP = "Tap"; private const string ANIM_BOOL_VIBRATING = "IsVibrating"; - public static DiapasonListeningHandler Instance { get; private set; } - [Header("References")] [SerializeField] private GameObject _diapasonObject; [SerializeField] private Animator _diapasonAnimator; @@ -72,23 +71,18 @@ namespace Unheimlich.Gameplay.Player private AudioListenable _currentTargetEmitter; private AudioSource _playerAudioSource; private Coroutine _listeningCoroutine; - private Coroutine _tweenCoroutine; - private Coroutine _audioFadeCoroutine; + private int _diapasonTweenId = -1; + private int _audioListenerFadeTweenId = -1; + private int _playerAudioFadeTweenId = -1; private Vector2 _originalCameraRotation; private float _originalAmbientVolume; private Vector3 _diapasonEquippedPosition; private Vector3 _diapasonHiddenPosition; private bool _canUseDiapason; + private bool _isFadingOutConcentration; - private void Awake() + protected override void OnAwake() { - if (Instance != null && Instance != this) - { - Destroy(this); - return; - } - Instance = this; - _playerAudioSource = GetComponent(); if (_playerAudioSource == null) _playerAudioSource = gameObject.AddComponent(); @@ -217,6 +211,9 @@ namespace Unheimlich.Gameplay.Player /// private void StartListeningMode() { + if (_isFadingOutConcentration) + return; + if (_listeningCoroutine != null) StopCoroutine(_listeningCoroutine); @@ -240,17 +237,21 @@ namespace Unheimlich.Gameplay.Player if (_diapasonObject != null) { - if (_tweenCoroutine != null) - StopCoroutine(_tweenCoroutine); + TweenHelper.CancelTween(ref _diapasonTweenId); - _tweenCoroutine = StartCoroutine(TweenDiapasonPosition(_diapasonEquippedPosition, _diapasonTweenDuration, () => - { - if (_diapasonAnimator != null) + _diapasonTweenId = TweenHelper.MoveLocalWithCurve( + _diapasonObject, + _diapasonEquippedPosition, + _diapasonTweenDuration, + _diapasonTweenCurve, + () => { - _diapasonAnimator.enabled = true; - _diapasonAnimator.SetBool(ANIM_BOOL_VIBRATING, false); - } - })); + if (_diapasonAnimator != null) + { + _diapasonAnimator.enabled = true; + _diapasonAnimator.SetBool(ANIM_BOOL_VIBRATING, false); + } + }); } PlayBreathIn(); @@ -294,9 +295,21 @@ namespace Unheimlich.Gameplay.Player if (_playerAudioSource != null && _playerAudioSource.isPlaying) { - if (_audioFadeCoroutine != null) - StopCoroutine(_audioFadeCoroutine); - _audioFadeCoroutine = StartCoroutine(FadeOutAudio(_playerAudioSource, _audioFadeDuration)); + TweenHelper.CancelTween(ref _playerAudioFadeTweenId); + + float startVolume = _playerAudioSource.volume; + _playerAudioFadeTweenId = TweenHelper.FadeVolume( + _playerAudioSource, + 0f, + _audioFadeDuration, + () => + { + if (_playerAudioSource != null) + { + _playerAudioSource.Stop(); + _playerAudioSource.volume = startVolume; + } + }); } if (_diapasonObject != null) @@ -307,10 +320,13 @@ namespace Unheimlich.Gameplay.Player _diapasonAnimator.enabled = false; } - if (_tweenCoroutine != null) - StopCoroutine(_tweenCoroutine); + TweenHelper.CancelTween(ref _diapasonTweenId); - _tweenCoroutine = StartCoroutine(TweenDiapasonPosition(_diapasonHiddenPosition, _diapasonTweenDuration)); + _diapasonTweenId = TweenHelper.MoveLocalWithCurve( + _diapasonObject, + _diapasonHiddenPosition, + _diapasonTweenDuration, + _diapasonTweenCurve); } if (_diapasonParticles != null) @@ -331,13 +347,23 @@ namespace Unheimlich.Gameplay.Player if (_currentTargetEmitter != null) { - _currentTargetEmitter.StopListening(); + _currentTargetEmitter.SetConcentrating(false); + _currentTargetEmitter.StopListening(cancelConcentrationFade: false); _currentTargetEmitter = null; + + StartCoroutine(ResetFadingFlagAfterDelay()); } _canUseDiapason = false; } + private IEnumerator ResetFadingFlagAfterDelay() + { + _isFadingOutConcentration = true; + yield return new WaitForSeconds(0.5f); + _isFadingOutConcentration = false; + } + /// /// Updates cursor position and camera panning during listening mode. /// Supports both gamepad (look input) and mouse (screen edges). @@ -492,55 +518,20 @@ namespace Unheimlich.Gameplay.Player #region If needed private void AttenuateAmbientSound(bool enable) { - if (_audioFadeCoroutine != null) - StopCoroutine(_audioFadeCoroutine); + TweenHelper.CancelTween(ref _audioListenerFadeTweenId); - _audioFadeCoroutine = StartCoroutine(FadeAmbientSound(enable)); - } - - private IEnumerator FadeAmbientSound(bool fadeOut) - { - //To do : Create audio tracks AudioListener listener = FindFirstObjectByType(); if (listener == null) - yield break; + return; - float startVolume = AudioListener.volume; - float targetVolume = fadeOut ? _ambientAttenuationAmount : _originalAmbientVolume; + float targetVolume = enable ? _ambientAttenuationAmount : _originalAmbientVolume; - if (!fadeOut) - _originalAmbientVolume = startVolume; - else + if (!enable) _originalAmbientVolume = AudioListener.volume; - float elapsed = 0f; - while (elapsed < _audioFadeDuration) - { - elapsed += Time.deltaTime; - float t = elapsed / _audioFadeDuration; - AudioListener.volume = Mathf.Lerp(startVolume, targetVolume, t); - yield return null; - } - - AudioListener.volume = targetVolume; + _audioListenerFadeTweenId = TweenHelper.FadeVolume(gameObject, targetVolume, _audioFadeDuration); } #endregion - private IEnumerator FadeOutAudio(AudioSource source, float duration) - { - float startVolume = source.volume; - float elapsed = 0f; - - while (elapsed < duration) - { - elapsed += Time.deltaTime; - float t = elapsed / duration; - source.volume = Mathf.Lerp(startVolume, 0f, t); - yield return null; - } - - source.Stop(); - source.volume = startVolume; - } private void EnableVignette(bool enable) { @@ -551,23 +542,6 @@ namespace Unheimlich.Gameplay.Player } } - private IEnumerator TweenDiapasonPosition(Vector3 targetPosition, float duration, System.Action onComplete = null) - { - Vector3 startPosition = _diapasonObject.transform.localPosition; - float elapsed = 0f; - - while (elapsed < duration) - { - elapsed += Time.deltaTime; - float t = _diapasonTweenCurve.Evaluate(elapsed / duration); - _diapasonObject.transform.localPosition = Vector3.Lerp(startPosition, targetPosition, t); - yield return null; - } - - _diapasonObject.transform.localPosition = targetPosition; - onComplete?.Invoke(); - } - private void OnDrawGizmosSelected() { if (_playerCamera != null && IsListening) diff --git a/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/Player.cs b/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/Player.cs index 454b7603..19e9904b 100644 --- a/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/Player.cs +++ b/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/Player.cs @@ -1,22 +1,9 @@ using UnityEngine; +using Unheimlich.Core; namespace Unheimlich.Gameplay.Player { - /// - /// Singleton player reference accessible globally throughout the game. - /// - public class Player : MonoBehaviour + public class Player : Singleton { - public static Player Instance { get; private set; } - - private void Awake() - { - if (Instance != null && Instance != this) - { - Destroy(this); - return; - } - Instance = this; - } } } diff --git a/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/PlayerLook.cs b/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/PlayerLook.cs index 3059821b..3ffc187a 100644 --- a/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/PlayerLook.cs +++ b/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/PlayerLook.cs @@ -1,5 +1,6 @@ using UnityEngine; using UnityEngine.InputSystem; +using Unheimlich.Core; using Unheimlich.Gameplay.Portals; using InputManager = Unheimlich.Managers.PlayerInputManager; @@ -10,12 +11,10 @@ namespace Unheimlich.Gameplay.Player /// Supports both mouse and gamepad input with adaptive sensitivity. /// Includes aim assist for dialogue choices and camera shake effects. /// - public class PlayerLook : MonoBehaviour + public class PlayerLook : Singleton { private const float INPUT_DEADZONE = 0.1f; private const float MOUSE_DELTA_MULTIPLIER = 0.001f; - - public static PlayerLook Instance { get; private set; } [Header("Components")] [SerializeField] private Transform _playerCamera, _playerCameraPivot; @@ -46,15 +45,8 @@ namespace Unheimlich.Gameplay.Player private float _verticalRotation; private bool _isUsingGamepad = false; - private void Awake() + protected override void OnAwake() { - if (Instance != null && Instance != this) - { - Destroy(this); - return; - } - Instance = this; - QualitySettings.vSyncCount = 0; Application.targetFrameRate = _frameRate; diff --git a/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/PlayerMovement.cs b/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/PlayerMovement.cs index 4859c3fa..9d01b3ba 100644 --- a/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/PlayerMovement.cs +++ b/Assets/Unheimlich_POC/Scripts/Gameplay/GameObjects/Player/PlayerMovement.cs @@ -1,16 +1,12 @@ using UnityEngine; +using Unheimlich.Core; using Unheimlich.Managers; namespace Unheimlich.Gameplay.Player { - /// - /// Handles player movement, jumping, and gravity using CharacterController. - /// Provides movement state information for other systems like camera head bob. - /// [RequireComponent(typeof(CharacterController))] - public class PlayerMovement : MonoBehaviour + public class PlayerMovement : Singleton { - public static PlayerMovement Instance { get; private set; } [Header("Settings")] [SerializeField] private float _moveSpeed = 5f; @@ -24,15 +20,8 @@ namespace Unheimlich.Gameplay.Player private float _ySpeed; private Vector3 _moveDirection; - private void Awake() + protected override void OnAwake() { - if (Instance != null && Instance != this) - { - Destroy(this); - return; - } - Instance = this; - CharacterController = GetComponent(); } diff --git a/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/ChoiceFocusHandler.cs b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/ChoiceFocusHandler.cs index 2261f2f0..1913693f 100644 --- a/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/ChoiceFocusHandler.cs +++ b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/ChoiceFocusHandler.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Unheimlich.Data.Dialogues; using Unheimlich.Managers; using Unheimlich.Gameplay.Player; +using Unheimlich.Utility; namespace Unheimlich.Interactions.FloatingWords { @@ -219,12 +220,15 @@ namespace Unheimlich.Interactions.FloatingWords if (_choiceTextComponent == null) return; - if (_textScaleTweenId >= 0) - LeanTween.cancel(_textScaleTweenId); + TweenHelper.CancelTween(ref _textScaleTweenId); - _textScaleTweenId = LeanTween.scale(_choiceTextComponent.gameObject, Vector3.one * targetScale, duration) - .setEase(LeanTweenType.easeOutQuad) - .id; + _textScaleTweenId = TweenHelper.ScaleLocal( + _choiceTextComponent.gameObject, + Vector3.one * targetScale, + duration, + null, + LeanTweenType.easeOutQuad + ); } public void ResetTextScale(float duration = 0.3f) @@ -510,23 +514,20 @@ namespace Unheimlich.Interactions.FloatingWords { LeanTween.cancel(gameObject); - if (_textScaleTweenId >= 0) - LeanTween.cancel(_textScaleTweenId); + TweenHelper.CancelTween(ref _textScaleTweenId); StopOrganicShake(); ResetSketchFade(); - - LeanTween.moveLocal(gameObject, _startLocalPos, duration) - .setEase(LeanTweenType.easeOutQuad) - .setOnComplete(() => { - _positionCaptured = false; + + TweenHelper.MoveLocal(gameObject, _startLocalPos, duration, () => { + _positionCaptured = false; + + if (DialogueManager.Instance != null) + DialogueManager.Instance.UnlockFocus(); - if (DialogueManager.Instance != null) - DialogueManager.Instance.UnlockFocus(); - - StartOrganicShake(); - }); + StartOrganicShake(); + }, LeanTweenType.easeOutQuad); transform.localScale = Vector3.one * _originalScale; diff --git a/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/FloatingWordsSpawner.cs b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/FloatingWordsSpawner.cs index 5a400c43..da79f853 100644 --- a/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/FloatingWordsSpawner.cs +++ b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/FloatingWordsSpawner.cs @@ -2,14 +2,10 @@ using UnityEngine; using System.Collections.Generic; using Unheimlich.Data.Dialogues; using Unheimlich.Gameplay.Player; +using Unheimlich.Utility; namespace Unheimlich.Interactions.FloatingWords { - /// - /// Utility class for spawning floating 3D text choices in world space. - /// Used for both dialogue choices and object descriptions. Handles positioning, - /// rotation, animations (spawn, merge, floating) using LeanTween for smooth effects. - /// public static class FloatingWordsSpawner { private const float ROTATOR_SCALE = 0.01f; @@ -249,26 +245,32 @@ namespace Unheimlich.Interactions.FloatingWords CanvasGroup canvasGroup = words[i].GetComponent(); if (canvasGroup != null) { - LeanTween.value(words[i].gameObject, 1f, 0f, config.MergeDuration) - .setOnUpdate((float val) => { canvasGroup.alpha = val; }) - .setEaseInQuad(); + TweenHelper.FadeCanvasGroup(canvasGroup, 0f, config.MergeDuration, null, LeanTweenType.easeInQuad); } } } if (words.Count > 0 && words[0] != null) { - LeanTween.scale(words[0].gameObject, Vector3.one * config.MergeScalePulse, config.MergeDuration / 2f) - .setEaseOutBack() - .setOnComplete(() => + TweenHelper.ScaleLocal( + words[0].gameObject, + Vector3.one * config.MergeScalePulse, + config.MergeDuration / 2f, + () => { if (words.Count > 0 && words[0] != null) { - LeanTween.scale(words[0].gameObject, Vector3.one, config.MergeDuration / 2f) - .setEaseInBack() - .setOnComplete(() => onMergeComplete?.Invoke()); + TweenHelper.ScaleLocal( + words[0].gameObject, + Vector3.one, + config.MergeDuration / 2f, + () => onMergeComplete.SafeInvoke(), + LeanTweenType.easeInBack + ); } - }); + }, + LeanTweenType.easeOutBack + ); } } @@ -292,13 +294,11 @@ namespace Unheimlich.Interactions.FloatingWords { if (words[i] != null) { - LeanTween.cancel(words[i].gameObject); + words[i].gameObject.CancelAllTweens(); CanvasGroup canvasGroup = words[i].GetComponent(); if (canvasGroup != null) { - LeanTween.value(words[i].gameObject, canvasGroup.alpha, 1f, config.MergeDuration) - .setOnUpdate((float val) => { canvasGroup.alpha = val; }) - .setEaseOutQuad(); + TweenHelper.FadeCanvasGroup(canvasGroup, 1f, config.MergeDuration, null, LeanTweenType.easeOutQuad); } words[i].transform.localScale = Vector3.one; } @@ -306,7 +306,7 @@ namespace Unheimlich.Interactions.FloatingWords if (words.Count > 0 && words[0] != null) { - LeanTween.cancel(words[0].gameObject); + words[0].gameObject.CancelAllTweens(); words[0].transform.localScale = Vector3.one; } } diff --git a/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/SketchFadeEffect.cs b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/SketchFadeEffect.cs index 6ef33419..096c6464 100644 --- a/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/SketchFadeEffect.cs +++ b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/FloatingWords/SketchFadeEffect.cs @@ -1,6 +1,7 @@ using UnityEngine; using UnityEngine.UI; using TMPro; +using Unheimlich.Utility; namespace Unheimlich.Interactions.FloatingWords { @@ -93,42 +94,41 @@ namespace Unheimlich.Interactions.FloatingWords _isEffectActive = true; - _scribbleTweenId = LeanTween.value(gameObject, 0f, 1f, scribbleDuration) - .setEase(LeanTweenType.easeInOutQuad) - .setOnUpdate((float val) => { - if (_scribbleMaterialInstance != null) - _scribbleMaterialInstance.SetFloat(ProgressID, val); - }) - .setOnComplete(() => { + _scribbleTweenId = TweenHelper.AnimateMaterialFloat( + gameObject, + _scribbleMaterialInstance, + ProgressID, + 0f, + 1f, + scribbleDuration, + () => { _scribbleTweenId = -1; + + float ghostAlphaRatio = _ghostTextComponent != null ? (_originalGhostColor.a / _originalTextColor.a) : 0f; + _fadeTweenId = LeanTween.value(gameObject, _originalTextColor.a, finalAlpha, fadeDuration) .setEase(LeanTweenType.easeInQuad) .setOnUpdate((float val) => { if (_textComponent != null) { - Color textColor = _textComponent.color; - textColor.a = val; - _textComponent.color = textColor; + _textComponent.color = _textComponent.color.WithAlpha(val); } if (_ghostTextComponent != null) { - Color ghostColor = _ghostTextComponent.color; - ghostColor.a = val * (_originalGhostColor.a / _originalTextColor.a); - _ghostTextComponent.color = ghostColor; + _ghostTextComponent.color = _ghostTextComponent.color.WithAlpha(val * ghostAlphaRatio); } if (_scribbleOverlay != null) { - Color scribbleColor = _scribbleOverlay.color; - scribbleColor.a = val; - _scribbleOverlay.color = scribbleColor; + _scribbleOverlay.color = _scribbleOverlay.color.WithAlpha(val); } }) .setOnComplete(() => { _fadeTweenId = -1; }).id; - }).id; + }, + LeanTweenType.easeInOutQuad); } public void ResetEffect() @@ -141,17 +141,19 @@ namespace Unheimlich.Interactions.FloatingWords if (currentProgress > 0f) { - _scribbleTweenId = LeanTween.value(gameObject, currentProgress, 0f, 0.3f) - .setEase(LeanTweenType.easeOutQuad) - .setOnUpdate((float val) => { - if (_scribbleMaterialInstance != null) - _scribbleMaterialInstance.SetFloat(ProgressID, val); - }) - .setOnComplete(() => { + _scribbleTweenId = TweenHelper.AnimateMaterialFloat( + gameObject, + _scribbleMaterialInstance, + ProgressID, + currentProgress, + 0f, + 0.3f, + () => { _scribbleTweenId = -1; if (_scribbleOverlay != null) _scribbleOverlay.gameObject.SetActive(false); - }).id; + }, + LeanTweenType.easeOutQuad); } } @@ -161,34 +163,23 @@ namespace Unheimlich.Interactions.FloatingWords if (currentAlpha < _originalTextColor.a) { - _fadeTweenId = LeanTween.value(gameObject, currentAlpha, _originalTextColor.a, 0.3f) - .setEase(LeanTweenType.easeOutQuad) - .setOnUpdate((float val) => { - if (_textComponent != null) - { - Color c = _textComponent.color; - c.a = val; - _textComponent.color = c; - } - - if (_ghostTextComponent != null) - { - Color c = _ghostTextComponent.color; - c.a = val * (_originalGhostColor.a / _originalTextColor.a); - _ghostTextComponent.color = c; - } - }) - .setOnComplete(() => { - _fadeTweenId = -1; - }).id; + float ghostAlphaRatio = _ghostTextComponent != null ? (_originalGhostColor.a / _originalTextColor.a) : 0f; + + _fadeTweenId = TweenHelper.FadeText( + gameObject, + _textComponent, + _ghostTextComponent, + _originalTextColor.a, + 0.3f, + ghostAlphaRatio, + () => { _fadeTweenId = -1; }, + LeanTweenType.easeOutQuad); } } if (_scribbleOverlay != null) { - Color c = _scribbleOverlay.color; - c.a = 1f; - _scribbleOverlay.color = c; + _scribbleOverlay.color = _scribbleOverlay.color.WithAlpha(1f); } _isEffectActive = false; @@ -196,17 +187,8 @@ namespace Unheimlich.Interactions.FloatingWords private void CancelActiveTweens() { - if (_scribbleTweenId != -1) - { - LeanTween.cancel(_scribbleTweenId); - _scribbleTweenId = -1; - } - - if (_fadeTweenId != -1) - { - LeanTween.cancel(_fadeTweenId); - _fadeTweenId = -1; - } + TweenHelper.CancelTween(ref _scribbleTweenId); + TweenHelper.CancelTween(ref _fadeTweenId); } private void OnDestroy() diff --git a/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/InteracionHandler.cs b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/InteracionHandler.cs index 7520e028..b7d12c92 100644 --- a/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/InteracionHandler.cs +++ b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/InteracionHandler.cs @@ -1,15 +1,12 @@ using UnityEngine; +using Unheimlich.Core; using Unheimlich.Managers; +using Unheimlich.Utility; namespace Unheimlich.Interactions.Core { - /// - /// Detects interactable objects in front of the player camera using raycasts. - /// Triggers interaction when player presses interact button while looking at an interactable. - /// - public class InteractionHandler : MonoBehaviour -{ - public static InteractionHandler Instance { get; private set; } + public class InteractionHandler : Singleton + { [Header("Settings")] [SerializeField] private float _interactionRange = 1f; @@ -19,50 +16,32 @@ namespace Unheimlich.Interactions.Core private InteractableBase _currentInteractable; private Camera _playerCamera; private float _raycastTimer; - - private static readonly Vector3 ViewportCenter = new Vector3(0.5f, 0.5f, 0f); - private void Awake() + protected override void OnAwake() { - if (Instance != null && Instance != this) - { - Destroy(this); - return; - } - Instance = this; _playerCamera = GetComponent(); } private void Update() { - _raycastTimer += Time.deltaTime; - - if (_raycastTimer >= _raycastInterval) + if (RaycastHelper.ThrottledRaycastComponent( + ref _raycastTimer, + _raycastInterval, + _playerCamera, + _interactionRange, + _interactableLayers, + out InteractableBase interactable)) { - CheckForInteractable(); - _raycastTimer = 0f; + _currentInteractable = interactable; + } + else if (_raycastTimer == 0f) + { + _currentInteractable = null; } HandleInteractionInput(); } - /// - /// Performs raycast from camera center to detect interactable objects within range. - /// Updates the current interactable reference for input handling. - /// - private void CheckForInteractable() - { - Ray ray = _playerCamera.ViewportPointToRay(ViewportCenter); - - if (Physics.Raycast(ray, out RaycastHit hit, _interactionRange, _interactableLayers)) - { - InteractableBase hitInteractable = hit.collider.GetComponent(); - _currentInteractable = hitInteractable; - } - else - _currentInteractable = null; - } - private void HandleInteractionInput() { if (PlayerInputManager.Instance != null && @@ -79,7 +58,8 @@ namespace Unheimlich.Interactions.Core { Gizmos.color = _currentInteractable != null ? Color.green : Color.red; - Ray ray = _playerCamera.ViewportPointToRay(ViewportCenter); + Vector3 viewportCenter = new Vector3(0.5f, 0.5f, 0f); + Ray ray = _playerCamera.ViewportPointToRay(viewportCenter); Gizmos.DrawRay(ray.origin, ray.direction * _interactionRange); } } diff --git a/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/PlayerInspectionHandler.cs b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/PlayerInspectionHandler.cs index efe60152..1c93724e 100644 --- a/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/PlayerInspectionHandler.cs +++ b/Assets/Unheimlich_POC/Scripts/Handlers/Interactions/PlayerInspectionHandler.cs @@ -1,14 +1,18 @@ using System.Collections; using UnityEngine; using UnityEngine.InputSystem; +using Unheimlich.Core; using Unheimlich.Gameplay.Items; using InputManager = Unheimlich.Managers.PlayerInputManager; namespace Unheimlich.Interactions.Core { - public class PlayerInspectionHandler : MonoBehaviour + /// + /// Handles player item inspection mode. + /// When inspecting, disables player movement and allows item rotation using look input. + /// + public class PlayerInspectionHandler : Singleton { - public static PlayerInspectionHandler Instance { get; private set; } [Header("Components")] [SerializeField] private Transform _inspectionHoldPoint; @@ -19,21 +23,7 @@ namespace Unheimlich.Interactions.Core private Coroutine _interactionListener; private bool _isUsingGamepad = false; - - private void Awake() - { - if (Instance != null && Instance != this) - { - Destroy(this); - return; - } - Instance = this; - } - /// - /// Toggles inspection mode for an item. When inspecting, disables player movement - /// and allows item rotation using look input until interaction key is pressed again. - /// public void ToggleInspection(InspectableItem item) { if (!IsInspecting) diff --git a/Assets/Unheimlich_POC/Scripts/Managers/DialogueFocusSystem.cs b/Assets/Unheimlich_POC/Scripts/Managers/DialogueFocusSystem.cs index 2ad25339..f43bb8c9 100644 --- a/Assets/Unheimlich_POC/Scripts/Managers/DialogueFocusSystem.cs +++ b/Assets/Unheimlich_POC/Scripts/Managers/DialogueFocusSystem.cs @@ -4,6 +4,7 @@ using Unheimlich.Gameplay.Player; using Unheimlich.Interactions.Dialogues; using Unheimlich.Interactions.FloatingWords; using Unheimlich.Data.Dialogues; +using Unheimlich.Utility; namespace Unheimlich.Managers { @@ -32,8 +33,6 @@ namespace Unheimlich.Managers private readonly float _focusRetentionTime; private readonly float _fallbackShakeIntensity; - private static readonly Vector3 ViewportCenter = new Vector3(0.5f, 0.5f, 0f); - public event Action OnChoiceMade; public DialogueFocusSystem( @@ -96,16 +95,13 @@ namespace Unheimlich.Managers if (_mainCamera == null) return; - Ray ray = _mainCamera.ViewportPointToRay(ViewportCenter); - - ChoiceFocusHandler hitHandler = null; - bool raycastHitChoice = Physics.Raycast(ray, out RaycastHit hit, _focusRange, _choiceLayer); - - if (raycastHitChoice) + ChoiceFocusHandler hitHandler = null; + + if (RaycastHelper.RaycastFromCameraCenter(_mainCamera, _focusRange, _choiceLayer, out RaycastHit hit)) { hitHandler = hit.collider.GetComponent(); - if (_playerLook != null) + if (_playerLook != null && hitHandler != null) _playerLook.ApplyAimAssist(hit.collider.bounds.center); } diff --git a/Assets/Unheimlich_POC/Scripts/Managers/DialogueManager.cs b/Assets/Unheimlich_POC/Scripts/Managers/DialogueManager.cs index d4a9cd07..d66fb90e 100644 --- a/Assets/Unheimlich_POC/Scripts/Managers/DialogueManager.cs +++ b/Assets/Unheimlich_POC/Scripts/Managers/DialogueManager.cs @@ -1,5 +1,6 @@ using UnityEngine; using System; +using Unheimlich.Core; using Unheimlich.Data.Dialogues; using Unheimlich.Gameplay.Player; using Unheimlich.UI; @@ -8,9 +9,8 @@ using System.Collections.Generic; namespace Unheimlich.Managers { - public class DialogueManager : MonoBehaviour -{ - public static DialogueManager Instance { get; private set; } + public class DialogueManager : Singleton + { [Header("State")] public bool IsInDialogue = false; @@ -51,14 +51,8 @@ namespace Unheimlich.Managers private float _autoCloseTimer = 0f; private bool _isWaitingForAutoClose = false; - private void Awake() + protected override void OnAwake() { - if (Instance != null && Instance != this) - { - Destroy(this); - return; - } - Instance = this; } private void Start() diff --git a/Assets/Unheimlich_POC/Scripts/Managers/PlayerInputManager.cs b/Assets/Unheimlich_POC/Scripts/Managers/PlayerInputManager.cs index 46d6e722..61770f5d 100644 --- a/Assets/Unheimlich_POC/Scripts/Managers/PlayerInputManager.cs +++ b/Assets/Unheimlich_POC/Scripts/Managers/PlayerInputManager.cs @@ -1,5 +1,6 @@ using UnityEngine; using UnityEngine.InputSystem; +using Unheimlich.Core; using Unheimlich.Gameplay.Player; namespace Unheimlich.Managers @@ -10,7 +11,7 @@ namespace Unheimlich.Managers /// Supports different input contexts (Normal, Dialogue, Listening) to control which inputs are active. /// [RequireComponent(typeof(PlayerInput))] - public class PlayerInputManager : MonoBehaviour + public class PlayerInputManager : Singleton { /// /// Defines different input contexts that control which player actions are enabled. @@ -22,8 +23,6 @@ namespace Unheimlich.Managers Listening // Only look enabled, movement disabled (for diapason camera pan) } - public static PlayerInputManager Instance { get; private set; } - [Header("Input Values (Read only)")] public Vector2 MoveInput; public Vector2 LookInput; @@ -44,15 +43,8 @@ namespace Unheimlich.Managers private PlayerLook _playerLook; private CameraHeadBob _headBob; - private void Awake() + protected override void OnAwake() { - if (Instance != null && Instance != this) - { - Destroy(this); - return; - } - Instance = this; - IsInputEnabled = true; PlayerInput playerInput = GetComponent(); diff --git a/Assets/Unheimlich_POC/Scripts/Managers/PortalRoomManager.cs b/Assets/Unheimlich_POC/Scripts/Managers/PortalRoomManager.cs index 98afd473..988e5e16 100644 --- a/Assets/Unheimlich_POC/Scripts/Managers/PortalRoomManager.cs +++ b/Assets/Unheimlich_POC/Scripts/Managers/PortalRoomManager.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using UnityEngine; +using Unheimlich.Core; using Unheimlich.Gameplay.Portals; namespace Unheimlich.Managers @@ -9,9 +10,8 @@ namespace Unheimlich.Managers /// Monitors player distance to portals and loads/unloads room instances accordingly. /// Supports room pooling (keeping inactive rooms in memory) and distance-based activation. /// - public class PortalRoomManager : MonoBehaviour + public class PortalRoomManager : Singleton { - public static PortalRoomManager Instance { get; private set; } [Header("Settings")] [SerializeField] private float _loadDistance = 15f; @@ -44,16 +44,6 @@ namespace Unheimlich.Managers public bool BoundsCalculated = false; } - private void Awake() - { - if (Instance != null && Instance != this) - { - Destroy(this); - return; - } - Instance = this; - } - private void Start() { _player = GameObject.FindGameObjectWithTag("Player")?.transform; diff --git a/Assets/Unheimlich_POC/Scripts/UI/DialogueBlurEffectURP.cs b/Assets/Unheimlich_POC/Scripts/UI/DialogueBlurEffectURP.cs index c6c9c617..699f9234 100644 --- a/Assets/Unheimlich_POC/Scripts/UI/DialogueBlurEffectURP.cs +++ b/Assets/Unheimlich_POC/Scripts/UI/DialogueBlurEffectURP.cs @@ -1,13 +1,13 @@ using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; +using Unheimlich.Core; namespace Unheimlich.UI { [RequireComponent(typeof(Camera))] - public class DialogueBlurEffectURP : MonoBehaviour + public class DialogueBlurEffectURP : Singleton { - public static DialogueBlurEffectURP Instance { get; private set; } [Header("Volume Settings")] [SerializeField] private Volume _globalVolume; @@ -43,15 +43,8 @@ namespace Unheimlich.UI private Camera _baseCam; private Camera _dialogueCam; - void Awake() + protected override void OnAwake() { - if (Instance != null && Instance != this) - { - Destroy(this); - return; - } - Instance = this; - _baseCam = GetComponent(); if (_fadeCurve == null || _fadeCurve.keys.Length == 0) diff --git a/Assets/Unheimlich_POC/Scripts/UI/DialogueScreenUI.cs b/Assets/Unheimlich_POC/Scripts/UI/DialogueScreenUI.cs index c8178ae9..742a36bd 100644 --- a/Assets/Unheimlich_POC/Scripts/UI/DialogueScreenUI.cs +++ b/Assets/Unheimlich_POC/Scripts/UI/DialogueScreenUI.cs @@ -1,11 +1,12 @@ using UnityEngine; using TMPro; +using Unheimlich.Core; +using Unheimlich.Utility; namespace Unheimlich.UI { - public class DialogueScreenUI : MonoBehaviour + public class DialogueScreenUI : Singleton { - public static DialogueScreenUI Instance { get; private set; } [Header("UI References")] [SerializeField] private GameObject _dialoguePanel; @@ -17,16 +18,8 @@ namespace Unheimlich.UI private CanvasGroup _canvasGroup; - private void Awake() + protected override void OnAwake() { - if (Instance != null && Instance != this) - { - Debug.LogWarning("[DialogueScreenUI] Multiple instances detected! Destroying duplicate."); - Destroy(gameObject); - return; - } - - Instance = this; if (_dialoguePanel != null) { @@ -37,12 +30,6 @@ namespace Unheimlich.UI } } - private void OnDestroy() - { - if (Instance == this) - Instance = null; - } - public void Show(string speaker, string dialogue) { if (_speakerText != null) @@ -73,18 +60,19 @@ namespace Unheimlich.UI { if (_canvasGroup != null && _dialoguePanel != null) { - LeanTween.cancel(_dialoguePanel); - LeanTween.alphaCanvas(_canvasGroup, 0f, _fadeOutDuration) - .setOnComplete(() => - { + TweenHelper.FadeCanvasGroup( + _canvasGroup, + 0f, + _fadeOutDuration, + () => { Hide(); - onComplete?.Invoke(); + onComplete.SafeInvoke(); }); } else { Hide(); - onComplete?.Invoke(); + onComplete.SafeInvoke(); } } } diff --git a/Assets/Unheimlich_POC/Scripts/UI/DialogueUI.cs b/Assets/Unheimlich_POC/Scripts/UI/DialogueUI.cs index 4dfd69b9..b5ea013f 100644 --- a/Assets/Unheimlich_POC/Scripts/UI/DialogueUI.cs +++ b/Assets/Unheimlich_POC/Scripts/UI/DialogueUI.cs @@ -4,6 +4,7 @@ using TMPro; using Unheimlich.Data.Dialogues; using Unheimlich.Gameplay.Player; using Unheimlich.Interactions.FloatingWords; +using Unheimlich.Utility; namespace Unheimlich.UI { @@ -43,6 +44,10 @@ namespace Unheimlich.UI private DialogueDisplayMode _currentDisplayMode = DialogueDisplayMode.World; + private int _fadeCanvasTweenId = -1; + private int _rotationTweenId = -1; + private List _floatingTweenIds = new List(); + private static readonly Vector3 ViewportCenter = new Vector3(0.5f, 0.5f, 0f); private void Awake() @@ -91,7 +96,7 @@ namespace Unheimlich.UI if (_dialogueCanvasGroup != null) { - LeanTween.cancel(_dialoguePanel); + TweenHelper.CancelTween(ref _fadeCanvasTweenId); _dialogueCanvasGroup.alpha = 1f; } } @@ -125,9 +130,12 @@ namespace Unheimlich.UI { if (_dialogueCanvasGroup != null && _dialoguePanel != null) { - LeanTween.cancel(_dialoguePanel); - LeanTween.alphaCanvas(_dialogueCanvasGroup, 0f, _fadeOutDuration) - .setOnComplete(() => + TweenHelper.CancelTween(ref _fadeCanvasTweenId); + _fadeCanvasTweenId = TweenHelper.FadeCanvasGroup( + _dialogueCanvasGroup, + 0f, + _fadeOutDuration, + () => { HideDialogueBox(); onComplete?.Invoke(); @@ -247,35 +255,18 @@ namespace Unheimlich.UI float amplitude = _currentAnimationSettings.FloatingAmplitude; float duration = _currentAnimationSettings.FloatingDuration; - float offset = index * 0.2f; + float delay = index * 0.2f; Vector3 startPos = choice.transform.localPosition; - LeanTween.value(choice, 0f, Mathf.PI * 2f, duration) - .setDelay(offset) - .setOnUpdate((float val) => { - if (choice != null) - { - Vector3 newPos = startPos; - newPos.y += Mathf.Sin(val) * amplitude; - choice.transform.localPosition = newPos; - } - }) - .setLoopClamp() - .setEase(LeanTweenType.linear); + int tweenId = TweenHelper.FloatAnimation(choice, startPos, amplitude, duration, delay, Axis.Y); + _floatingTweenIds.Add(tweenId); } public void RotationAnimation(float degreesPerSecond = 60f) { - if (degreesPerSecond <= 0f) - return; - - float duration = 360f / degreesPerSecond; - - LeanTween.cancel(ChoiceContainer); - LeanTween.rotateAroundLocal(ChoiceContainer, _currentRotationAxis, 360f, duration) - .setLoopClamp() - .setEase(LeanTweenType.linear); + TweenHelper.CancelTween(ref _rotationTweenId); + _rotationTweenId = TweenHelper.RotateAround(ChoiceContainer, _currentRotationAxis, degreesPerSecond); } /// @@ -318,10 +309,17 @@ namespace Unheimlich.UI _currentAnchor = null; _currentAnimationSettings = null; _activeChoices.Clear(); - LeanTween.cancel(ChoiceContainer); + + TweenHelper.CancelTween(ref _rotationTweenId); + foreach (int tweenId in _floatingTweenIds) + { + int id = tweenId; + TweenHelper.CancelTween(ref id); + } + _floatingTweenIds.Clear(); + foreach (Transform child in ChoiceContainer.transform) { - LeanTween.cancel(child.gameObject); Destroy(child.gameObject); } } diff --git a/Assets/Unheimlich_POC/Scripts/Utility/RaycastHelper.cs b/Assets/Unheimlich_POC/Scripts/Utility/RaycastHelper.cs new file mode 100644 index 00000000..593ce407 --- /dev/null +++ b/Assets/Unheimlich_POC/Scripts/Utility/RaycastHelper.cs @@ -0,0 +1,66 @@ +using UnityEngine; + +namespace Unheimlich.Utility +{ + public static class RaycastHelper + { + private static readonly Vector3 ViewportCenter = new Vector3(0.5f, 0.5f, 0f); + + public static bool RaycastFromCameraCenter(Camera camera, float maxDistance, + LayerMask layerMask, out RaycastHit hit) + { + if (camera == null) + { + hit = default; + return false; + } + + Ray ray = camera.ViewportPointToRay(ViewportCenter); + return Physics.Raycast(ray, out hit, maxDistance, layerMask); + } + + public static bool RaycastFromCameraCenter(Camera camera, float maxDistance, + LayerMask layerMask, out T component) where T : Component + { + component = null; + + if (RaycastFromCameraCenter(camera, maxDistance, layerMask, out RaycastHit hit)) + { + component = hit.collider.GetComponent(); + return component != null; + } + + return false; + } + + public static bool ThrottledRaycast(ref float timer, float interval, + Camera camera, float maxDistance, LayerMask layerMask, out RaycastHit hit) + { + timer += Time.deltaTime; + + if (timer >= interval) + { + timer = 0f; + return RaycastFromCameraCenter(camera, maxDistance, layerMask, out hit); + } + + hit = default; + return false; + } + + public static bool ThrottledRaycastComponent(ref float timer, float interval, + Camera camera, float maxDistance, LayerMask layerMask, out T component) where T : Component + { + timer += Time.deltaTime; + + if (timer >= interval) + { + timer = 0f; + return RaycastFromCameraCenter(camera, maxDistance, layerMask, out component); + } + + component = null; + return false; + } + } +} diff --git a/Assets/Unheimlich_POC/Scripts/Utility/RaycastHelper.cs.meta b/Assets/Unheimlich_POC/Scripts/Utility/RaycastHelper.cs.meta new file mode 100644 index 00000000..30dc0c96 --- /dev/null +++ b/Assets/Unheimlich_POC/Scripts/Utility/RaycastHelper.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f37b9866154109148b3f7d2d3c121c36 \ No newline at end of file diff --git a/Assets/Unheimlich_POC/Scripts/Utility/TweenHelper.cs b/Assets/Unheimlich_POC/Scripts/Utility/TweenHelper.cs new file mode 100644 index 00000000..947914b9 --- /dev/null +++ b/Assets/Unheimlich_POC/Scripts/Utility/TweenHelper.cs @@ -0,0 +1,224 @@ +using UnityEngine; +using TMPro; + +namespace Unheimlich.Utility +{ + public static class TweenHelper + { + public static int FadeCanvasGroup(CanvasGroup canvasGroup, float targetAlpha, + float duration, System.Action onComplete = null, + LeanTweenType easeType = LeanTweenType.easeInQuad) + { + if (canvasGroup == null) return -1; + + LeanTween.cancel(canvasGroup.gameObject); + return LeanTween.alphaCanvas(canvasGroup, targetAlpha, duration) + .setEase(easeType) + .setOnComplete(onComplete) + .id; + } + + public static int FadeText(TextMeshProUGUI text, float targetAlpha, + float duration, System.Action onComplete = null, + LeanTweenType easeType = LeanTweenType.easeInQuad) + { + return FadeText(text?.gameObject, text, null, targetAlpha, duration, 1f, onComplete, easeType); + } + + public static int FadeText(GameObject targetObject, TextMeshProUGUI mainText, + TextMeshProUGUI ghostText, float targetAlpha, float duration, + float ghostAlphaRatio, System.Action onComplete = null, + LeanTweenType easeType = LeanTweenType.easeInQuad) + { + if (mainText == null || targetObject == null) return -1; + + float startAlpha = mainText.color.a; + LeanTween.cancel(targetObject); + + return LeanTween.value(targetObject, startAlpha, targetAlpha, duration) + .setEase(easeType) + .setOnUpdate((float val) => { + if (mainText != null) + { + Color c = mainText.color; + c.a = val; + mainText.color = c; + } + + if (ghostText != null) + { + Color gc = ghostText.color; + gc.a = val * ghostAlphaRatio; + ghostText.color = gc; + } + }) + .setOnComplete(onComplete) + .id; + } + + public static int ScaleLocal(GameObject target, Vector3 targetScale, + float duration, System.Action onComplete = null, + LeanTweenType easeType = LeanTweenType.easeInOutQuad) + { + if (target == null) return -1; + + LeanTween.cancel(target); + return LeanTween.scale(target, targetScale, duration) + .setEase(easeType) + .setOnComplete(onComplete) + .id; + } + + public static int MoveLocal(GameObject target, Vector3 targetPosition, + float duration, System.Action onComplete = null, + LeanTweenType easeType = LeanTweenType.easeInOutQuad) + { + if (target == null) return -1; + + LeanTween.cancel(target); + return LeanTween.moveLocal(target, targetPosition, duration) + .setEase(easeType) + .setOnComplete(onComplete) + .id; + } + + public static int RotateLocal(GameObject target, Vector3 targetRotation, + float duration, System.Action onComplete = null, + LeanTweenType easeType = LeanTweenType.easeInOutQuad) + { + if (target == null) return -1; + + LeanTween.cancel(target); + return LeanTween.rotate(target, targetRotation, duration) + .setEase(easeType) + .setOnComplete(onComplete) + .id; + } + + public static int MoveLocalWithCurve(GameObject target, Vector3 targetPosition, + float duration, AnimationCurve curve, System.Action onComplete = null) + { + if (target == null) return -1; + + LeanTween.cancel(target); + return LeanTween.moveLocal(target, targetPosition, duration) + .setEase(curve) + .setOnComplete(onComplete) + .id; + } + + public static int FadeVolume(AudioSource source, float targetVolume, + float duration, System.Action onComplete = null, + LeanTweenType easeType = LeanTweenType.easeInOutQuad) + { + if (source == null) return -1; + + GameObject sourceObject = source.gameObject; + LeanTween.cancel(sourceObject); + + return LeanTween.value(sourceObject, source.volume, targetVolume, duration) + .setEase(easeType) + .setOnUpdate((float val) => { + if (source != null) + source.volume = val; + }) + .setOnComplete(onComplete) + .id; + } + + public static int FadeVolume(GameObject target, float targetVolume, + float duration, System.Action onComplete = null, + LeanTweenType easeType = LeanTweenType.linear) + { + if (target == null) return -1; + + float startVolume = AudioListener.volume; + LeanTween.cancel(target); + + return LeanTween.value(target, startVolume, targetVolume, duration) + .setEase(easeType) + .setOnUpdate((float val) => { + AudioListener.volume = val; + }) + .setOnComplete(onComplete) + .id; + } + + public static int FloatAnimation(GameObject target, Vector3 basePosition, + float amplitude, float duration, float delay = 0f, Axis axis = Axis.Y) + { + if (target == null) return -1; + + LeanTween.cancel(target); + + return LeanTween.value(target, 0f, Mathf.PI * 2f, duration) + .setDelay(delay) + .setOnUpdate((float val) => { + if (target != null) + { + Vector3 newPos = basePosition; + float offset = Mathf.Sin(val) * amplitude; + + switch (axis) + { + case Axis.X: newPos.x += offset; break; + case Axis.Y: newPos.y += offset; break; + case Axis.Z: newPos.z += offset; break; + } + + target.transform.localPosition = newPos; + } + }) + .setLoopClamp() + .setEase(LeanTweenType.linear) + .id; + } + + public static int RotateAround(GameObject target, Vector3 axis, float degreesPerSecond) + { + if (target == null || degreesPerSecond <= 0f) return -1; + + float duration = 360f / degreesPerSecond; + LeanTween.cancel(target); + + return LeanTween.rotateAroundLocal(target, axis, 360f, duration) + .setLoopClamp() + .setEase(LeanTweenType.linear) + .id; + } + + public static int AnimateMaterialFloat(GameObject target, Material material, + int propertyID, float fromValue, float toValue, float duration, + System.Action onComplete = null, + LeanTweenType easeType = LeanTweenType.easeInOutQuad) + { + if (target == null || material == null) return -1; + + LeanTween.cancel(target); + + return LeanTween.value(target, fromValue, toValue, duration) + .setEase(easeType) + .setOnUpdate((float val) => { + if (material != null) + material.SetFloat(propertyID, val); + }) + .setOnComplete(onComplete) + .id; + } + + public static void CancelTween(ref int tweenId) + { + if (tweenId != -1) + { + LeanTween.cancel(tweenId); + tweenId = -1; + } + } + + } + + public enum Axis + { + X, Y, Z + } +} diff --git a/Assets/Unheimlich_POC/Scripts/Utility/TweenHelper.cs.meta b/Assets/Unheimlich_POC/Scripts/Utility/TweenHelper.cs.meta new file mode 100644 index 00000000..4d62b7b8 --- /dev/null +++ b/Assets/Unheimlich_POC/Scripts/Utility/TweenHelper.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f70df97fd94f86a4aa2cb00353624ba4 \ No newline at end of file diff --git a/Assets/Unheimlich_POC/Scripts/Utility/UnityExtensions.cs b/Assets/Unheimlich_POC/Scripts/Utility/UnityExtensions.cs new file mode 100644 index 00000000..148976a0 --- /dev/null +++ b/Assets/Unheimlich_POC/Scripts/Utility/UnityExtensions.cs @@ -0,0 +1,47 @@ +using UnityEngine; + +namespace Unheimlich.Utility +{ + public static class UnityExtensions + { + public static void SafeInvoke(this System.Action action) + { + action?.Invoke(); + } + + public static void SafeInvoke(this System.Action action, T arg) + { + action?.Invoke(arg); + } + + public static Color WithAlpha(this Color color, float alpha) + { + color.a = alpha; + return color; + } + + public static Vector3 WithY(this Vector3 vector, float y) + { + vector.y = y; + return vector; + } + + public static Vector3 WithX(this Vector3 vector, float x) + { + vector.x = x; + return vector; + } + + public static Vector3 WithZ(this Vector3 vector, float z) + { + vector.z = z; + return vector; + } + + public static void CancelAllTweens(this GameObject gameObject) + { + if (gameObject != null) + LeanTween.cancel(gameObject); + } + } +} diff --git a/Assets/Unheimlich_POC/Scripts/Utility/UnityExtensions.cs.meta b/Assets/Unheimlich_POC/Scripts/Utility/UnityExtensions.cs.meta new file mode 100644 index 00000000..04814e07 --- /dev/null +++ b/Assets/Unheimlich_POC/Scripts/Utility/UnityExtensions.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b5c8821940a7678458f26015ee167fdf \ No newline at end of file