Advanced POC - portals, dialogue system + floating words, room loading

This commit is contained in:
2026-01-26 20:31:27 +01:00
parent b5ca4763b3
commit 203b00578a
426 changed files with 123482 additions and 29 deletions
@@ -0,0 +1,214 @@
using UnityEngine;
using System;
using Unheimlich.Gameplay.Player;
using Unheimlich.Interactions.Dialogues;
using Unheimlich.Interactions.FloatingWords;
using Unheimlich.Data.Dialogues;
namespace Unheimlich.Managers
{
/// <summary>
/// Manages gaze-based focus detection for dialogue choices using raycasts.
/// Handles progressive focus timers, aim assist integration, visual feedback,
/// and automatic choice selection when player maintains focus for a duration.
/// </summary>
public class DialogueFocusSystem
{
private readonly DialogueManager _manager;
private readonly Camera _mainCamera;
private readonly PlayerLook _playerLook;
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 _shakeIntensity;
private static readonly Vector3 ViewportCenter = new Vector3(0.5f, 0.5f, 0f);
public event Action<DialogueChoiceData> OnChoiceMade;
public DialogueFocusSystem(
DialogueManager manager,
Camera mainCamera,
PlayerLook playerLook,
LayerMask choiceLayer,
float focusRange,
float focusTime,
float raycastInterval,
float recoveryDuration,
float focusRetentionTime,
float shakeIntensity)
{
_manager = manager;
_mainCamera = mainCamera;
_playerLook = playerLook;
_choiceLayer = choiceLayer;
_focusRange = focusRange;
_focusTime = focusTime;
_raycastInterval = raycastInterval;
_recoveryDuration = recoveryDuration;
_focusRetentionTime = focusRetentionTime;
_shakeIntensity = shakeIntensity;
}
/// <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.IsInDialogue || !_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;
return;
}
if (_mainCamera == null)
return;
Ray ray = _mainCamera.ViewportPointToRay(ViewportCenter);
ChoiceFocusHandler hitHandler = null;
bool raycastHitChoice = Physics.Raycast(ray, out RaycastHit hit, _focusRange, _choiceLayer);
if (raycastHitChoice)
{
hitHandler = hit.collider.GetComponent<ChoiceFocusHandler>();
if (_playerLook != null)
_playerLook.ApplyAimAssist(hit.collider.bounds.center);
}
if (hitHandler != _currentFocusedHandler)
{
_retentionTimer -= Time.deltaTime;
if (hitHandler != null && _retentionTimer <= 0f)
{
if (_currentFocusedHandler != null)
{
_isFocusLocked = true;
_currentFocusedHandler.ResetFeedback(_recoveryDuration);
}
_currentFocusedHandler = hitHandler;
_focusTimer = 0f;
_retentionTimer = _focusRetentionTime;
if (_playerLook != null)
_playerLook.LockAim(true);
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);
if (_playerLook != null)
_playerLook.Shake(progress / _shakeIntensity);
if (_focusTimer >= _focusTime)
{
_isFocusLocked = true;
_currentFocusedHandler.ResetFeedback(_recoveryDuration);
if (_currentFocusedHandler.DialogueData != null)
{
OnChoiceMade?.Invoke(_currentFocusedHandler.DialogueData);
DialogueNode nextNode = _currentFocusedHandler.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();
if (_playerLook != null)
_playerLook.LockAim(false);
}
}
else if (_currentFocusedHandler != null && _retentionTimer <= 0f)
{
_isFocusLocked = true;
_currentFocusedHandler.ResetFeedback(_recoveryDuration);
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 (_playerLook != null)
_playerLook.LockAim(false);
}
}
private void ResetAnimationTimers()
{
_currentFocusedHandler = null;
_focusTimer = 0f;
_retentionTimer = 0f;
}
public void UnlockFocus()
{
_isFocusLocked = false;
}
}
}