chore: Project Restructuring & Theatre
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e871b828a2e9ca4ba7cc5e025373372
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unheimlich.Features.Diapason
|
||||
{
|
||||
|
||||
[System.Serializable]
|
||||
public class TapAnimationSettings
|
||||
{
|
||||
[Header("Tap Motion")]
|
||||
[Tooltip("Elbow rotation during windup (degrees)")]
|
||||
public float WindupRotation = 15f;
|
||||
|
||||
[Tooltip("Duration of windup (pull back)")]
|
||||
public float WindupDuration = 0.1f;
|
||||
|
||||
[Tooltip("Duration of strike (forward snap)")]
|
||||
public float StrikeDuration = 0.05f;
|
||||
|
||||
[Tooltip("Wrist snap rotation during strike (degrees)")]
|
||||
public float WristSnapRotation = 10f;
|
||||
|
||||
[Header("Vibration Shake")]
|
||||
[Tooltip("Shake intensity during vibration")]
|
||||
public float ShakeIntensity = 2f;
|
||||
|
||||
[Tooltip("Shake speed (frequency)")]
|
||||
public float ShakeSpeed = 30f;
|
||||
}
|
||||
|
||||
public class EquippableTapSettings : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private TapAnimationSettings _tapSettings = new TapAnimationSettings();
|
||||
|
||||
public TapAnimationSettings TapSettings => _tapSettings;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b46af809fa8f34d49b3dec606a505c5e
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: adbe6317f6c8e9e469b36e06ab2abbfb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,379 @@
|
||||
using UnityEngine;
|
||||
using Unheimlich.Core;
|
||||
using Unheimlich.Core.Audio;
|
||||
using Unheimlich.Features.OctoArms;
|
||||
using InputManager = Unheimlich.Core.Input.InputManager;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Unheimlich.Features.Diapason
|
||||
{
|
||||
|
||||
public class DiapasonEquipmentManager : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private OctoIkArmsImproved _armsSystem;
|
||||
[SerializeField] private GameObject _diapasonPrefab;
|
||||
[SerializeField] private int _equipmentArmIndex = 1;
|
||||
|
||||
[Header("Detection Settings")]
|
||||
[SerializeField] private LayerMask _soundEmitterLayer;
|
||||
[SerializeField] private float _detectionRadius = 0.15f;
|
||||
[SerializeField] private Transform[] _detectionPoints;
|
||||
|
||||
[Header("Visual Feedback")]
|
||||
[SerializeField] private GameObject _contactFeedbackPrefab;
|
||||
|
||||
[Header("Diapason Animation")]
|
||||
[SerializeField] private string _vibratingBoolParam = "IsVibrating";
|
||||
[SerializeField] private string _tapTriggerParam = "Tap";
|
||||
|
||||
[Header("Tap Settings")]
|
||||
[SerializeField] private AudioClip _tapSound;
|
||||
[SerializeField] [Range(0f, 1f)] private float _tapVolume = 1f;
|
||||
[SerializeField] private float _vibrationDuration = 15f;
|
||||
|
||||
public bool IsEquipped => _isEquipped;
|
||||
public AudioListenable CurrentDetectedEmitter => _currentDetectedEmitter;
|
||||
public bool CanTap => _isEquipped && _currentDetectedEmitter != null && !_isVibrating && !_currentDetectedEmitter.HasBeenTapped;
|
||||
|
||||
public event Action<AudioListenable> OnEmitterDetected;
|
||||
public event Action<AudioListenable> OnEmitterLost;
|
||||
|
||||
private GameObject _spawnedDiapason;
|
||||
private bool _isEquipped = false;
|
||||
private AudioListenable _currentDetectedEmitter;
|
||||
private GameObject _contactFeedback;
|
||||
private bool _isVibrating = false;
|
||||
private AudioSource _audioSource;
|
||||
private Coroutine _vibrationCoroutine;
|
||||
private Animator _diapasonAnimator;
|
||||
private ParticleSystem _diapasonParticles;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (_armsSystem == null)
|
||||
{
|
||||
_armsSystem = GetComponent<OctoIkArmsImproved>();
|
||||
if (_armsSystem == null)
|
||||
{
|
||||
Debug.LogError("[DiapasonEquipment] OctoIkArmsImproved not found!");
|
||||
}
|
||||
}
|
||||
|
||||
_audioSource = GetComponent<AudioSource>();
|
||||
if (_audioSource == null)
|
||||
{
|
||||
_audioSource = gameObject.AddComponent<AudioSource>();
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (_isEquipped && _armsSystem.GetArmState(_equipmentArmIndex) == ArmState.Equipped)
|
||||
{
|
||||
CheckForSoundEmitters();
|
||||
|
||||
var fireAction = InputManager.GetFireAction();
|
||||
if (fireAction != null && fireAction.WasPressedThisFrame())
|
||||
{
|
||||
if (CanTap)
|
||||
TapDiapason();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void EquipDiapason()
|
||||
{
|
||||
if (_isEquipped)
|
||||
{
|
||||
Debug.LogWarning("[DiapasonEquipment] Diapason already equipped!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_diapasonPrefab == null)
|
||||
{
|
||||
Debug.LogError("[DiapasonEquipment] Diapason prefab not assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
_spawnedDiapason = Instantiate(_diapasonPrefab);
|
||||
_spawnedDiapason.SetActive(true);
|
||||
|
||||
_diapasonAnimator = _spawnedDiapason.GetComponentInChildren<Animator>(true);
|
||||
if (_diapasonAnimator == null)
|
||||
{
|
||||
Debug.LogWarning("[DiapasonEquipment] No Animator found on spawned diapason");
|
||||
}
|
||||
else
|
||||
{
|
||||
_diapasonAnimator.enabled = false;
|
||||
}
|
||||
|
||||
_diapasonParticles = _spawnedDiapason.GetComponentInChildren<ParticleSystem>(true);
|
||||
if (_diapasonParticles == null)
|
||||
{
|
||||
Debug.LogWarning("[DiapasonEquipment] No ParticleSystem found on spawned diapason");
|
||||
}
|
||||
|
||||
_armsSystem.EquipObjectOnArm(_equipmentArmIndex, _spawnedDiapason, () => {
|
||||
_isEquipped = true;
|
||||
OnDiapasonEquipped();
|
||||
});
|
||||
}
|
||||
|
||||
public void UnequipDiapason()
|
||||
{
|
||||
if (!_isEquipped)
|
||||
{
|
||||
Debug.LogWarning("[DiapasonEquipment] Diapason not equipped!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_vibrationCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_vibrationCoroutine);
|
||||
_vibrationCoroutine = null;
|
||||
}
|
||||
|
||||
if (_currentDetectedEmitter != null)
|
||||
{
|
||||
_currentDetectedEmitter.SetConcentrating(false);
|
||||
_currentDetectedEmitter.StopListening(cancelConcentrationFade: false);
|
||||
_currentDetectedEmitter = null;
|
||||
}
|
||||
|
||||
_armsSystem.UnequipObjectFromArm(_equipmentArmIndex, () => {
|
||||
_isEquipped = false;
|
||||
_spawnedDiapason = null;
|
||||
OnDiapasonUnequipped();
|
||||
});
|
||||
}
|
||||
|
||||
private void CheckForSoundEmitters()
|
||||
{
|
||||
if (_detectionPoints == null || _detectionPoints.Length == 0)
|
||||
return;
|
||||
|
||||
AudioListenable closestEmitter = null;
|
||||
float closestDistance = float.MaxValue;
|
||||
|
||||
foreach (Transform detectionPoint in _detectionPoints)
|
||||
{
|
||||
if (detectionPoint == null)
|
||||
continue;
|
||||
|
||||
Collider[] hits = Physics.OverlapSphere(detectionPoint.position, _detectionRadius, _soundEmitterLayer);
|
||||
|
||||
foreach (Collider hit in hits)
|
||||
{
|
||||
AudioListenable emitter = hit.GetComponent<AudioListenable>();
|
||||
|
||||
if (emitter != null)
|
||||
{
|
||||
float distance = Vector3.Distance(detectionPoint.position, hit.transform.position);
|
||||
|
||||
if (distance < closestDistance)
|
||||
{
|
||||
closestDistance = distance;
|
||||
closestEmitter = emitter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (closestEmitter != _currentDetectedEmitter)
|
||||
{
|
||||
if (_currentDetectedEmitter != null)
|
||||
{
|
||||
OnEmitterExited(_currentDetectedEmitter);
|
||||
}
|
||||
|
||||
_currentDetectedEmitter = closestEmitter;
|
||||
|
||||
if (_currentDetectedEmitter != null)
|
||||
{
|
||||
OnEmitterEntered(_currentDetectedEmitter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDiapasonEquipped()
|
||||
{
|
||||
PlayerEvents.TriggerDiapasonEquip();
|
||||
|
||||
if (_diapasonAnimator != null)
|
||||
{
|
||||
_diapasonAnimator.enabled = true;
|
||||
_diapasonAnimator.SetBool(_vibratingBoolParam, false);
|
||||
}
|
||||
|
||||
if (_diapasonParticles != null)
|
||||
{
|
||||
_diapasonParticles.Stop();
|
||||
var emission = _diapasonParticles.emission;
|
||||
emission.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDiapasonUnequipped()
|
||||
{
|
||||
PlayerEvents.TriggerDiapasonUnequip();
|
||||
|
||||
_isVibrating = false;
|
||||
_diapasonAnimator = null;
|
||||
_diapasonParticles = null;
|
||||
}
|
||||
|
||||
public void TapDiapason()
|
||||
{
|
||||
if (!CanTap)
|
||||
{
|
||||
Debug.LogWarning("[DiapasonEquipment] Cannot tap: not equipped, no contact, or already tapped");
|
||||
return;
|
||||
}
|
||||
|
||||
TapAnimationSettings tapSettings = null;
|
||||
if (_spawnedDiapason != null)
|
||||
{
|
||||
EquippableTapSettings equippable = _spawnedDiapason.GetComponent<EquippableTapSettings>();
|
||||
if (equippable != null)
|
||||
{
|
||||
tapSettings = equippable.TapSettings;
|
||||
}
|
||||
}
|
||||
|
||||
ArmEquipment equipment = _armsSystem.GetArmEquipment(_equipmentArmIndex);
|
||||
if (equipment != null && tapSettings != null)
|
||||
{
|
||||
equipment.PlayTapAnimation(tapSettings);
|
||||
equipment.StartShake(tapSettings);
|
||||
}
|
||||
|
||||
if (_tapSound != null && _audioSource != null)
|
||||
{
|
||||
_audioSource.PlayOneShot(_tapSound, _tapVolume);
|
||||
}
|
||||
|
||||
if (_diapasonAnimator != null)
|
||||
{
|
||||
_diapasonAnimator.SetTrigger(_tapTriggerParam);
|
||||
_diapasonAnimator.SetBool(_vibratingBoolParam, true);
|
||||
}
|
||||
|
||||
if (_diapasonParticles != null)
|
||||
{
|
||||
var emission = _diapasonParticles.emission;
|
||||
emission.enabled = true;
|
||||
_diapasonParticles.Play();
|
||||
}
|
||||
|
||||
_isVibrating = true;
|
||||
|
||||
if (_currentDetectedEmitter != null)
|
||||
{
|
||||
_currentDetectedEmitter.StartListening(transform.position);
|
||||
}
|
||||
|
||||
PlayerEvents.TriggerDiapasonTap();
|
||||
|
||||
if (_vibrationCoroutine != null)
|
||||
StopCoroutine(_vibrationCoroutine);
|
||||
_vibrationCoroutine = StartCoroutine(StopVibrationAfterDuration());
|
||||
}
|
||||
|
||||
private IEnumerator StopVibrationAfterDuration()
|
||||
{
|
||||
yield return new WaitForSeconds(_vibrationDuration);
|
||||
|
||||
if (_diapasonAnimator != null)
|
||||
{
|
||||
_diapasonAnimator.SetBool(_vibratingBoolParam, false);
|
||||
}
|
||||
|
||||
if (_diapasonParticles != null)
|
||||
{
|
||||
_diapasonParticles.Stop();
|
||||
var emission = _diapasonParticles.emission;
|
||||
emission.enabled = false;
|
||||
}
|
||||
|
||||
if (_currentDetectedEmitter != null)
|
||||
{
|
||||
_currentDetectedEmitter.StopListening();
|
||||
}
|
||||
|
||||
ArmEquipment equipment = _armsSystem.GetArmEquipment(_equipmentArmIndex);
|
||||
if (equipment != null)
|
||||
{
|
||||
equipment.StopShake();
|
||||
}
|
||||
|
||||
_isVibrating = false;
|
||||
_vibrationCoroutine = null;
|
||||
}
|
||||
|
||||
private void OnEmitterEntered(AudioListenable emitter)
|
||||
{
|
||||
|
||||
emitter.SetConcentrating(true);
|
||||
|
||||
if (_contactFeedbackPrefab != null && _contactFeedback == null)
|
||||
{
|
||||
_contactFeedback = Instantiate(_contactFeedbackPrefab, emitter.transform);
|
||||
_contactFeedback.transform.localPosition = Vector3.zero;
|
||||
}
|
||||
|
||||
OnEmitterDetected?.Invoke(emitter);
|
||||
}
|
||||
|
||||
private void OnEmitterExited(AudioListenable emitter)
|
||||
{
|
||||
emitter.SetConcentrating(false);
|
||||
|
||||
if (!emitter.HasBeenTapped)
|
||||
{
|
||||
emitter.StopListening();
|
||||
}
|
||||
|
||||
if (_contactFeedback != null)
|
||||
{
|
||||
Destroy(_contactFeedback);
|
||||
_contactFeedback = null;
|
||||
}
|
||||
|
||||
if (_diapasonAnimator != null)
|
||||
{
|
||||
_diapasonAnimator.SetBool(_vibratingBoolParam, false);
|
||||
}
|
||||
|
||||
if (_diapasonParticles != null)
|
||||
{
|
||||
_diapasonParticles.Stop();
|
||||
var emission = _diapasonParticles.emission;
|
||||
emission.enabled = false;
|
||||
}
|
||||
|
||||
_isVibrating = false;
|
||||
|
||||
OnEmitterLost?.Invoke(emitter);
|
||||
}
|
||||
|
||||
void OnDrawGizmosSelected()
|
||||
{
|
||||
if (_detectionPoints == null || !_isEquipped)
|
||||
return;
|
||||
|
||||
Gizmos.color = Color.yellow;
|
||||
|
||||
foreach (Transform detectionPoint in _detectionPoints)
|
||||
{
|
||||
if (detectionPoint != null)
|
||||
{
|
||||
Gizmos.DrawWireSphere(detectionPoint.position, _detectionRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 38b80731eb88f834ab3d79a566e40b48
|
||||
@@ -0,0 +1,193 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using Unheimlich.Core.Audio;
|
||||
using Unheimlich.Core;
|
||||
using Unheimlich.Core.Utils;
|
||||
using Unheimlich.Features.Player;
|
||||
using Unheimlich.World.CameraSystem;
|
||||
using InputManager = Unheimlich.Core.Input.InputManager;
|
||||
|
||||
namespace Unheimlich.Features.Diapason
|
||||
{
|
||||
public class DiapasonListeningHandlerV2 : Singleton<DiapasonListeningHandlerV2>
|
||||
{
|
||||
[Header("Settings")]
|
||||
[SerializeField] private bool _disablePlayerMovementDuringListen = true;
|
||||
[SerializeField] private bool _enablePanMode = true;
|
||||
|
||||
[Header("Visual Feedback")]
|
||||
[Tooltip("Vignette intensity during listening (0-1)")]
|
||||
[SerializeField] [Range(0f, 1f)] private float _vignetteIntensity = 0.3f;
|
||||
|
||||
[Header("Audio")]
|
||||
[SerializeField] private AudioClip _breathInClip;
|
||||
[SerializeField] [Range(0f, 1f)] private float _breathInVolume = 1f;
|
||||
[SerializeField] private float _concentrationDuration = 1f;
|
||||
[SerializeField] private float _breathInDuration = 0.5f;
|
||||
|
||||
public bool IsListening { get; private set; }
|
||||
public bool IsConcentrating { get; private set; }
|
||||
|
||||
private AudioSource _playerAudioSource;
|
||||
private Coroutine _listeningCoroutine;
|
||||
private CameraHeadBob _cameraHeadBob;
|
||||
private CameraPanSettings _originalCameraSettings;
|
||||
private AudioListenable _lastDetectedEmitter;
|
||||
private bool _isFadingOut;
|
||||
|
||||
protected override void OnAwake()
|
||||
{
|
||||
_playerAudioSource = GetComponent<AudioSource>();
|
||||
if (_playerAudioSource == null)
|
||||
_playerAudioSource = gameObject.AddComponent<AudioSource>();
|
||||
|
||||
_cameraHeadBob = GetComponentInChildren<CameraHeadBob>();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
var diapasonAction = InputManager.GetDiapasonAction();
|
||||
if (diapasonAction != null && diapasonAction.WasPressedThisFrame())
|
||||
{
|
||||
if (!IsListening && !IsConcentrating)
|
||||
{
|
||||
StartListeningMode();
|
||||
}
|
||||
else if (IsListening || IsConcentrating)
|
||||
{
|
||||
StopListeningMode();
|
||||
}
|
||||
}
|
||||
|
||||
if (IsListening && PlayerController.Instance?.CameraPanController != null)
|
||||
{
|
||||
PlayerController.Instance.CameraPanController.UpdatePan();
|
||||
}
|
||||
}
|
||||
|
||||
private void StartListeningMode()
|
||||
{
|
||||
if (_isFadingOut || PlayerController.Instance?.DiapasonEquipment == null)
|
||||
return;
|
||||
|
||||
if (_listeningCoroutine != null)
|
||||
StopCoroutine(_listeningCoroutine);
|
||||
|
||||
PlayerEvents.TriggerEnterListening();
|
||||
_listeningCoroutine = StartCoroutine(ListeningSequence());
|
||||
}
|
||||
|
||||
private IEnumerator ListeningSequence()
|
||||
{
|
||||
IsConcentrating = true;
|
||||
InputManager.SetContext(InputManager.InputContext.Listening);
|
||||
|
||||
if (_disablePlayerMovementDuringListen && PlayerController.Instance?.Movement != null)
|
||||
{
|
||||
PlayerController.Instance.Movement.enabled = false;
|
||||
}
|
||||
|
||||
PlayerController.Instance.DiapasonEquipment.EquipDiapason();
|
||||
|
||||
PlayBreathIn();
|
||||
yield return new WaitForSeconds(_breathInDuration);
|
||||
|
||||
if (_cameraHeadBob != null)
|
||||
{
|
||||
float resetDuration = _concentrationDuration - _breathInDuration;
|
||||
_cameraHeadBob.LerpToZero(resetDuration);
|
||||
}
|
||||
|
||||
yield return new WaitForSeconds(_concentrationDuration - _breathInDuration);
|
||||
|
||||
IsConcentrating = false;
|
||||
IsListening = true;
|
||||
|
||||
EnableVignette(true);
|
||||
|
||||
if (_enablePanMode && PlayerController.Instance?.CameraPanController != null)
|
||||
{
|
||||
PlayerController.Instance.CameraPanController.SaveOriginalState();
|
||||
PlayerController.Instance.CameraPanController.StartPan();
|
||||
}
|
||||
|
||||
Cursor.lockState = CursorLockMode.Confined;
|
||||
Cursor.visible = false;
|
||||
}
|
||||
|
||||
private void StopListeningMode()
|
||||
{
|
||||
if (_listeningCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_listeningCoroutine);
|
||||
_listeningCoroutine = null;
|
||||
}
|
||||
|
||||
IsListening = false;
|
||||
IsConcentrating = false;
|
||||
_isFadingOut = true;
|
||||
|
||||
PlayerController.Instance.DiapasonEquipment.UnequipDiapason();
|
||||
|
||||
EnableVignette(false);
|
||||
|
||||
if (_enablePanMode && PlayerController.Instance?.CameraPanController != null)
|
||||
{
|
||||
PlayerController.Instance.CameraPanController.StopPan();
|
||||
PlayerController.Instance.CameraPanController.ReturnToOriginalPosition(() =>
|
||||
{
|
||||
if (_originalCameraSettings != null && PlayerController.Instance?.CameraPanController != null)
|
||||
{
|
||||
PlayerController.Instance.CameraPanController.Settings = _originalCameraSettings;
|
||||
_originalCameraSettings = null;
|
||||
}
|
||||
|
||||
if (_disablePlayerMovementDuringListen && PlayerController.Instance?.Movement != null)
|
||||
{
|
||||
PlayerController.Instance.Movement.enabled = true;
|
||||
}
|
||||
|
||||
_isFadingOut = false;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_disablePlayerMovementDuringListen && PlayerController.Instance?.Movement != null)
|
||||
{
|
||||
PlayerController.Instance.Movement.enabled = true;
|
||||
}
|
||||
|
||||
_isFadingOut = false;
|
||||
}
|
||||
|
||||
InputManager.SetContext(InputManager.InputContext.Normal);
|
||||
PlayerEvents.TriggerExitListening();
|
||||
|
||||
Cursor.lockState = CursorLockMode.Locked;
|
||||
Cursor.visible = false;
|
||||
}
|
||||
|
||||
private void PlayBreathIn()
|
||||
{
|
||||
if (_breathInClip != null && _playerAudioSource != null)
|
||||
{
|
||||
_playerAudioSource.PlayOneShot(_breathInClip, _breathInVolume);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnableVignette(bool enable)
|
||||
{
|
||||
if (ListeningVignetteEffect.Instance == null)
|
||||
return;
|
||||
|
||||
if (enable)
|
||||
{
|
||||
ListeningVignetteEffect.Instance.EnableVignetteWithIntensity(_vignetteIntensity);
|
||||
}
|
||||
else
|
||||
{
|
||||
ListeningVignetteEffect.Instance.EnableVignette(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46c1f473fbd3cac4397aabaa76d18357
|
||||
Reference in New Issue
Block a user