Files
Ipseity-Demo/Assets/Unheimlich_POC/Scripts/Features/Dialogue/Runtime/DialogueFocusSystem.cs
T

256 lines
9.1 KiB
C#

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;
}
}
}