Outsourcing of the camera pan system
This commit is contained in:
+24
-210
@@ -5,6 +5,8 @@ using Unheimlich.Audio;
|
||||
using Unheimlich.Core;
|
||||
using Unheimlich.Utility;
|
||||
using Unheimlich.Gameplay.Player;
|
||||
using Unheimlich.Gameplay.CameraControl;
|
||||
using Unheimlich.Data.CameraSettings;
|
||||
using InputManager = Unheimlich.Managers.PlayerInputManager;
|
||||
|
||||
namespace Unheimlich.Gameplay.Player
|
||||
@@ -14,7 +16,7 @@ namespace Unheimlich.Gameplay.Player
|
||||
/// Handles:
|
||||
/// - Equipping/unequipping the diapason with tween animations
|
||||
/// - Concentration phase (breathing sound and duration)
|
||||
/// - Listening mode with camera pan control
|
||||
/// - Listening mode with camera pan control (via CameraPanController)
|
||||
/// - Tapping the diapason on listenable objects
|
||||
/// - Visual feedback (vignette, cursor, particles)
|
||||
/// - Preventing multiple taps on the same emitter
|
||||
@@ -27,7 +29,8 @@ namespace Unheimlich.Gameplay.Player
|
||||
[Header("References")]
|
||||
[SerializeField] private GameObject _diapasonObject;
|
||||
[SerializeField] private Animator _diapasonAnimator;
|
||||
[SerializeField] private Camera _playerCamera;
|
||||
[SerializeField] private UnityEngine.Camera _playerCamera;
|
||||
[SerializeField] private CameraPanController _cameraPanController;
|
||||
|
||||
[Header("Listening Settings")]
|
||||
[SerializeField] private float _listeningRange = 5f;
|
||||
@@ -41,13 +44,6 @@ namespace Unheimlich.Gameplay.Player
|
||||
[SerializeField] private AnimationCurve _diapasonTweenCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
|
||||
[SerializeField] private float _vibrationDuration = 15f;
|
||||
|
||||
[Header("Camera Settings")]
|
||||
[SerializeField] private float _cameraPanSpeed = 2f;
|
||||
[SerializeField] private float _cameraPanBorderSize = 0.1f;
|
||||
[SerializeField] private float _maxCameraPanAngle = 15f;
|
||||
[SerializeField] private float _cameraAlignmentSpeed = 3f;
|
||||
[SerializeField] private float _cameraAlignmentThreshold = 0.5f;
|
||||
|
||||
[Header("Visual Feedback")]
|
||||
[SerializeField] private float _vignetteIntensity = 0.5f;
|
||||
[SerializeField] private ListeningVignetteEffect _vignetteEffect;
|
||||
@@ -66,7 +62,6 @@ namespace Unheimlich.Gameplay.Player
|
||||
|
||||
[Header("Input Action")]
|
||||
[SerializeField] private InputActionReference _diapasonAction;
|
||||
[SerializeField] private InputActionReference _lookAction;
|
||||
[SerializeField] private InputActionReference _fireAction;
|
||||
|
||||
public bool IsListening { get; private set; }
|
||||
@@ -78,17 +73,11 @@ namespace Unheimlich.Gameplay.Player
|
||||
private int _diapasonTweenId = -1;
|
||||
private int _audioListenerFadeTweenId = -1;
|
||||
private int _playerAudioFadeTweenId = -1;
|
||||
private Vector2 _originalCameraRotation;
|
||||
private float _originalAmbientVolume;
|
||||
private Vector3 _diapasonEquippedPosition;
|
||||
private Vector3 _diapasonHiddenPosition;
|
||||
private Vector3 _originalCameraLocalPosition;
|
||||
private Vector3 _originalPlayerPosition;
|
||||
private Vector3 _originalPlayerRotation;
|
||||
private Vector3 _originalCameraLocalRotation;
|
||||
private bool _canUseDiapason;
|
||||
private bool _isFadingOutConcentration;
|
||||
private Coroutine _returnCoroutine;
|
||||
private Coroutine _vibrationCoroutine;
|
||||
|
||||
protected override void OnAwake()
|
||||
@@ -97,15 +86,16 @@ namespace Unheimlich.Gameplay.Player
|
||||
if (_playerAudioSource == null)
|
||||
_playerAudioSource = gameObject.AddComponent<AudioSource>();
|
||||
|
||||
if (_playerCamera != null)
|
||||
if (_cameraPanController == null)
|
||||
{
|
||||
_originalCameraLocalPosition = _playerCamera.transform.localPosition;
|
||||
_originalCameraLocalRotation = _playerCamera.transform.localEulerAngles;
|
||||
_cameraPanController = GetComponent<CameraPanController>();
|
||||
if (_cameraPanController == null)
|
||||
{
|
||||
Debug.LogWarning("[Diapason] CameraPanController not found! Adding one automatically.");
|
||||
_cameraPanController = gameObject.AddComponent<CameraPanController>();
|
||||
}
|
||||
}
|
||||
|
||||
_originalPlayerPosition = transform.position;
|
||||
_originalPlayerRotation = transform.eulerAngles;
|
||||
|
||||
if (_diapasonObject != null)
|
||||
{
|
||||
_diapasonEquippedPosition = _diapasonObject.transform.localPosition;
|
||||
@@ -154,9 +144,9 @@ namespace Unheimlich.Gameplay.Player
|
||||
}
|
||||
}
|
||||
|
||||
if (IsListening)
|
||||
if (IsListening && _cameraPanController != null)
|
||||
{
|
||||
UpdateCursorAndCameraPan();
|
||||
_cameraPanController.UpdatePan();
|
||||
CheckForListenableObject();
|
||||
}
|
||||
}
|
||||
@@ -252,11 +242,12 @@ namespace Unheimlich.Gameplay.Player
|
||||
IsConcentrating = true;
|
||||
InputManager.SetContext(InputManager.InputContext.Listening);
|
||||
|
||||
if (_currentTargetEmitter != null)
|
||||
if (_currentTargetEmitter != null && _cameraPanController != null)
|
||||
{
|
||||
_currentTargetEmitter.SetConcentrating(true);
|
||||
|
||||
yield return StartCoroutine(AlignCameraToTarget(_currentTargetEmitter.transform));
|
||||
_cameraPanController.AlignToTarget(_currentTargetEmitter.transform);
|
||||
yield return new WaitForSeconds(1f / 3f);
|
||||
}
|
||||
|
||||
if (_diapasonObject != null)
|
||||
@@ -288,11 +279,10 @@ namespace Unheimlich.Gameplay.Player
|
||||
|
||||
EnableVignette(true);
|
||||
|
||||
float currentPitch = _playerCamera.transform.localEulerAngles.x;
|
||||
if (currentPitch > 180f)
|
||||
currentPitch -= 360f;
|
||||
|
||||
_originalCameraRotation = new Vector2(transform.eulerAngles.y, currentPitch);
|
||||
if (_cameraPanController != null)
|
||||
{
|
||||
_cameraPanController.StartPan();
|
||||
}
|
||||
|
||||
Cursor.lockState = CursorLockMode.Confined;
|
||||
Cursor.visible = false;
|
||||
@@ -325,11 +315,11 @@ namespace Unheimlich.Gameplay.Player
|
||||
IsListening = false;
|
||||
IsConcentrating = false;
|
||||
|
||||
if (_returnCoroutine != null)
|
||||
if (_cameraPanController != null)
|
||||
{
|
||||
StopCoroutine(_returnCoroutine);
|
||||
_cameraPanController.StopPan();
|
||||
_cameraPanController.ReturnToOriginalPosition();
|
||||
}
|
||||
_returnCoroutine = StartCoroutine(ReturnToOriginalPosition());
|
||||
|
||||
if (_playerAudioSource != null && _playerAudioSource.isPlaying)
|
||||
{
|
||||
@@ -403,182 +393,6 @@ namespace Unheimlich.Gameplay.Player
|
||||
_isFadingOutConcentration = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aligns the player and camera to the target object:
|
||||
/// - Player moves along target's local X axis to align with target center
|
||||
/// - Player rotates to face the target
|
||||
/// - Camera pitch adjusts to look at target center
|
||||
/// </summary>
|
||||
private IEnumerator AlignCameraToTarget(Transform target)
|
||||
{
|
||||
if (_playerCamera == null || target == null)
|
||||
yield break;
|
||||
|
||||
_originalCameraLocalPosition = _playerCamera.transform.localPosition;
|
||||
_originalCameraLocalRotation = _playerCamera.transform.localEulerAngles;
|
||||
_originalPlayerPosition = transform.position;
|
||||
_originalPlayerRotation = transform.eulerAngles;
|
||||
|
||||
Vector3 targetCenter = target.position;
|
||||
Renderer targetRenderer = target.GetComponent<Renderer>();
|
||||
if (targetRenderer != null)
|
||||
{
|
||||
targetCenter = targetRenderer.bounds.center;
|
||||
}
|
||||
else
|
||||
{
|
||||
Collider targetCollider = target.GetComponent<Collider>();
|
||||
if (targetCollider != null)
|
||||
targetCenter = targetCollider.bounds.center;
|
||||
}
|
||||
|
||||
Vector3 startPlayerPos = transform.position;
|
||||
Vector3 startPlayerRotation = transform.eulerAngles;
|
||||
Vector3 startCameraLocalRotation = _playerCamera.transform.localEulerAngles;
|
||||
|
||||
float startPitch = startCameraLocalRotation.x;
|
||||
if (startPitch > 180f)
|
||||
startPitch -= 360f;
|
||||
|
||||
float elapsed = 0f;
|
||||
float duration = 1f / _cameraAlignmentSpeed;
|
||||
|
||||
while (elapsed < duration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = Mathf.SmoothStep(0f, 1f, elapsed / duration);
|
||||
|
||||
Vector3 currentCameraPos = _playerCamera.transform.position;
|
||||
Vector3 toTarget = targetCenter - currentCameraPos;
|
||||
Vector3 directionToTarget = toTarget.normalized;
|
||||
|
||||
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget);
|
||||
float targetYaw = targetRotation.eulerAngles.y;
|
||||
float targetPitch = -Mathf.Asin(directionToTarget.y) * Mathf.Rad2Deg;
|
||||
|
||||
Vector3 targetRightAxis = target.right;
|
||||
Vector3 playerToTargetCenter = targetCenter - transform.position;
|
||||
float offsetAlongTargetRight = Vector3.Dot(playerToTargetCenter, targetRightAxis);
|
||||
Vector3 targetPlayerPos = transform.position + targetRightAxis * offsetAlongTargetRight;
|
||||
|
||||
Vector3 currentPlayerPos = Vector3.Lerp(startPlayerPos, targetPlayerPos, t);
|
||||
transform.position = currentPlayerPos;
|
||||
|
||||
float currentYaw = Mathf.LerpAngle(startPlayerRotation.y, targetYaw, t);
|
||||
transform.eulerAngles = new Vector3(0f, currentYaw, 0f);
|
||||
|
||||
float currentPitch = Mathf.LerpAngle(startPitch, targetPitch, t);
|
||||
_playerCamera.transform.localEulerAngles = new Vector3(currentPitch, 0f, 0f);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns player and camera to original position and rotation with smooth lerp.
|
||||
/// </summary>
|
||||
private IEnumerator ReturnToOriginalPosition()
|
||||
{
|
||||
if (_playerCamera == null)
|
||||
yield break;
|
||||
|
||||
Vector3 startPlayerPos = transform.position;
|
||||
Vector3 startPlayerRotation = transform.eulerAngles;
|
||||
Vector3 startCameraLocalPos = _playerCamera.transform.localPosition;
|
||||
Vector3 startCameraLocalRotation = _playerCamera.transform.localEulerAngles;
|
||||
|
||||
float startPitch = startCameraLocalRotation.x;
|
||||
if (startPitch > 180f)
|
||||
startPitch -= 360f;
|
||||
|
||||
float targetPitch = _originalCameraLocalRotation.x;
|
||||
if (targetPitch > 180f)
|
||||
targetPitch -= 360f;
|
||||
|
||||
float elapsed = 0f;
|
||||
float duration = 1f / _cameraAlignmentSpeed;
|
||||
|
||||
while (elapsed < duration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = Mathf.SmoothStep(0f, 1f, elapsed / duration);
|
||||
|
||||
transform.position = Vector3.Lerp(startPlayerPos, _originalPlayerPosition, t);
|
||||
|
||||
float currentYaw = Mathf.LerpAngle(startPlayerRotation.y, _originalPlayerRotation.y, t);
|
||||
transform.eulerAngles = new Vector3(0f, currentYaw, 0f);
|
||||
|
||||
float currentPitch = Mathf.LerpAngle(startPitch, targetPitch, t);
|
||||
_playerCamera.transform.localEulerAngles = new Vector3(currentPitch, 0f, 0f);
|
||||
|
||||
_playerCamera.transform.localPosition = Vector3.Lerp(startCameraLocalPos, _originalCameraLocalPosition, t);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
transform.position = _originalPlayerPosition;
|
||||
transform.eulerAngles = _originalPlayerRotation;
|
||||
_playerCamera.transform.localPosition = _originalCameraLocalPosition;
|
||||
_playerCamera.transform.localEulerAngles = _originalCameraLocalRotation;
|
||||
|
||||
_returnCoroutine = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates cursor position and camera panning during listening mode.
|
||||
/// Supports both gamepad (look input) and mouse (screen edges).
|
||||
/// </summary>
|
||||
private void UpdateCursorAndCameraPan()
|
||||
{
|
||||
if (_playerCamera == null)
|
||||
return;
|
||||
|
||||
float panX = 0f;
|
||||
float panY = 0f;
|
||||
|
||||
if (_lookAction != null && _lookAction.action != null)
|
||||
{
|
||||
Vector2 lookInput = _lookAction.action.ReadValue<Vector2>();
|
||||
if (lookInput.sqrMagnitude > 0.01f)
|
||||
{
|
||||
panX = lookInput.x;
|
||||
panY = lookInput.y;
|
||||
}
|
||||
}
|
||||
|
||||
if (panX == 0f && panY == 0f && Mouse.current != null)
|
||||
{
|
||||
Vector2 viewportPos = _playerCamera.ScreenToViewportPoint(Mouse.current.position.ReadValue());
|
||||
|
||||
if (viewportPos.x < _cameraPanBorderSize)
|
||||
panX = -1f;
|
||||
else if (viewportPos.x > 1f - _cameraPanBorderSize)
|
||||
panX = 1f;
|
||||
|
||||
if (viewportPos.y < _cameraPanBorderSize)
|
||||
panY = -1f;
|
||||
else if (viewportPos.y > 1f - _cameraPanBorderSize)
|
||||
panY = 1f;
|
||||
}
|
||||
|
||||
float targetYaw = _originalCameraRotation.x + (panX * _maxCameraPanAngle);
|
||||
float targetPitch = _originalCameraRotation.y - (panY * _maxCameraPanAngle);
|
||||
|
||||
targetYaw = Mathf.Clamp(targetYaw, _originalCameraRotation.x - _maxCameraPanAngle, _originalCameraRotation.x + _maxCameraPanAngle);
|
||||
targetPitch = Mathf.Clamp(targetPitch, _originalCameraRotation.y - _maxCameraPanAngle, _originalCameraRotation.y + _maxCameraPanAngle);
|
||||
targetPitch = Mathf.Clamp(targetPitch, -89f, 89f);
|
||||
|
||||
Vector3 currentRotation = transform.eulerAngles;
|
||||
currentRotation.y = Mathf.LerpAngle(currentRotation.y, targetYaw, Time.deltaTime * _cameraPanSpeed);
|
||||
transform.eulerAngles = currentRotation;
|
||||
|
||||
Vector3 cameraRotation = _playerCamera.transform.localEulerAngles;
|
||||
if (cameraRotation.x > 180f)
|
||||
cameraRotation.x -= 360f;
|
||||
cameraRotation.x = Mathf.LerpAngle(cameraRotation.x, targetPitch, Time.deltaTime * _cameraPanSpeed);
|
||||
_playerCamera.transform.localEulerAngles = cameraRotation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raycasts from camera center to detect listenable objects.
|
||||
/// Handles tap input and prevents tapping the same emitter multiple times.
|
||||
|
||||
Reference in New Issue
Block a user