clean : deleted all useless scripts
This commit is contained in:
@@ -1,38 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unheimlich.Core.Audio
|
||||
{
|
||||
[CreateAssetMenu(fileName = "AudioEmitterData", menuName = "Unheimlich/Audio/Audio Emitter Data")]
|
||||
public class AudioEmitterData : ScriptableObject
|
||||
{
|
||||
[Header("Emitter Info")]
|
||||
public AudioEmitterType EmitterType = AudioEmitterType.Generic;
|
||||
public string DisplayName = "Generic";
|
||||
|
||||
[Header("Procedural Audio Settings")]
|
||||
public float BaseFrequency = 440f;
|
||||
public float FrequencyVariation = 50f;
|
||||
public float VolumeMin = 0.3f;
|
||||
public float VolumeMax = 0.7f;
|
||||
public float DurationMin = 0.5f;
|
||||
public float DurationMax = 2f;
|
||||
|
||||
[Header("Harmonics (for complex sounds)")]
|
||||
public float[] HarmonicMultipliers = { 1f };
|
||||
public float[] HarmonicAmplitudes = { 1f };
|
||||
|
||||
[Header("Noise Settings")]
|
||||
[Range(0f, 1f)]
|
||||
public float NoiseAmount = 0f;
|
||||
|
||||
[Header("Vibration Settings")]
|
||||
public bool EnableVibration = true;
|
||||
public float VibrationIntensityMin = 0.1f;
|
||||
public float VibrationIntensityMax = 0.5f;
|
||||
public float VibrationFrequency = 10f;
|
||||
|
||||
[Header("Distance Settings")]
|
||||
public float MaxDistance = 10f;
|
||||
public AnimationCurve DistanceAttenuation = AnimationCurve.EaseInOut(0f, 1f, 1f, 0f);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c20752cb7065e844aaaedf606c4df2db
|
||||
@@ -1,434 +0,0 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using Unheimlich.Core.Utils;
|
||||
using Unheimlich.World.CameraSystem;
|
||||
|
||||
namespace Unheimlich.Core.Audio
|
||||
{
|
||||
public enum AudioEmitterType
|
||||
{
|
||||
Generic,
|
||||
Wall,
|
||||
Wood,
|
||||
Metal,
|
||||
Glass,
|
||||
Water,
|
||||
Stone,
|
||||
Mechanical,
|
||||
Electrical,
|
||||
Organic
|
||||
}
|
||||
|
||||
public class AudioListenable : MonoBehaviour
|
||||
{
|
||||
// String constants
|
||||
private const string CONTINUOUS_AUDIO_SOURCE_NAME = "Continuous Audio Source";
|
||||
|
||||
// Volume multipliers for concentration system
|
||||
private const float BASELINE_VOLUME_MULTIPLIER = 0.1f;
|
||||
private const float FULL_VOLUME_MULTIPLIER = 1.5f;
|
||||
private const float CONCENTRATION_FADE_DURATION = 1f;
|
||||
private const float RELEASE_FADE_DURATION = 0.5f;
|
||||
|
||||
[Header("Emitter Settings")]
|
||||
[SerializeField] private AudioEmitterType _emitterType = AudioEmitterType.Generic;
|
||||
[SerializeField] private bool _useProceduralAudio = true;
|
||||
|
||||
[Header("Continuous Emission")]
|
||||
[SerializeField] private bool _emitContinuously = true;
|
||||
[SerializeField] private float _continuousBaseVolume = 0.1f;
|
||||
[SerializeField] private float _hearingRangeMin = 4f;
|
||||
[SerializeField] private float _hearingRangeMax = 8f;
|
||||
|
||||
[Header("Listening Enhancement")]
|
||||
[SerializeField] private float _reverbWetMix = 0.5f;
|
||||
|
||||
[Header("Static Audio")]
|
||||
[SerializeField] private AudioClip _staticAudioClip;
|
||||
[SerializeField] private bool _loopStaticAudio = true;
|
||||
|
||||
[Header("Procedural Settings")]
|
||||
[SerializeField] private float _baseFrequency = 440f;
|
||||
[SerializeField] private float _frequencyVariation = 50f;
|
||||
[SerializeField] private float _volumeMin = 0.3f;
|
||||
[SerializeField] private float _volumeMax = 0.7f;
|
||||
[SerializeField] private float _durationMin = 0.5f;
|
||||
[SerializeField] private float _durationMax = 2f;
|
||||
|
||||
[Header("Distance Attenuation")]
|
||||
[SerializeField] private float _maxDistance = 10f;
|
||||
[SerializeField] private AnimationCurve _distanceAttenuation = AnimationCurve.EaseInOut(0f, 1f, 1f, 0f);
|
||||
|
||||
[Header("Visual Feedback")]
|
||||
[SerializeField] private ParticleSystem _emitterParticles;
|
||||
|
||||
[Header("Camera Settings")]
|
||||
[SerializeField] private CameraPanSettings _cameraPanSettings;
|
||||
|
||||
private AudioSource _audioSource;
|
||||
private AudioSource _continuousAudioSource;
|
||||
private AudioReverbFilter _reverbFilter;
|
||||
private bool _isCurrentlyListening;
|
||||
private float _listeningTimer;
|
||||
private float _nextProceduralEventTime;
|
||||
private Vector3 _listenerPosition;
|
||||
private bool _hasBeenTapped;
|
||||
private AudioClip _continuousClip;
|
||||
private Coroutine _volumeFadeCoroutine;
|
||||
private Coroutine _startListeningCoroutine;
|
||||
private int _concentrationFadeTweenId = -1;
|
||||
private int _crossfadeTweenId = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this emitter has been tapped during the current listening session.
|
||||
/// Used to prevent multiple taps on the same emitter.
|
||||
/// </summary>
|
||||
public bool HasBeenTapped => _hasBeenTapped;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the camera pan settings for this emitter, or null if not set.
|
||||
/// </summary>
|
||||
public CameraPanSettings CameraPanSettings => _cameraPanSettings;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_audioSource = GetComponent<AudioSource>();
|
||||
if (_audioSource == null)
|
||||
_audioSource = gameObject.AddComponent<AudioSource>();
|
||||
|
||||
_audioSource.spatialBlend = 1f;
|
||||
_audioSource.maxDistance = _maxDistance;
|
||||
_audioSource.rolloffMode = AudioRolloffMode.Custom;
|
||||
_audioSource.playOnAwake = false;
|
||||
|
||||
_reverbFilter = gameObject.AddComponent<AudioReverbFilter>();
|
||||
_reverbFilter.reverbPreset = AudioReverbPreset.Off;
|
||||
_reverbFilter.dryLevel = 0f;
|
||||
_reverbFilter.room = -10000;
|
||||
|
||||
GameObject continuousSourceObject = new GameObject(CONTINUOUS_AUDIO_SOURCE_NAME);
|
||||
continuousSourceObject.transform.SetParent(transform);
|
||||
continuousSourceObject.transform.localPosition = Vector3.zero;
|
||||
_continuousAudioSource = continuousSourceObject.AddComponent<AudioSource>();
|
||||
_continuousAudioSource.spatialBlend = 1f;
|
||||
_continuousAudioSource.maxDistance = _hearingRangeMax;
|
||||
_continuousAudioSource.minDistance = _hearingRangeMin;
|
||||
_continuousAudioSource.rolloffMode = AudioRolloffMode.Linear;
|
||||
_continuousAudioSource.playOnAwake = false;
|
||||
_continuousAudioSource.loop = true;
|
||||
|
||||
if (_emitterParticles != null)
|
||||
{
|
||||
_emitterParticles.Stop();
|
||||
var emission = _emitterParticles.emission;
|
||||
emission.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (_emitContinuously && _useProceduralAudio)
|
||||
{
|
||||
StartContinuousEmission();
|
||||
}
|
||||
}
|
||||
|
||||
private void StartContinuousEmission()
|
||||
{
|
||||
float frequency = _baseFrequency + Random.Range(-_frequencyVariation * 0.5f, _frequencyVariation * 0.5f);
|
||||
_continuousClip = GenerateProceduralAudioClip(frequency, 10f);
|
||||
|
||||
_continuousAudioSource.clip = _continuousClip;
|
||||
_continuousAudioSource.volume = _continuousBaseVolume * BASELINE_VOLUME_MULTIPLIER;
|
||||
_continuousAudioSource.loop = true;
|
||||
_continuousAudioSource.Play();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the concentration state and fades the emitter volume accordingly.
|
||||
/// When concentrating: fades from 30% to 100% over 1 second.
|
||||
/// When releasing: fades from 100% to 30% over 0.5 seconds.
|
||||
/// </summary>
|
||||
public void SetConcentrating(bool concentrating)
|
||||
{
|
||||
TweenHelper.CancelTween(ref _concentrationFadeTweenId);
|
||||
|
||||
if (_continuousAudioSource == null)
|
||||
return;
|
||||
|
||||
float baseVolume = _continuousBaseVolume;
|
||||
float fromMultiplier = concentrating ? BASELINE_VOLUME_MULTIPLIER : FULL_VOLUME_MULTIPLIER;
|
||||
float toMultiplier = concentrating ? FULL_VOLUME_MULTIPLIER : BASELINE_VOLUME_MULTIPLIER;
|
||||
float duration = concentrating ? CONCENTRATION_FADE_DURATION : RELEASE_FADE_DURATION;
|
||||
|
||||
float fromVolume = baseVolume * fromMultiplier;
|
||||
float toVolume = baseVolume * toMultiplier;
|
||||
|
||||
_concentrationFadeTweenId = TweenHelper.FadeVolume(
|
||||
_continuousAudioSource,
|
||||
toVolume,
|
||||
duration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the listening sequence for this emitter.
|
||||
/// Blocked if the emitter has already been tapped during this session.
|
||||
/// </summary>
|
||||
public void StartListening(Vector3 listenerPosition)
|
||||
{
|
||||
// Prevent multiple taps on the same emitter
|
||||
if (_hasBeenTapped)
|
||||
return;
|
||||
|
||||
if (_startListeningCoroutine != null)
|
||||
StopCoroutine(_startListeningCoroutine);
|
||||
|
||||
_startListeningCoroutine = StartCoroutine(StartListeningSequence(listenerPosition));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that handles the full listening sequence:
|
||||
/// - Crossfades from continuous to procedural audio
|
||||
/// - Activates particles
|
||||
/// - Marks emitter as tapped (prevents re-triggering)
|
||||
/// - Starts vibration
|
||||
/// </summary>
|
||||
private IEnumerator StartListeningSequence(Vector3 listenerPosition)
|
||||
{
|
||||
_isCurrentlyListening = true;
|
||||
_listenerPosition = listenerPosition;
|
||||
_listeningTimer = 0f;
|
||||
_nextProceduralEventTime = 0f;
|
||||
|
||||
// Mark as tapped to prevent re-triggering
|
||||
_hasBeenTapped = true;
|
||||
|
||||
if (_volumeFadeCoroutine != null)
|
||||
StopCoroutine(_volumeFadeCoroutine);
|
||||
|
||||
TweenHelper.CancelTween(ref _crossfadeTweenId);
|
||||
|
||||
float crossfadeDuration = 1f;
|
||||
|
||||
if (_continuousAudioSource != null)
|
||||
{
|
||||
_crossfadeTweenId = TweenHelper.FadeVolume(
|
||||
_continuousAudioSource,
|
||||
0f,
|
||||
crossfadeDuration,
|
||||
() =>
|
||||
{
|
||||
if (_continuousAudioSource != null)
|
||||
_continuousAudioSource.Stop();
|
||||
});
|
||||
}
|
||||
|
||||
if (_emitterParticles != null)
|
||||
{
|
||||
var emission = _emitterParticles.emission;
|
||||
emission.enabled = true;
|
||||
_emitterParticles.Play();
|
||||
}
|
||||
|
||||
EnableReverb(true);
|
||||
|
||||
yield return new WaitForSeconds(crossfadeDuration);
|
||||
|
||||
if (_useProceduralAudio)
|
||||
{
|
||||
PlayProceduralSound();
|
||||
}
|
||||
else if (_staticAudioClip != null)
|
||||
{
|
||||
_audioSource.clip = _staticAudioClip;
|
||||
_audioSource.loop = _loopStaticAudio;
|
||||
_audioSource.Play();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the listening mode and resets the emitter state.
|
||||
/// This is the only place where _hasBeenTapped is reset to false,
|
||||
/// allowing the emitter to be used again in a future listening session.
|
||||
/// </summary>
|
||||
public void StopListening(bool cancelConcentrationFade = true)
|
||||
{
|
||||
bool wasBeenTapped = _hasBeenTapped;
|
||||
|
||||
_isCurrentlyListening = false;
|
||||
_hasBeenTapped = false;
|
||||
|
||||
if (_volumeFadeCoroutine != null)
|
||||
StopCoroutine(_volumeFadeCoroutine);
|
||||
|
||||
if (_startListeningCoroutine != null)
|
||||
StopCoroutine(_startListeningCoroutine);
|
||||
|
||||
if (cancelConcentrationFade)
|
||||
TweenHelper.CancelTween(ref _concentrationFadeTweenId);
|
||||
|
||||
if (_audioSource != null && _audioSource.isPlaying)
|
||||
_audioSource.Stop();
|
||||
|
||||
if (_emitterParticles != null && wasBeenTapped)
|
||||
{
|
||||
_emitterParticles.Stop();
|
||||
var emission = _emitterParticles.emission;
|
||||
emission.enabled = false;
|
||||
}
|
||||
|
||||
if (_continuousAudioSource != null && !_continuousAudioSource.isPlaying && gameObject.activeInHierarchy)
|
||||
{
|
||||
StartContinuousEmission();
|
||||
}
|
||||
|
||||
EnableReverb(false);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
|
||||
if (!_isCurrentlyListening)
|
||||
return;
|
||||
|
||||
_listeningTimer += Time.deltaTime;
|
||||
|
||||
if (_useProceduralAudio && _listeningTimer >= _nextProceduralEventTime)
|
||||
{
|
||||
PlayProceduralSound();
|
||||
_nextProceduralEventTime = _listeningTimer + Random.Range(_durationMin, _durationMax);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void EnableReverb(bool enable)
|
||||
{
|
||||
if (_reverbFilter == null)
|
||||
return;
|
||||
|
||||
if (enable)
|
||||
{
|
||||
_reverbFilter.reverbPreset = AudioReverbPreset.Room;
|
||||
_reverbFilter.dryLevel = Mathf.Lerp(0f, -1000f, 1f - _reverbWetMix);
|
||||
_reverbFilter.room = Mathf.Lerp(-1000f, -100f, _reverbWetMix);
|
||||
}
|
||||
else
|
||||
{
|
||||
_reverbFilter.reverbPreset = AudioReverbPreset.Off;
|
||||
_reverbFilter.dryLevel = 0f;
|
||||
_reverbFilter.room = -10000;
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayProceduralSound()
|
||||
{
|
||||
float distance = Vector3.Distance(_listenerPosition, transform.position);
|
||||
float distanceFactor = Mathf.Clamp01(1f - (distance / _maxDistance));
|
||||
float attenuation = _distanceAttenuation.Evaluate(distanceFactor);
|
||||
|
||||
float frequency = _baseFrequency + Random.Range(-_frequencyVariation, _frequencyVariation);
|
||||
float volume = Mathf.Lerp(_volumeMin, _volumeMax, Random.value) * attenuation;
|
||||
|
||||
AudioClip proceduralClip = GenerateProceduralAudioClip(frequency, Random.Range(_durationMin, _durationMax));
|
||||
|
||||
_audioSource.clip = proceduralClip;
|
||||
_audioSource.volume = volume;
|
||||
_audioSource.loop = false;
|
||||
_audioSource.Play();
|
||||
}
|
||||
|
||||
private AudioClip GenerateProceduralAudioClip(float frequency, float duration)
|
||||
{
|
||||
int sampleRate = 44100;
|
||||
int sampleCount = Mathf.RoundToInt(sampleRate * duration);
|
||||
|
||||
AudioClip clip = AudioClip.Create("ProceduralSound", sampleCount, 1, sampleRate, false);
|
||||
float[] samples = new float[sampleCount];
|
||||
|
||||
for (int i = 0; i < sampleCount; i++)
|
||||
{
|
||||
float t = (float)i / sampleRate;
|
||||
float envelope = Mathf.Sin(Mathf.PI * t / duration);
|
||||
|
||||
samples[i] = GenerateSampleForType(frequency, t, _emitterType) * envelope;
|
||||
}
|
||||
|
||||
clip.SetData(samples, 0);
|
||||
return clip;
|
||||
}
|
||||
|
||||
private float GenerateSampleForType(float frequency, float time, AudioEmitterType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case AudioEmitterType.Metal:
|
||||
return Mathf.Sin(2f * Mathf.PI * frequency * time) * 0.5f +
|
||||
Mathf.Sin(2f * Mathf.PI * frequency * 2.1f * time) * 0.3f +
|
||||
Mathf.Sin(2f * Mathf.PI * frequency * 3.8f * time) * 0.2f;
|
||||
|
||||
case AudioEmitterType.Wood:
|
||||
return Mathf.Sin(2f * Mathf.PI * frequency * time) * 0.6f +
|
||||
(Random.value - 0.5f) * 0.1f;
|
||||
|
||||
case AudioEmitterType.Glass:
|
||||
return Mathf.Sin(2f * Mathf.PI * frequency * 1.5f * time) * 0.4f +
|
||||
Mathf.Sin(2f * Mathf.PI * frequency * 2.7f * time) * 0.3f +
|
||||
Mathf.Sin(2f * Mathf.PI * frequency * 4.2f * time) * 0.3f;
|
||||
|
||||
case AudioEmitterType.Stone:
|
||||
return Mathf.Sin(2f * Mathf.PI * frequency * 0.8f * time) * 0.5f +
|
||||
(Random.value - 0.5f) * 0.3f;
|
||||
|
||||
case AudioEmitterType.Water:
|
||||
return (Random.value - 0.5f) * 0.5f +
|
||||
Mathf.Sin(2f * Mathf.PI * frequency * 0.5f * time) * 0.3f;
|
||||
|
||||
case AudioEmitterType.Mechanical:
|
||||
return Mathf.Sin(2f * Mathf.PI * frequency * time) * 0.4f +
|
||||
Mathf.Sign(Mathf.Sin(2f * Mathf.PI * frequency * 0.3f * time)) * 0.3f;
|
||||
|
||||
case AudioEmitterType.Electrical:
|
||||
return (Random.value - 0.5f) * 0.6f +
|
||||
Mathf.Sin(2f * Mathf.PI * frequency * 50f * time) * 0.2f;
|
||||
|
||||
case AudioEmitterType.Organic:
|
||||
return Mathf.Sin(2f * Mathf.PI * frequency * 0.7f * time) * 0.5f +
|
||||
Mathf.Sin(2f * Mathf.PI * frequency * 1.3f * time) * 0.3f +
|
||||
(Random.value - 0.5f) * 0.2f;
|
||||
|
||||
case AudioEmitterType.Wall:
|
||||
return Mathf.Sin(2f * Mathf.PI * frequency * 0.5f * time) * 0.3f +
|
||||
(Random.value - 0.5f) * 0.1f;
|
||||
|
||||
default:
|
||||
return Mathf.Sin(2f * Mathf.PI * frequency * time);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
StopListening();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_continuousAudioSource != null && !_continuousAudioSource.isPlaying && _emitContinuously)
|
||||
{
|
||||
StartContinuousEmission();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawWireSphere(transform.position, _maxDistance);
|
||||
|
||||
if (_isCurrentlyListening)
|
||||
{
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawLine(transform.position, _listenerPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e15490f7adacb594cb114f535e71d6b6
|
||||
@@ -1,67 +0,0 @@
|
||||
using UnityEngine;
|
||||
using Unheimlich.Core;
|
||||
using Unheimlich.Core.Utils;
|
||||
using InputManager = Unheimlich.Core.Input.InputManager;
|
||||
|
||||
namespace Unheimlich.Core.Interaction
|
||||
{
|
||||
public class InteractionHandler : Singleton<InteractionHandler>
|
||||
{
|
||||
|
||||
[Header("Settings")]
|
||||
[SerializeField] private float _interactionRange = 1f;
|
||||
[SerializeField] private LayerMask _interactableLayers;
|
||||
[SerializeField] private float _raycastInterval = 0.1f;
|
||||
|
||||
private InteractableBase _currentInteractable;
|
||||
private Camera _playerCamera;
|
||||
private float _raycastTimer;
|
||||
|
||||
protected override void OnAwake()
|
||||
{
|
||||
_playerCamera = GetComponent<Camera>();
|
||||
}
|
||||
|
||||
private bool _didHit;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
_didHit = RaycastHelper.ThrottledRaycastComponent(
|
||||
ref _raycastTimer,
|
||||
_raycastInterval,
|
||||
_playerCamera,
|
||||
_interactionRange,
|
||||
_interactableLayers,
|
||||
out InteractableBase interactable);
|
||||
|
||||
if (_didHit)
|
||||
_currentInteractable = interactable;
|
||||
else if (!_didHit && _raycastTimer <= 0f)
|
||||
_currentInteractable = null;
|
||||
|
||||
HandleInteractionInput();
|
||||
}
|
||||
|
||||
private void HandleInteractionInput()
|
||||
{
|
||||
var interactAction = InputManager.GetInteractAction();
|
||||
if (interactAction != null && interactAction.WasPressedThisFrame())
|
||||
{
|
||||
if (_currentInteractable != null)
|
||||
_currentInteractable.Interact();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (_playerCamera != null)
|
||||
{
|
||||
Gizmos.color = _currentInteractable != null ? Color.green : Color.red;
|
||||
|
||||
Vector3 viewportCenter = new Vector3(0.5f, 0.5f, 0f);
|
||||
Ray ray = _playerCamera.ViewportPointToRay(viewportCenter);
|
||||
Gizmos.DrawRay(ray.origin, ray.direction * _interactionRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d96ceb0ee513b1e4197e55e9b926269b
|
||||
@@ -1,17 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unheimlich.Core.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all interactable objects in the game.
|
||||
/// Derived classes must implement Interact() to define interaction behavior.
|
||||
/// </summary>
|
||||
public abstract class InteractableBase : MonoBehaviour
|
||||
{
|
||||
[Header("Interaction Settings")]
|
||||
[Tooltip("Text displayed when the player looks at the object")]
|
||||
public string InteractionText = "Interact";
|
||||
|
||||
public abstract void Interact();
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2bd6cb9667f8674294172efbdf8afd3
|
||||
@@ -1,8 +1,6 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using Unheimlich.Features.Dialogue.Data;
|
||||
using Unheimlich.Features.DetectiveBoard.Data;
|
||||
using Unheimlich.Features.DetectiveBoard;
|
||||
using Unheimlich.Features.Player;
|
||||
using Unheimlich.Features.Dialogue;
|
||||
using Unheimlich.Features.Dialogue.UI;
|
||||
@@ -29,10 +27,6 @@ namespace Unheimlich.Core.Interaction
|
||||
[Header("Prefab")]
|
||||
[SerializeField] private ChoiceFocusHandler _wordPrefab;
|
||||
|
||||
[Header("Detective Board Integration")]
|
||||
[Tooltip("Clue to unlock when words are fully merged (optional)")]
|
||||
[SerializeField] private ClueData _clueToUnlock;
|
||||
|
||||
private Transform _playerTransform;
|
||||
private Camera _mainCamera;
|
||||
private List<ChoiceFocusHandler> _spawnedWords = new List<ChoiceFocusHandler>();
|
||||
@@ -327,21 +321,12 @@ namespace Unheimlich.Core.Interaction
|
||||
.setOnComplete(() =>
|
||||
{
|
||||
_hasCompletedMergeOnce = true;
|
||||
UnlockClue();
|
||||
DespawnWords();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UnlockClue()
|
||||
{
|
||||
if (_clueToUnlock != null && DetectiveBoardManager.Instance != null)
|
||||
{
|
||||
DetectiveBoardManager.Instance.UnlockClue(_clueToUnlock);
|
||||
Debug.Log($"[ObjectDescription] Unlocked clue: {_clueToUnlock.name} from description merge");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void UnmergeWords()
|
||||
{
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using Unheimlich.Core;
|
||||
using Unheimlich.Features.Items;
|
||||
using Unheimlich.Features.Player;
|
||||
using InputManager = Unheimlich.Core.Input.InputManager;
|
||||
|
||||
namespace Unheimlich.Core.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles player item inspection mode.
|
||||
/// </summary>
|
||||
public class PlayerInspectionHandler : Singleton<PlayerInspectionHandler>
|
||||
{
|
||||
|
||||
[Header("Components")]
|
||||
[SerializeField] private Transform _inspectionHoldPoint;
|
||||
|
||||
[Header("State")]
|
||||
public bool IsInspecting { get; private set; } = false;
|
||||
private InspectableItem _currentInspectedItem;
|
||||
|
||||
private Coroutine _interactionListener;
|
||||
private bool _isUsingGamepad = false;
|
||||
|
||||
public void ToggleInspection(InspectableItem item)
|
||||
{
|
||||
if (!IsInspecting)
|
||||
{
|
||||
StartInspection(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_interactionListener != null)
|
||||
StopInspection();
|
||||
}
|
||||
}
|
||||
|
||||
private void StartInspection(InspectableItem item)
|
||||
{
|
||||
IsInspecting = true;
|
||||
_currentInspectedItem = item;
|
||||
|
||||
// Set Inspection context (handles cursor automatically)
|
||||
InputManager.SetContext(InputManager.InputContext.Inspection);
|
||||
|
||||
// Disable OctoPlayer to prevent movement and look
|
||||
if (PlayerController.Instance?.Movement != null)
|
||||
{
|
||||
PlayerController.Instance.Movement.enabled = false;
|
||||
}
|
||||
|
||||
item.PrepareForInspection(_inspectionHoldPoint);
|
||||
|
||||
if (_interactionListener == null)
|
||||
_interactionListener = StartCoroutine(ListenForInteraction());
|
||||
|
||||
}
|
||||
|
||||
private void StopInspection()
|
||||
{
|
||||
if (_interactionListener != null)
|
||||
{
|
||||
StopCoroutine(_interactionListener);
|
||||
_interactionListener = null;
|
||||
}
|
||||
|
||||
StopInspectionInternal();
|
||||
}
|
||||
|
||||
private void StopInspectionInternal()
|
||||
{
|
||||
if (_currentInspectedItem != null)
|
||||
{
|
||||
_currentInspectedItem.Release();
|
||||
_currentInspectedItem = null;
|
||||
|
||||
StartCoroutine(DelayedToggleInput(true));
|
||||
}
|
||||
IsInspecting = false;
|
||||
}
|
||||
|
||||
private IEnumerator DelayedToggleInput(bool enable)
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
|
||||
// Re-enable OctoPlayer
|
||||
if (PlayerController.Instance?.Movement != null)
|
||||
{
|
||||
PlayerController.Instance.Movement.enabled = true;
|
||||
}
|
||||
|
||||
// Restore Normal context (handles cursor automatically)
|
||||
InputManager.SetContext(InputManager.InputContext.Normal);
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
|
||||
private IEnumerator ListenForInteraction()
|
||||
{
|
||||
yield return null;
|
||||
|
||||
while (IsInspecting)
|
||||
{
|
||||
var interactAction = InputManager.GetInteractAction();
|
||||
if (interactAction != null && interactAction.WasPressedThisFrame())
|
||||
{
|
||||
StopInspectionInternal();
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
_interactionListener = null;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (IsInspecting && _currentInspectedItem != null)
|
||||
{
|
||||
HandleObjectRotation();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleObjectRotation()
|
||||
{
|
||||
if (_currentInspectedItem == null) return;
|
||||
|
||||
Vector2 lookInput = Vector2.zero;
|
||||
bool hasInput = false;
|
||||
|
||||
// Read input directly (not cached)
|
||||
if (Gamepad.current != null)
|
||||
{
|
||||
Vector2 stickInput = Gamepad.current.rightStick.ReadValue();
|
||||
if (stickInput.sqrMagnitude > 0.01f)
|
||||
{
|
||||
lookInput = stickInput;
|
||||
hasInput = true;
|
||||
_isUsingGamepad = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasInput && Mouse.current != null)
|
||||
{
|
||||
Vector2 mouseDelta = Mouse.current.delta.ReadValue();
|
||||
if (mouseDelta.sqrMagnitude > 0.01f)
|
||||
{
|
||||
lookInput = mouseDelta;
|
||||
hasInput = true;
|
||||
_isUsingGamepad = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Only rotate if there's actual input
|
||||
if (hasInput)
|
||||
{
|
||||
// Normalize to get constant rotation speed regardless of input magnitude
|
||||
Vector2 direction = lookInput.normalized;
|
||||
|
||||
float rotationMultiplier = _isUsingGamepad ? Time.deltaTime : 0.02f;
|
||||
|
||||
float rotX = direction.y * _currentInspectedItem.RotationSpeed * rotationMultiplier;
|
||||
float rotY = direction.x * _currentInspectedItem.RotationSpeed * rotationMultiplier;
|
||||
|
||||
_currentInspectedItem.transform.Rotate(transform.up, -rotY, Space.World);
|
||||
_currentInspectedItem.transform.Rotate(transform.right, rotX, Space.World);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 464960797e705e84a9eb87ecce4e9311
|
||||
Reference in New Issue
Block a user