Start implementation of diapason mechanics

This commit is contained in:
2026-01-28 18:26:21 +01:00
parent b64a57d810
commit bef8c6ef94
66 changed files with 10191 additions and 98 deletions
@@ -0,0 +1,284 @@
using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;
using Unheimlich.Audio;
using InputManager = Unheimlich.Managers.PlayerInputManager;
namespace Unheimlich.Gameplay.Player
{
public class DiapasonListeningHandler : MonoBehaviour
{
public static DiapasonListeningHandler Instance { get; private set; }
[Header("References")]
[SerializeField] private GameObject _diapasonObject;
[SerializeField] private Animator _diapasonAnimator;
[SerializeField] private Camera _playerCamera;
[SerializeField] private Transform _cursorTransform;
[Header("Listening Settings")]
[SerializeField] private float _listeningRange = 5f;
[SerializeField] private LayerMask _listenableLayers;
[SerializeField] private float _concentrationDuration = 1f;
[SerializeField] private float _breathInDuration = 0.5f;
[Header("Camera Settings")]
[SerializeField] private float _cameraPanSpeed = 2f;
[SerializeField] private float _cameraPanBorderSize = 0.1f;
[SerializeField] private float _maxCameraPanAngle = 15f;
[Header("Visual Feedback")]
[SerializeField] private float _vignetteIntensity = 0.5f;
[SerializeField] private ListeningVignetteEffect _vignetteEffect;
[Header("Audio")]
[SerializeField] private AudioClip _breathInClip;
[SerializeField] private AudioClip _diapasonTapClip;
[SerializeField] private float _ambientAttenuationAmount = 0.3f;
[Header("Input Action")]
[SerializeField] private InputActionReference _diapasonAction;
public bool IsListening { get; private set; }
public bool IsConcentrating { get; private set; }
private AudioListenable _currentTargetEmitter;
private AudioSource _playerAudioSource;
private Coroutine _listeningCoroutine;
private Vector2 _originalCameraRotation;
private float _originalAmbientVolume;
private const string ANIM_TRIGGER_TAP = "Tap";
private const string ANIM_BOOL_EQUIPPED = "IsEquipped";
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(this);
return;
}
Instance = this;
_playerAudioSource = GetComponent<AudioSource>();
if (_playerAudioSource == null)
_playerAudioSource = gameObject.AddComponent<AudioSource>();
if (_diapasonObject != null)
_diapasonObject.SetActive(false);
}
private void Update()
{
if (!InputManager.IsInputEnabled)
return;
if (_diapasonAction != null && _diapasonAction.action.WasPressedThisFrame())
{
if (!IsListening && !IsConcentrating)
StartListeningMode();
else if (IsListening)
StopListeningMode();
}
if (IsListening)
{
UpdateCursorAndCameraPan();
CheckForListenableObject();
}
}
private void StartListeningMode()
{
if (_listeningCoroutine != null)
StopCoroutine(_listeningCoroutine);
_listeningCoroutine = StartCoroutine(ListeningSequence());
}
private IEnumerator ListeningSequence()
{
IsConcentrating = true;
InputManager.ToggleInput(false);
if (_diapasonObject != null)
{
_diapasonObject.SetActive(true);
if (_diapasonAnimator != null)
_diapasonAnimator.SetBool(ANIM_BOOL_EQUIPPED, true);
}
PlayBreathIn();
yield return new WaitForSeconds(_breathInDuration);
yield return new WaitForSeconds(_concentrationDuration - _breathInDuration);
IsConcentrating = false;
IsListening = true;
_originalCameraRotation = new Vector2(transform.eulerAngles.y, _playerCamera.transform.eulerAngles.x);
AttenuateAmbientSound(true);
EnableVignette(true);
Cursor.lockState = CursorLockMode.Confined;
Cursor.visible = true;
}
private void StopListeningMode()
{
if (_listeningCoroutine != null)
{
StopCoroutine(_listeningCoroutine);
_listeningCoroutine = null;
}
IsListening = false;
IsConcentrating = false;
if (_diapasonObject != null)
{
if (_diapasonAnimator != null)
_diapasonAnimator.SetBool(ANIM_BOOL_EQUIPPED, false);
_diapasonObject.SetActive(false);
}
AttenuateAmbientSound(false);
EnableVignette(false);
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
InputManager.ToggleInput(true);
if (_currentTargetEmitter != null)
{
_currentTargetEmitter.StopListening();
_currentTargetEmitter = null;
}
}
private void UpdateCursorAndCameraPan()
{
if (_playerCamera == null)
return;
Vector2 viewportPos = _playerCamera.ScreenToViewportPoint(Mouse.current.position.ReadValue());
float panX = 0f;
float panY = 0f;
if (viewportPos.x < _cameraPanBorderSize)
panX = -1f;
else if (viewportPos.x > 1f - _cameraPanBorderSize)
panX = 1f;
if (viewportPos.y < _cameraPanBorderSize)
panY = -1f;
else if (viewportPos.y > 1f - _cameraPanBorderSize)
panY = 1f;
float targetYaw = _originalCameraRotation.x + (panX * _maxCameraPanAngle);
float targetPitch = _originalCameraRotation.y - (panY * _maxCameraPanAngle);
targetPitch = Mathf.Clamp(targetPitch, _originalCameraRotation.y - _maxCameraPanAngle, _originalCameraRotation.y + _maxCameraPanAngle);
Vector3 currentRotation = transform.eulerAngles;
currentRotation.y = Mathf.Lerp(currentRotation.y, targetYaw, Time.deltaTime * _cameraPanSpeed);
transform.eulerAngles = currentRotation;
Vector3 cameraRotation = _playerCamera.transform.localEulerAngles;
if (cameraRotation.x > 180f)
cameraRotation.x -= 360f;
cameraRotation.x = Mathf.Lerp(cameraRotation.x, targetPitch, Time.deltaTime * _cameraPanSpeed);
_playerCamera.transform.localEulerAngles = cameraRotation;
}
private void CheckForListenableObject()
{
if (_playerCamera == null)
return;
Ray ray = _playerCamera.ScreenPointToRay(Mouse.current.position.ReadValue());
if (Physics.Raycast(ray, out RaycastHit hit, _listeningRange, _listenableLayers))
{
AudioListenable listenable = hit.collider.GetComponent<AudioListenable>();
if (listenable != null && listenable != _currentTargetEmitter)
{
if (_currentTargetEmitter != null)
_currentTargetEmitter.StopListening();
_currentTargetEmitter = listenable;
}
if (Mouse.current.leftButton.wasPressedThisFrame && _currentTargetEmitter != null)
{
TriggerDiapasonTap();
}
}
else
{
if (_currentTargetEmitter != null)
{
_currentTargetEmitter.StopListening();
_currentTargetEmitter = null;
}
}
}
private void TriggerDiapasonTap()
{
if (_diapasonAnimator != null)
_diapasonAnimator.SetTrigger(ANIM_TRIGGER_TAP);
if (_diapasonTapClip != null && _playerAudioSource != null)
_playerAudioSource.PlayOneShot(_diapasonTapClip);
if (_currentTargetEmitter != null)
_currentTargetEmitter.StartListening(transform.position);
}
private void PlayBreathIn()
{
if (_breathInClip != null && _playerAudioSource != null)
_playerAudioSource.PlayOneShot(_breathInClip);
}
private void AttenuateAmbientSound(bool enable)
{
AudioListener listener = FindFirstObjectByType<AudioListener>();
if (listener != null)
{
if (enable)
{
_originalAmbientVolume = AudioListener.volume;
AudioListener.volume = _ambientAttenuationAmount;
}
else
{
AudioListener.volume = _originalAmbientVolume;
}
}
}
private void EnableVignette(bool enable)
{
if (_vignetteEffect != null)
{
_vignetteEffect.EnableVignette(enable);
_vignetteEffect.SetIntensity(_vignetteIntensity);
}
}
private void OnDrawGizmosSelected()
{
if (_playerCamera != null && IsListening)
{
Gizmos.color = _currentTargetEmitter != null ? Color.green : Color.yellow;
Ray ray = _playerCamera.ScreenPointToRay(Mouse.current != null ? Mouse.current.position.ReadValue() : Vector2.zero);
Gizmos.DrawRay(ray.origin, ray.direction * _listeningRange);
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2094bd9c6f72e5b4cadf430f4d70f8f6
@@ -0,0 +1,57 @@
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
namespace Unheimlich.Gameplay.Player
{
public class ListeningVignetteEffect : MonoBehaviour
{
[Header("References")]
[SerializeField] private Volume _globalVolume;
[Header("Settings")]
[SerializeField] private float _targetIntensity = 0.5f;
[SerializeField] private float _transitionSpeed = 2f;
private Vignette _vignette;
private float _originalIntensity;
private bool _isEnabled;
private void Awake()
{
if (_globalVolume == null)
_globalVolume = FindFirstObjectByType<Volume>();
if (_globalVolume != null && _globalVolume.profile != null)
{
if (_globalVolume.profile.TryGet(out Vignette vignette))
{
_vignette = vignette;
_originalIntensity = _vignette.intensity.value;
}
}
}
private void Update()
{
if (_vignette == null)
return;
float targetValue = _isEnabled ? _targetIntensity : _originalIntensity;
float currentValue = _vignette.intensity.value;
float newValue = Mathf.Lerp(currentValue, targetValue, Time.deltaTime * _transitionSpeed);
_vignette.intensity.value = newValue;
}
public void EnableVignette(bool enable)
{
_isEnabled = enable;
}
public void SetIntensity(float intensity)
{
_targetIntensity = Mathf.Clamp01(intensity);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 878ebc270c13e5c49a3813b1848f6ea5