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