Add detective board

This commit is contained in:
2026-02-10 03:00:41 +01:00
parent d4a728a150
commit b172762d4f
51 changed files with 1502 additions and 92 deletions
@@ -2,6 +2,7 @@ using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;
using Unheimlich.Data.CameraSettings;
using PlayerInput = Unheimlich.Managers.PlayerInputManager;
namespace Unheimlich.Gameplay.CameraControl
{
@@ -18,9 +19,6 @@ namespace Unheimlich.Gameplay.CameraControl
[Header("Settings")]
[SerializeField] private CameraPanSettings _settings;
[Header("Input Actions")]
[SerializeField] private InputActionReference _lookAction;
private Vector2 _originalCameraRotation;
private Vector3 _originalCameraLocalPosition;
private Vector3 _originalPlayerPosition;
@@ -29,7 +27,6 @@ namespace Unheimlich.Gameplay.CameraControl
private Coroutine _alignmentCoroutine;
private Coroutine _returnCoroutine;
private float _timeSinceLastInput;
private const float INPUT_IDLE_THRESHOLD = 0.1f;
public bool IsPanning { get; private set; }
@@ -99,9 +96,10 @@ namespace Unheimlich.Gameplay.CameraControl
float panY = 0f;
bool hasInput = false;
if (_lookAction != null && _lookAction.action != null)
var lookAction = PlayerInput.GetLookAction();
if (lookAction != null)
{
Vector2 lookInput = _lookAction.action.ReadValue<Vector2>();
Vector2 lookInput = lookAction.ReadValue<Vector2>();
if (lookInput.sqrMagnitude > 0.01f)
{
panX = lookInput.x;
@@ -150,29 +148,37 @@ namespace Unheimlich.Gameplay.CameraControl
_timeSinceLastInput += Time.deltaTime;
}
float targetYaw;
float targetPitch;
if (hasInput || _timeSinceLastInput < INPUT_IDLE_THRESHOLD)
{
targetYaw = _originalCameraRotation.x + panX * _settings.MaxPanAngle;
targetPitch = _originalCameraRotation.y - panY * _settings.MaxPanAngle;
}
else
{
targetYaw = _originalCameraRotation.x;
targetPitch = _originalCameraRotation.y;
}
targetYaw = Mathf.Clamp(targetYaw, _originalCameraRotation.x - _settings.MaxPanAngle, _originalCameraRotation.x + _settings.MaxPanAngle);
targetPitch = Mathf.Clamp(targetPitch, _originalCameraRotation.y - _settings.MaxPanAngle, _originalCameraRotation.y + _settings.MaxPanAngle);
float currentYaw = _playerTransform.eulerAngles.y;
float currentPitch = _camera.transform.localEulerAngles.x;
if (currentPitch > 180f)
currentPitch -= 360f;
float lerpSpeed = hasInput ? _settings.PanSpeed : _settings.PanSpeed * 2f;
float targetYaw;
float targetPitch;
float lerpSpeed;
bool shouldRecenter = _settings.EnableRecentering && _timeSinceLastInput >= _settings.RecenterDelay;
if (hasInput)
{
targetYaw = _originalCameraRotation.x + panX * _settings.MaxPanAngle;
targetPitch = _originalCameraRotation.y - panY * _settings.MaxPanAngle;
targetYaw = Mathf.Clamp(targetYaw, _originalCameraRotation.x - _settings.MaxPanAngle, _originalCameraRotation.x + _settings.MaxPanAngle);
targetPitch = Mathf.Clamp(targetPitch, _originalCameraRotation.y - _settings.MaxPanAngle, _originalCameraRotation.y + _settings.MaxPanAngle);
lerpSpeed = _settings.PanSpeed;
}
else if (shouldRecenter)
{
targetYaw = _originalCameraRotation.x;
targetPitch = _originalCameraRotation.y;
lerpSpeed = _settings.RecenterSpeed;
}
else
{
targetYaw = currentYaw;
targetPitch = currentPitch;
lerpSpeed = 0f;
}
float newYaw = Mathf.LerpAngle(currentYaw, targetYaw, Time.deltaTime * lerpSpeed);
float newPitch = Mathf.LerpAngle(currentPitch, targetPitch, Time.deltaTime * lerpSpeed);
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 82ca56838506a694196a2130ecff8694
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,186 @@
using UnityEngine;
using Unheimlich.Data.CameraSettings;
using Unheimlich.Gameplay.CameraControl;
using Unheimlich.Gameplay.Player;
using PlayerInput = Unheimlich.Managers.PlayerInputManager;
namespace Unheimlich.Gameplay.DetectiveBoard
{
/// <summary>
/// Controls player interaction with the detective board in world space.
/// Uses CameraPanController for wide-angle camera movement across the board.
/// Activates when player is in range and looking at the board.
/// </summary>
public class DetectiveBoardController : MonoBehaviour
{
[Header("References")]
[SerializeField] private CameraPanController _cameraPanController;
[SerializeField] private Transform _boardTransform;
[Header("Settings")]
[SerializeField] private CameraPanSettings _boardCameraPanSettings;
[SerializeField] private float _activationRange = 2f;
[SerializeField] private LayerMask _boardLayer;
private Camera _playerCamera;
private bool _isBoardActive;
private CameraPanSettings _originalCameraPanSettings;
private CameraHeadBob _cameraHeadBob;
private void Awake()
{
_playerCamera = Camera.main;
if (_cameraPanController == null)
{
_cameraPanController = FindFirstObjectByType<CameraPanController>();
}
_cameraHeadBob = FindFirstObjectByType<CameraHeadBob>();
}
private void Update()
{
if (_isBoardActive)
{
HandleBoardInteraction();
}
else
{
CheckForBoardActivation();
}
}
/// <summary>
/// Checks if player is looking at the board and in range to activate.
/// </summary>
private void CheckForBoardActivation()
{
if (_playerCamera == null || _boardTransform == null)
return;
float distanceToBoard = Vector3.Distance(_playerCamera.transform.position, _boardTransform.position);
if (distanceToBoard > _activationRange)
return;
Ray ray = new Ray(_playerCamera.transform.position, _playerCamera.transform.forward);
if (Physics.Raycast(ray, out RaycastHit hit, _activationRange, _boardLayer))
{
if (hit.transform.IsChildOf(_boardTransform) || hit.transform == _boardTransform)
{
var interactAction = PlayerInput.GetInteractAction();
if (interactAction != null && interactAction.WasPressedThisFrame())
{
ActivateBoard();
}
}
}
}
/// <summary>
/// Activates the detective board, switching to pan mode with wide-angle settings.
/// </summary>
public void ActivateBoard()
{
if (_cameraPanController == null || _boardCameraPanSettings == null)
{
Debug.LogError("[DetectiveBoardController] Missing required references!");
return;
}
_isBoardActive = true;
_originalCameraPanSettings = _cameraPanController.Settings;
_cameraPanController.Settings = _boardCameraPanSettings;
_cameraPanController.SaveOriginalState();
_cameraPanController.AlignToTarget(_boardTransform, () =>
{
_cameraPanController.StartPan();
});
PlayerInput.SetContext(PlayerInput.InputContext.Listening);
ApplyHeadBobSettings(_boardCameraPanSettings);
Debug.Log("[DetectiveBoardController] Detective board activated");
}
/// <summary>
/// Handles board interaction while active (panning and exit).
/// </summary>
private void HandleBoardInteraction()
{
if (_cameraPanController != null)
{
_cameraPanController.UpdatePan();
}
var exitAction = PlayerInput.GetExitAction();
if (exitAction != null && exitAction.WasPressedThisFrame())
{
DeactivateBoard();
}
}
/// <summary>
/// Deactivates the board and returns to normal gameplay.
/// </summary>
public void DeactivateBoard()
{
if (!_isBoardActive)
return;
_isBoardActive = false;
if (_cameraPanController != null)
{
_cameraPanController.StopPan();
_cameraPanController.ReturnToOriginalPosition(() =>
{
if (_originalCameraPanSettings != null)
{
_cameraPanController.Settings = _originalCameraPanSettings;
}
});
}
PlayerInput.SetContext(PlayerInput.InputContext.Normal);
RestoreHeadBob();
Debug.Log("[DetectiveBoardController] Detective board deactivated");
}
/// <summary>
/// Applies head bob settings based on CameraPanSettings configuration.
/// </summary>
private void ApplyHeadBobSettings(CameraPanSettings settings)
{
if (_cameraHeadBob == null || settings == null)
return;
_cameraHeadBob.SetMode(settings.HeadBobMode);
}
/// <summary>
/// Restores head bob to normal behavior.
/// </summary>
private void RestoreHeadBob()
{
if (_cameraHeadBob == null)
return;
_cameraHeadBob.ClearOverride();
}
private void OnDrawGizmosSelected()
{
if (_boardTransform == null)
return;
Gizmos.color = _isBoardActive ? Color.green : Color.yellow;
Gizmos.DrawWireSphere(_boardTransform.position, _activationRange);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9f9f72364623a2a4bb50f02537fd2e72
@@ -1,5 +1,6 @@
using UnityEngine;
using Unheimlich.Interactions.Core;
using Unheimlich.Data.CameraSettings;
namespace Unheimlich.Gameplay.Player
{
@@ -21,6 +22,9 @@ namespace Unheimlich.Gameplay.Player
private PlayerMovement _playerMovement;
private float _timer = 0f;
private Vector3 _startLocalPos;
private HeadBobMode _overrideMode = HeadBobMode.Normal;
private bool _hasOverride = false;
void Start()
{
@@ -33,7 +37,15 @@ namespace Unheimlich.Gameplay.Player
if (_playerMovement == null || _playerMovement.CharacterController == null)
return;
if (PlayerInspectionHandler.Instance != null && PlayerInspectionHandler.Instance.IsInspecting)
HeadBobMode currentMode = _hasOverride ? _overrideMode : HeadBobMode.Normal;
if (currentMode == HeadBobMode.Disabled)
{
ResetPosition();
return;
}
if (currentMode == HeadBobMode.Idle || (PlayerInspectionHandler.Instance != null && PlayerInspectionHandler.Instance.IsInspecting))
{
ApplyBob(_idleBobSpeed, _idleBobAmplitude);
return;
@@ -52,6 +64,23 @@ namespace Unheimlich.Gameplay.Player
else
ResetPosition();
}
/// <summary>
/// Sets the head bob mode override.
/// </summary>
public void SetMode(HeadBobMode mode)
{
_overrideMode = mode;
_hasOverride = true;
}
/// <summary>
/// Clears the override and returns to normal behavior.
/// </summary>
public void ClearOverride()
{
_hasOverride = false;
}
private void ApplyBob(float speed, float amplitude)
{
@@ -60,10 +60,6 @@ namespace Unheimlich.Gameplay.Player
[Header("Touch Settings")]
[SerializeField] private float _touchDistance = 2f;
[Header("Input Action")]
[SerializeField] private InputActionReference _diapasonAction;
[SerializeField] private InputActionReference _fireAction;
public bool IsListening { get; private set; }
public bool IsConcentrating { get; private set; }
@@ -118,11 +114,6 @@ namespace Unheimlich.Gameplay.Player
{
_handTouchFeedback.SetActive(false);
}
if (_diapasonAction == null)
{
Debug.LogError("[Diapason] _diapasonAction is NOT assigned! Please assign it in the Inspector.");
}
}
private void Update()
@@ -132,7 +123,8 @@ namespace Unheimlich.Gameplay.Player
CheckForNearbyEmitter();
}
if (_diapasonAction != null && _diapasonAction.action.WasPressedThisFrame())
var diapasonAction = InputManager.GetDiapasonAction();
if (diapasonAction != null && diapasonAction.WasPressedThisFrame())
{
if (!IsListening && !IsConcentrating && _canUseDiapason)
{
@@ -419,7 +411,8 @@ namespace Unheimlich.Gameplay.Player
}
bool firePressed = false;
if (_fireAction != null && _fireAction.action != null && _fireAction.action.WasPressedThisFrame())
var fireAction = InputManager.GetFireAction();
if (fireAction != null && fireAction.WasPressedThisFrame())
{
firePressed = true;
}
@@ -49,7 +49,8 @@ namespace Unheimlich.Gameplay.Player
{
_ySpeed = _groundedGravity;
if (PlayerInputManager.Instance.JumpAction.action.WasPressedThisFrame())
var jumpAction = PlayerInputManager.GetJumpAction();
if (jumpAction != null && jumpAction.WasPressedThisFrame())
_ySpeed = _jumpForce;
}
else