chore: Project Restructuring & Theatre
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using Unheimlich.Features.Player;
|
||||
using Unheimlich.Features.Dialogue;
|
||||
using Unheimlich.Features.Dialogue.UI;
|
||||
using Unheimlich.Features.Dialogue.Data;
|
||||
using Unheimlich.Core.Utils;
|
||||
|
||||
namespace Unheimlich.Features.Dialogue
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages gaze-based focus detection for dialogue choices using raycasts.
|
||||
/// </summary>
|
||||
public class DialogueFocusSystem
|
||||
{
|
||||
private readonly DialogueManager _manager;
|
||||
private readonly Camera _mainCamera;
|
||||
private readonly CameraEffects _cameraEffects;
|
||||
|
||||
private ChoiceFocusHandler _currentFocusedHandler;
|
||||
private float _focusTimer = 0f;
|
||||
private float _retentionTimer = 0f;
|
||||
private float _raycastTimer = 0f;
|
||||
private bool _isFocusLocked = false;
|
||||
|
||||
private readonly LayerMask _choiceLayer;
|
||||
private readonly float _focusRange;
|
||||
private readonly float _focusTime;
|
||||
private readonly float _raycastInterval;
|
||||
private readonly float _recoveryDuration;
|
||||
private readonly float _focusRetentionTime;
|
||||
private readonly float _fallbackShakeIntensity;
|
||||
|
||||
public event Action<DialogueChoiceData> OnChoiceMade;
|
||||
|
||||
public DialogueFocusSystem(
|
||||
DialogueManager manager,
|
||||
Camera mainCamera,
|
||||
CameraEffects cameraEffects,
|
||||
LayerMask choiceLayer,
|
||||
float focusRange,
|
||||
float focusTime,
|
||||
float raycastInterval,
|
||||
float recoveryDuration,
|
||||
float focusRetentionTime,
|
||||
float fallbackShakeIntensity)
|
||||
{
|
||||
_manager = manager;
|
||||
_mainCamera = mainCamera;
|
||||
_cameraEffects = cameraEffects;
|
||||
_choiceLayer = choiceLayer;
|
||||
_focusRange = focusRange;
|
||||
_focusTime = focusTime;
|
||||
_raycastInterval = raycastInterval;
|
||||
_recoveryDuration = recoveryDuration;
|
||||
_focusRetentionTime = focusRetentionTime;
|
||||
_fallbackShakeIntensity = fallbackShakeIntensity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the focus detection system. Should be called every frame during active dialogue.
|
||||
/// Performs raycasts at configured intervals to detect choice focus.
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
if (!_manager.IsDialogueActive || !_manager.CurrentDialogueUI.AreChoicesVisible())
|
||||
return;
|
||||
|
||||
_raycastTimer += Time.deltaTime;
|
||||
if (_raycastTimer >= _raycastInterval)
|
||||
{
|
||||
CheckForChoiceFocus();
|
||||
_raycastTimer = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs raycast from camera center to detect focused dialogue choices.
|
||||
/// Manages focus state transitions, retention timers, visual feedback, and choice selection.
|
||||
/// Implements stabilization to prevent rapid focus switching between choices.
|
||||
/// </summary>
|
||||
private void CheckForChoiceFocus()
|
||||
{
|
||||
if (_isFocusLocked)
|
||||
{
|
||||
if (_retentionTimer > 0f)
|
||||
_retentionTimer -= Time.deltaTime;
|
||||
else
|
||||
_isFocusLocked = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_mainCamera == null)
|
||||
return;
|
||||
|
||||
ChoiceFocusHandler hitHandler = null;
|
||||
|
||||
if (RaycastHelper.RaycastFromCameraCenter(_mainCamera, _focusRange, _choiceLayer, out RaycastHit hit))
|
||||
{
|
||||
hitHandler = hit.collider.GetComponent<ChoiceFocusHandler>();
|
||||
|
||||
if (_cameraEffects != null && hitHandler != null)
|
||||
_cameraEffects.ApplyAimAssist(hit.collider.bounds.center);
|
||||
}
|
||||
|
||||
if (hitHandler != _currentFocusedHandler)
|
||||
{
|
||||
_retentionTimer -= Time.deltaTime;
|
||||
|
||||
if (hitHandler != null && _retentionTimer <= 0f)
|
||||
{
|
||||
if (_currentFocusedHandler != null)
|
||||
{
|
||||
_currentFocusedHandler.ResetFeedback(_recoveryDuration);
|
||||
ResetAllUnfocusEffects();
|
||||
}
|
||||
|
||||
_currentFocusedHandler = hitHandler;
|
||||
_focusTimer = 0f;
|
||||
_retentionTimer = _focusRetentionTime;
|
||||
|
||||
if (_cameraEffects != null)
|
||||
_cameraEffects.LockAim(true);
|
||||
|
||||
ApplyUnfocusEffectToOthers(hitHandler);
|
||||
|
||||
DialogueAnimationSettings settings = _manager.CurrentAnimationSettings;
|
||||
bool shouldRotate = settings == null || settings.EnableRotation;
|
||||
if (shouldRotate && _manager.CurrentDialogueUI != null)
|
||||
{
|
||||
float focusSpeed = settings != null ? settings.FocusRotationSpeed : 60f;
|
||||
_manager.CurrentDialogueUI.RotationAnimation(focusSpeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (hitHandler != null)
|
||||
{
|
||||
_retentionTimer = _focusRetentionTime;
|
||||
}
|
||||
|
||||
bool isFocusActive = _currentFocusedHandler != null && (_retentionTimer > 0f);
|
||||
|
||||
if (isFocusActive)
|
||||
{
|
||||
_focusTimer += Time.deltaTime;
|
||||
float progress = _focusTimer / _focusTime;
|
||||
|
||||
_currentFocusedHandler.ApplyFocusFeedback(progress);
|
||||
|
||||
DialogueAnimationSettings currentSettings = _manager.CurrentAnimationSettings;
|
||||
bool shouldShake = currentSettings == null || currentSettings.EnableCameraShakeOnFocus;
|
||||
|
||||
if (shouldShake && _cameraEffects != null)
|
||||
{
|
||||
float shakeIntensity = currentSettings != null ? currentSettings.CameraShakeIntensity : _fallbackShakeIntensity;
|
||||
float shakeSmoothness = currentSettings != null ? currentSettings.CameraShakeSmoothness : 10f;
|
||||
_cameraEffects.ShakeCamera(progress / shakeIntensity, shakeSmoothness);
|
||||
}
|
||||
|
||||
if (_focusTimer >= _focusTime)
|
||||
{
|
||||
ChoiceFocusHandler selectedHandler = _currentFocusedHandler;
|
||||
|
||||
if (selectedHandler.DialogueData != null)
|
||||
{
|
||||
OnChoiceMade?.Invoke(selectedHandler.DialogueData);
|
||||
DialogueNode nextNode = selectedHandler.GetNextNode();
|
||||
_manager.GoToNextNode(nextNode);
|
||||
}
|
||||
|
||||
DialogueAnimationSettings settings = _manager.CurrentAnimationSettings;
|
||||
bool shouldRotate = settings == null || settings.EnableRotation;
|
||||
if (shouldRotate && _manager.CurrentDialogueUI != null)
|
||||
{
|
||||
float normalSpeed = settings != null ? settings.NormalRotationSpeed : 36f;
|
||||
_manager.CurrentDialogueUI.RotationAnimation(normalSpeed);
|
||||
}
|
||||
|
||||
ResetAnimationTimers();
|
||||
ResetAllUnfocusEffects();
|
||||
|
||||
selectedHandler.ResetFeedback(_recoveryDuration);
|
||||
|
||||
if (_cameraEffects != null)
|
||||
_cameraEffects.LockAim(false);
|
||||
}
|
||||
}
|
||||
else if (_currentFocusedHandler != null && _retentionTimer <= 0f)
|
||||
{
|
||||
_currentFocusedHandler.ResetFeedback(_recoveryDuration);
|
||||
ResetAllUnfocusEffects();
|
||||
|
||||
DialogueAnimationSettings settings = _manager.CurrentAnimationSettings;
|
||||
bool shouldRotate = settings == null || settings.EnableRotation;
|
||||
if (shouldRotate && _manager.CurrentDialogueUI != null)
|
||||
{
|
||||
float normalSpeed = settings != null ? settings.NormalRotationSpeed : 36f;
|
||||
_manager.CurrentDialogueUI.RotationAnimation(normalSpeed);
|
||||
}
|
||||
|
||||
ResetAnimationTimers();
|
||||
|
||||
if (_cameraEffects != null)
|
||||
_cameraEffects.LockAim(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyUnfocusEffectToOthers(ChoiceFocusHandler focusedHandler)
|
||||
{
|
||||
if (_manager.CurrentDialogueUI == null || _manager.CurrentDialogueUI.ChoiceContainer == null)
|
||||
return;
|
||||
|
||||
DialogueAnimationSettings settings = _manager.CurrentAnimationSettings;
|
||||
float shakeSlowdown = settings != null ? settings.UnfocusedShakeSpeedMultiplier : 0.3f;
|
||||
|
||||
foreach (Transform child in _manager.CurrentDialogueUI.ChoiceContainer.transform)
|
||||
{
|
||||
ChoiceFocusHandler handler = child.GetComponent<ChoiceFocusHandler>();
|
||||
if (handler != null && handler != focusedHandler)
|
||||
{
|
||||
handler.SetShakeSpeedMultiplier(shakeSlowdown);
|
||||
handler.ApplySketchFade();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetAllUnfocusEffects()
|
||||
{
|
||||
if (_manager.CurrentDialogueUI == null || _manager.CurrentDialogueUI.ChoiceContainer == null)
|
||||
return;
|
||||
|
||||
foreach (Transform child in _manager.CurrentDialogueUI.ChoiceContainer.transform)
|
||||
{
|
||||
ChoiceFocusHandler handler = child.GetComponent<ChoiceFocusHandler>();
|
||||
if (handler != null)
|
||||
{
|
||||
handler.SetShakeSpeedMultiplier(1f);
|
||||
handler.ResetSketchFade();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetAnimationTimers()
|
||||
{
|
||||
_currentFocusedHandler = null;
|
||||
_focusTimer = 0f;
|
||||
_retentionTimer = 0f;
|
||||
}
|
||||
|
||||
public void UnlockFocus()
|
||||
{
|
||||
_isFocusLocked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user