From df1377206a0de832f17698fcd2561599c130bf68 Mon Sep 17 00:00:00 2001
From: Scove <104053980+Scove-Metalreal@users.noreply.github.com>
Date: Fri, 27 Mar 2026 12:27:50 +0700
Subject: [PATCH] Update
---
.idea/.idea.HALLUCINATE/.idea/workspace.xml | 8 +-
Assets/InputSystem_Actions.inputactions | 33 +++-
Assets/Prefabs/Player.prefab | 41 ++++-
Assets/Scove/Player Movement.unity | 49 ++++++
.../Camera Controller/CameraController.cs | 160 +++++++++++++++---
.../Camera Controller/CameraDynamicFOV.cs | 24 ++-
.../CameraRotationHandler.cs | 6 +-
7 files changed, 280 insertions(+), 41 deletions(-)
diff --git a/.idea/.idea.HALLUCINATE/.idea/workspace.xml b/.idea/.idea.HALLUCINATE/.idea/workspace.xml
index c4965257..733f2115 100644
--- a/.idea/.idea.HALLUCINATE/.idea/workspace.xml
+++ b/.idea/.idea.HALLUCINATE/.idea/workspace.xml
@@ -7,12 +7,10 @@
-
-
-
-
+
+
@@ -105,7 +103,7 @@
1774531360140
-
+
diff --git a/Assets/InputSystem_Actions.inputactions b/Assets/InputSystem_Actions.inputactions
index 29ceeb76..c66e1e10 100644
--- a/Assets/InputSystem_Actions.inputactions
+++ b/Assets/InputSystem_Actions.inputactions
@@ -82,7 +82,7 @@
"name": "Sprint",
"type": "Button",
"id": "641cd816-40e6-41b4-8c3d-04687c349290",
- "expectedControlType": "Button",
+ "expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false
@@ -95,6 +95,15 @@
"processors": "",
"interactions": "",
"initialStateCheck": true
+ },
+ {
+ "name": "ToggleView",
+ "type": "Button",
+ "id": "7e8b9416-0a2d-4652-98d8-e7368560ede9",
+ "expectedControlType": "",
+ "processors": "",
+ "interactions": "",
+ "initialStateCheck": false
}
],
"bindings": [
@@ -515,6 +524,28 @@
"action": "Scroll",
"isComposite": false,
"isPartOfComposite": false
+ },
+ {
+ "name": "",
+ "id": "f365517f-5a04-4d18-afa0-369ab80e0c54",
+ "path": "/f2",
+ "interactions": "",
+ "processors": "",
+ "groups": "",
+ "action": "ToggleView",
+ "isComposite": false,
+ "isPartOfComposite": false
+ },
+ {
+ "name": "",
+ "id": "048282c5-5a5b-4ab2-9777-a20889af4532",
+ "path": "/buttonEast",
+ "interactions": "",
+ "processors": "",
+ "groups": "",
+ "action": "ToggleView",
+ "isComposite": false,
+ "isPartOfComposite": false
}
]
},
diff --git a/Assets/Prefabs/Player.prefab b/Assets/Prefabs/Player.prefab
index bbd13b41..645c9e23 100644
--- a/Assets/Prefabs/Player.prefab
+++ b/Assets/Prefabs/Player.prefab
@@ -446,6 +446,37 @@ MonoBehaviour:
shrineRiseHeight: 5
shrineFloorOffset: 0.5
camMoveSpeed: 4
+--- !u!1 &3751838835891881608
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 5188652905305800431}
+ m_Layer: 0
+ m_Name: FPV Camera
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &5188652905305800431
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3751838835891881608}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 7173064969670908494}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &4262004705141875331
GameObject:
m_ObjectHideFlags: 0
@@ -756,7 +787,10 @@ PrefabInstance:
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
- m_AddedGameObjects: []
+ m_AddedGameObjects:
+ - targetCorrespondingSourceObject: {fileID: -3765236089850875182, guid: 5847774ba45dc754598435b50d4a0247, type: 3}
+ insertIndex: -1
+ addedObject: {fileID: 5188652905305800431}
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 5847774ba45dc754598435b50d4a0247, type: 3}
--- !u!4 &3440275474486398839 stripped
@@ -764,3 +798,8 @@ Transform:
m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: 5847774ba45dc754598435b50d4a0247, type: 3}
m_PrefabInstance: {fileID: 2897196837322451100}
m_PrefabAsset: {fileID: 0}
+--- !u!4 &7173064969670908494 stripped
+Transform:
+ m_CorrespondingSourceObject: {fileID: -3765236089850875182, guid: 5847774ba45dc754598435b50d4a0247, type: 3}
+ m_PrefabInstance: {fileID: 2897196837322451100}
+ m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Scove/Player Movement.unity b/Assets/Scove/Player Movement.unity
index bb6b2f40..3418ab52 100644
--- a/Assets/Scove/Player Movement.unity
+++ b/Assets/Scove/Player Movement.unity
@@ -689,6 +689,11 @@ Transform:
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &1631120432 stripped
+Transform:
+ m_CorrespondingSourceObject: {fileID: 5188652905305800431, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3}
+ m_PrefabInstance: {fileID: 8240317044381527393}
+ m_PrefabAsset: {fileID: 0}
--- !u!4 &1732205146 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 8004958684693924044, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3}
@@ -954,6 +959,10 @@ PrefabInstance:
propertyPath: m_Layer
value: 8
objectReference: {fileID: 0}
+ - target: {fileID: 3657229949309460766, guid: fb7874830b9e56341bf88f2a1123c677, type: 3}
+ propertyPath: fpvTarget
+ value:
+ objectReference: {fileID: 1631120432}
- target: {fileID: 3657229949309460766, guid: fb7874830b9e56341bf88f2a1123c677, type: 3}
propertyPath: inputReader
value:
@@ -1015,6 +1024,46 @@ PrefabInstance:
propertyPath: m_Camera
value:
objectReference: {fileID: 442028704}
+ - target: {fileID: 3010251870038942475, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3}
+ propertyPath: m_ActionEvents.Array.size
+ value: 21
+ objectReference: {fileID: 0}
+ - target: {fileID: 3010251870038942475, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3}
+ propertyPath: m_ActionEvents.Array.data[20].m_ActionId
+ value: 7e8b9416-0a2d-4652-98d8-e7368560ede9
+ objectReference: {fileID: 0}
+ - target: {fileID: 3010251870038942475, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3}
+ propertyPath: m_ActionEvents.Array.data[20].m_ActionName
+ value: 'Player/Change View[/Keyboard/f2]'
+ objectReference: {fileID: 0}
+ - target: {fileID: 3010251870038942475, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3}
+ propertyPath: m_ActionEvents.Array.data[20].m_PersistentCalls.m_Calls.Array.size
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 3010251870038942475, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3}
+ propertyPath: m_ActionEvents.Array.data[20].m_PersistentCalls.m_Calls.Array.data[0].m_Mode
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 3010251870038942475, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3}
+ propertyPath: m_ActionEvents.Array.data[20].m_PersistentCalls.m_Calls.Array.data[0].m_Target
+ value:
+ objectReference: {fileID: 216247156}
+ - target: {fileID: 3010251870038942475, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3}
+ propertyPath: m_ActionEvents.Array.data[20].m_PersistentCalls.m_Calls.Array.data[0].m_CallState
+ value: 2
+ objectReference: {fileID: 0}
+ - target: {fileID: 3010251870038942475, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3}
+ propertyPath: m_ActionEvents.Array.data[20].m_PersistentCalls.m_Calls.Array.data[0].m_MethodName
+ value: OnToggleView
+ objectReference: {fileID: 0}
+ - target: {fileID: 3010251870038942475, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3}
+ propertyPath: m_ActionEvents.Array.data[20].m_PersistentCalls.m_Calls.Array.data[0].m_TargetAssemblyTypeName
+ value: OnlyScove.Scripts.InputReader, Assembly-CSharp
+ objectReference: {fileID: 0}
+ - target: {fileID: 3010251870038942475, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3}
+ propertyPath: m_ActionEvents.Array.data[20].m_PersistentCalls.m_Calls.Array.data[0].m_Arguments.m_ObjectArgumentAssemblyTypeName
+ value: UnityEngine.Object, UnityEngine
+ objectReference: {fileID: 0}
- target: {fileID: 3154409663696148700, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3}
propertyPath: m_LocalPosition.x
value: -5
diff --git a/Assets/Scripts/Camera Controller/CameraController.cs b/Assets/Scripts/Camera Controller/CameraController.cs
index bf1e2936..a34581ea 100644
--- a/Assets/Scripts/Camera Controller/CameraController.cs
+++ b/Assets/Scripts/Camera Controller/CameraController.cs
@@ -1,11 +1,14 @@
+using System; // For Action event
using UnityEngine;
namespace OnlyScove.Scripts
{
public class CameraController : MonoBehaviour
{
+ public enum CameraViewMode { ThirdPerson, FirstPerson }
+
[SerializeField] InputReader inputReader;
- [SerializeField] Transform followTarget;
+ [SerializeField] Transform followTarget; // Player's root for TPV
[SerializeField] float positionSmoothTime = 0.12f;
[SerializeField] float rotationSmoothTime = 5f;
[SerializeField] Vector2 framingOffset;
@@ -19,53 +22,160 @@ namespace OnlyScove.Scripts
[SerializeField] private CameraCharacterFading characterFading = new CameraCharacterFading();
[SerializeField] private CameraSideBias sideBias = new CameraSideBias();
[SerializeField] private CameraShakeManager shakeManager = new CameraShakeManager();
-
+
+ [Header("First Person View Settings")]
+ [SerializeField] Transform fpvTarget; // Specific transform on the player (e.g., eye level)
+ [SerializeField] float fpvPositionSmoothTime = 0.05f;
+ [SerializeField] float fpvRotationSmoothTime = 0.05f;
+ [SerializeField] float fpvFOV = 80f;
+ [SerializeField] float transitionDuration = 0.3f;
+ [SerializeField] float tpvBaseFOV = 60f; // Existing base FOV for TPV
+
private Vector3 _currentVelocity;
private Camera _cam;
+ private CameraViewMode _currentViewMode = CameraViewMode.ThirdPerson;
+ private CameraViewMode _targetViewMode = CameraViewMode.ThirdPerson;
+ private float _transitionTimer = 0f;
+ private bool _inTransition = false;
+
+ // Properties to get current smoothing values based on view mode
+ private float CurrentPositionSmoothTime => _currentViewMode == CameraViewMode.FirstPerson ? fpvPositionSmoothTime : positionSmoothTime;
+ private float CurrentRotationSmoothTime => _currentViewMode == CameraViewMode.FirstPerson ? fpvRotationSmoothTime : rotationSmoothTime;
+
+ private void OnEnable()
+ {
+ if (inputReader != null)
+ {
+ inputReader.OnToggleViewEvent += ToggleCameraView;
+ }
+ }
+
+ private void OnDisable()
+ {
+ if (inputReader != null)
+ {
+ inputReader.OnToggleViewEvent -= ToggleCameraView;
+ }
+ }
private void Start()
{
_cam = GetComponent();
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
-
+
rotationHandler.Initialize(transform);
+ dynamicFOV.Initialize(tpvBaseFOV: tpvBaseFOV, fpvFOV: fpvFOV); // Pass TPV and FPV base FOVs
}
private void Update()
{
- // Handle Input-related updates first
+ HandleViewTransition();
+
if (inputReader != null)
{
- rotationHandler.HandleRotation(inputReader, followTarget, rotationSmoothTime);
- zoomHandler.HandleZoom(inputReader);
- sideBias.HandleSideBias(inputReader);
- dynamicFOV.HandleDynamicFOV(_cam, inputReader);
+ // Input-related updates are handled differently based on view mode
+ rotationHandler.HandleRotation(inputReader, followTarget, CurrentRotationSmoothTime, _currentViewMode);
+
+ if (_currentViewMode == CameraViewMode.ThirdPerson)
+ {
+ zoomHandler.HandleZoom(inputReader);
+ sideBias.HandleSideBias(inputReader);
+ }
+ else
+ {
+ // Disable side bias and zoom in FPV
+ sideBias.HandleSideBias(null); // Pass null to effectively disable
+ zoomHandler.HandleZoom(null); // Pass null to effectively disable
+ }
+ dynamicFOV.HandleDynamicFOV(_cam, inputReader, _currentViewMode);
}
- // Update camera rotation
- transform.rotation = rotationHandler.CurrentRotation;
+ Vector3 focusPosition;
+ float targetDistance;
- // Calculate focus position with framing offset and side bias
- Vector3 focusPosition = followTarget.position + rotationHandler.CurrentRotation * new Vector3(framingOffset.x + sideBias.CurrentSideBias, framingOffset.y, 0);
-
- // Handle collision
- float targetDistance = collisionHandler.CheckCollision(focusPosition, rotationHandler.CurrentRotation, zoomHandler.CurrentDistance, zoomHandler.MinDistance);
+ if (_currentViewMode == CameraViewMode.ThirdPerson)
+ {
+ // TPV specific calculations
+ transform.rotation = rotationHandler.CurrentRotation; // Set rotation from handler
+ focusPosition = followTarget.position + rotationHandler.CurrentRotation * new Vector3(framingOffset.x + sideBias.CurrentSideBias, framingOffset.y, 0);
+ targetDistance = collisionHandler.CheckCollision(focusPosition, rotationHandler.CurrentRotation, zoomHandler.CurrentDistance, zoomHandler.MinDistance);
+ characterFading.HandleCharacterFading(targetDistance);
+ occlusionTransparency.HandleTransparency(transform, focusPosition);
+ }
+ else // FirstPerson
+ {
+ // FPV specific calculations
+ // Rotation is derived from fpvTarget, not input from rotationHandler
+ transform.rotation = fpvTarget.rotation; // Directly set FPV rotation
+ focusPosition = fpvTarget.position;
+ targetDistance = 0; // FPV has no distance to player
- // Handle character fading
- characterFading.HandleCharacterFading(targetDistance);
+ // Disable TPV-specific effects
+ characterFading.HandleCharacterFading(0); // Fully opaque character in FPV
+ occlusionTransparency.HandleTransparency(transform, fpvTarget.position); // Can still have occlusion transparency for environment in FPV
+ }
+
+ // Calculate target position using the currently set transform.rotation
+ Vector3 targetPosition = focusPosition - transform.rotation * new Vector3(0, 0, targetDistance);
- // Calculate target position
- Vector3 targetPosition = focusPosition - rotationHandler.CurrentRotation * new Vector3(0, 0, targetDistance);
-
// Handle camera shake
shakeManager.HandleShake();
-
+
// Apply final position and rotation
- transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref _currentVelocity, positionSmoothTime) + shakeManager.ShakeOffset;
-
- // Handle occlusion transparency (needs camera's final position and rotation)
- occlusionTransparency.HandleTransparency(transform, focusPosition);
+ transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref _currentVelocity, CurrentPositionSmoothTime) + shakeManager.ShakeOffset;
+ }
+
+ private void ToggleCameraView()
+ {
+ if (_inTransition) return; // Prevent multiple toggles during transition
+
+ _targetViewMode = (_currentViewMode == CameraViewMode.ThirdPerson) ? CameraViewMode.FirstPerson : CameraViewMode.ThirdPerson;
+ _inTransition = true;
+ _transitionTimer = 0f;
+ }
+
+ private void HandleViewTransition()
+ {
+ if (!_inTransition) return;
+
+ _transitionTimer += Time.deltaTime;
+ float t = _transitionTimer / transitionDuration;
+ t = Mathf.Clamp01(t); // Clamp t between 0 and 1
+
+ // Smoothly interpolate parameters during transition
+ if (_currentViewMode == CameraViewMode.ThirdPerson && _targetViewMode == CameraViewMode.FirstPerson)
+ {
+ // TPV -> FPV transition
+ // Interpolate FOV
+ _cam.fieldOfView = Mathf.Lerp(dynamicFOV.CurrentTpvBaseFOV, fpvFOV, t);
+ // Interpolate position and rotation
+ transform.position = Vector3.Lerp(transform.position, fpvTarget.position, t);
+ transform.rotation = Quaternion.Slerp(transform.rotation, fpvTarget.rotation, t);
+ }
+ else if (_currentViewMode == CameraViewMode.FirstPerson && _targetViewMode == CameraViewMode.ThirdPerson)
+ {
+ // FPV -> TPV transition
+ // Interpolate FOV
+ _cam.fieldOfView = Mathf.Lerp(fpvFOV, dynamicFOV.CurrentTpvBaseFOV, t);
+ // For position and rotation, we'll let the TPV logic take over after transition.
+ // If a smooth exit from FPV to TPV is desired for position/rotation,
+ // more complex calculations would be needed here to store TPV target position/rotation at the start.
+ }
+
+ if (t >= 1f)
+ {
+ _currentViewMode = _targetViewMode;
+ _inTransition = false;
+ // Re-initialize rotation handler for new view mode context
+ rotationHandler.Initialize(transform);
+
+ // Ensure FOV is set correctly at the end of transition
+ _cam.fieldOfView = (_currentViewMode == CameraViewMode.FirstPerson) ? fpvFOV : dynamicFOV.CurrentTpvBaseFOV;
+
+ // For FPV, ensure camera matches fpvTarget immediately after transition
+ // This is already handled by the `transform.position = Vector3.SmoothDamp...` and `transform.rotation = fpvTarget.rotation` in Update
+ }
}
public void Shake(float intensity, float duration)
diff --git a/Assets/Scripts/Camera Controller/CameraDynamicFOV.cs b/Assets/Scripts/Camera Controller/CameraDynamicFOV.cs
index 35a9a6bd..5a8f6143 100644
--- a/Assets/Scripts/Camera Controller/CameraDynamicFOV.cs
+++ b/Assets/Scripts/Camera Controller/CameraDynamicFOV.cs
@@ -1,4 +1,5 @@
using UnityEngine;
+using static OnlyScove.Scripts.CameraController; // Need to add this to access CameraController.CameraViewMode
namespace OnlyScove.Scripts
{
@@ -7,19 +8,30 @@ namespace OnlyScove.Scripts
{
[Header("Dynamic FOV")]
[SerializeField] private bool useDynamicFOV = true;
- [SerializeField] private float baseFOV = 60f;
- [SerializeField] private float sprintFOV = 70f;
+ [SerializeField] private float tpvSprintFOV = 70f; // Renamed from sprintFOV for clarity
[SerializeField] private float fovSmoothTime = 5f;
- public void HandleDynamicFOV(Camera cam, InputReader inputReader)
+ private float _currentTpvBaseFOV; // Stored from CameraController
+ private float _currentFpvFOV; // Stored from CameraController
+
+ public float CurrentTpvBaseFOV => _currentTpvBaseFOV; // Expose for CameraController transitions
+
+ public void Initialize(float tpvBaseFOV, float fpvFOV)
+ {
+ _currentTpvBaseFOV = tpvBaseFOV;
+ _currentFpvFOV = fpvFOV;
+ }
+
+ public void HandleDynamicFOV(Camera cam, InputReader inputReader, CameraViewMode viewMode)
{
if (!useDynamicFOV || cam == null || inputReader == null) return;
- float targetFOV = baseFOV;
+ float targetFOV = (viewMode == CameraViewMode.ThirdPerson) ? _currentTpvBaseFOV : _currentFpvFOV;
- if (inputReader.MoveInput.magnitude > 0.1f && inputReader.IsSprintHeld)
+ // Apply sprint FOV only in TPV
+ if (viewMode == CameraViewMode.ThirdPerson && inputReader.MoveInput.magnitude > 0.1f && inputReader.IsSprintHeld)
{
- targetFOV = sprintFOV;
+ targetFOV = tpvSprintFOV;
}
cam.fieldOfView = Mathf.Lerp(cam.fieldOfView, targetFOV, fovSmoothTime * Time.deltaTime);
diff --git a/Assets/Scripts/Camera Controller/CameraRotationHandler.cs b/Assets/Scripts/Camera Controller/CameraRotationHandler.cs
index b063803e..473ba957 100644
--- a/Assets/Scripts/Camera Controller/CameraRotationHandler.cs
+++ b/Assets/Scripts/Camera Controller/CameraRotationHandler.cs
@@ -1,4 +1,5 @@
using UnityEngine;
+using static OnlyScove.Scripts.CameraController;
namespace OnlyScove.Scripts
{
@@ -32,9 +33,9 @@ namespace OnlyScove.Scripts
CurrentRotation = cameraTransform.rotation;
}
- public void HandleRotation(InputReader inputReader, Transform followTarget, float rotationSmoothTime)
+ public void HandleRotation(InputReader inputReader, Transform followTarget, float rotationSmoothTime, CameraViewMode viewMode)
{
- if (inputReader == null) return;
+ if (inputReader == null || viewMode == CameraViewMode.FirstPerson) return; // Only process input in TPV
if (inputReader.LookInput.magnitude > 0.01f)
{
@@ -58,7 +59,6 @@ namespace OnlyScove.Scripts
_rotationY = Mathf.LerpAngle(_rotationY, targetYaw, autoRotateSpeed * Time.deltaTime);
}
}
-
Quaternion targetRotation = Quaternion.Euler(_rotationX, _rotationY, 0f);
CurrentRotation = Quaternion.Slerp(CurrentRotation, targetRotation, rotationSmoothTime * Time.deltaTime);
}