Update
This commit is contained in:
31
Assets/Scripts/Camera Controller/CameraCharacterFading.cs
Normal file
31
Assets/Scripts/Camera Controller/CameraCharacterFading.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
[System.Serializable]
|
||||
public class CameraCharacterFading
|
||||
{
|
||||
[Header("Character Fading")]
|
||||
[SerializeField] private bool useCharacterFading = true;
|
||||
[SerializeField] private float minVisibleDistance = 1.2f;
|
||||
[SerializeField] private float fullyHiddenDistance = 0.6f;
|
||||
[SerializeField] private Renderer[] characterRenderers;
|
||||
|
||||
public void HandleCharacterFading(float currentDistance)
|
||||
{
|
||||
if (!useCharacterFading || characterRenderers == null || characterRenderers.Length == 0) return;
|
||||
|
||||
float alpha = Mathf.InverseLerp(fullyHiddenDistance, minVisibleDistance, currentDistance);
|
||||
|
||||
foreach (var renderer in characterRenderers)
|
||||
{
|
||||
if (renderer != null)
|
||||
{
|
||||
Color color = renderer.material.color;
|
||||
color.a = alpha;
|
||||
renderer.material.color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d5d8d2fa005cd34a92ee259df535130
|
||||
24
Assets/Scripts/Camera Controller/CameraCollisionHandler.cs
Normal file
24
Assets/Scripts/Camera Controller/CameraCollisionHandler.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
[System.Serializable]
|
||||
public class CameraCollisionHandler
|
||||
{
|
||||
[Header("Collision Settings")]
|
||||
[SerializeField] private LayerMask collisionLayers;
|
||||
[SerializeField] private float cameraRadius = 0.2f;
|
||||
public float CheckCollision(Vector3 focusPosition, Quaternion currentRotation, float targetDistance, float minimumDistanceAllowed)
|
||||
{
|
||||
RaycastHit hit;
|
||||
Vector3 rayStart = focusPosition;
|
||||
Vector3 rayDirection = currentRotation * Vector3.back;
|
||||
|
||||
if (Physics.SphereCast(rayStart, cameraRadius, rayDirection, out hit, targetDistance, collisionLayers))
|
||||
{
|
||||
return Mathf.Max(minimumDistanceAllowed, hit.distance - 0.1f);
|
||||
}
|
||||
return targetDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57a506759df838e42b577ebdb542500b
|
||||
@@ -1,287 +1,83 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class CameraController : MonoBehaviour
|
||||
{
|
||||
[SerializeField] InputReader inputReader; // Kéo thả Object chứa InputReader vào đây
|
||||
[SerializeField] InputReader inputReader;
|
||||
[SerializeField] Transform followTarget;
|
||||
[SerializeField] float distance = 5;
|
||||
[SerializeField] float minDistance = 2f;
|
||||
[SerializeField] float maxDistance = 15f;
|
||||
[SerializeField] float zoomSensitivity = 1f;
|
||||
[SerializeField] float sensitivity = 0.1f; // Độ nhạy (chỉnh trong Inspector)
|
||||
[SerializeField] LayerMask collisionLayers;
|
||||
[SerializeField] float cameraRadius = 0.2f;
|
||||
[SerializeField] float positionSmoothTime = 0.12f; // Độ trễ đuổi theo nhân vật
|
||||
[SerializeField] float rotationSmoothTime = 5f; // Tốc độ làm mượt vòng xoay chuột
|
||||
|
||||
[Header("Auto Rotation")]
|
||||
[SerializeField] bool useAutoRotation = true;
|
||||
[SerializeField] float autoRotateDelay = 2.5f; // Sau bao lâu không chạm chuột thì xoay
|
||||
[SerializeField] float autoRotateSpeed = 2f; // Tốc độ xoay về sau lưng
|
||||
|
||||
[Header("Occlusion Transparency")]
|
||||
[SerializeField] bool useTransparency = true;
|
||||
[SerializeField] LayerMask transparencyLayers;
|
||||
[SerializeField] float fadeAlpha = 0.3f; // Độ trong suốt (0 là biến mất, 1 là hiện rõ)
|
||||
|
||||
[Header("Dynamic FOV")]
|
||||
[SerializeField] bool useDynamicFOV = true;
|
||||
[SerializeField] float baseFOV = 60f;
|
||||
[SerializeField] float sprintFOV = 70f;
|
||||
[SerializeField] float fovSmoothTime = 5f;
|
||||
|
||||
[Header("Character Fading")]
|
||||
[SerializeField] bool useCharacterFading = true;
|
||||
[SerializeField] float minVisibleDistance = 1.2f; // Khoảng cách bắt đầu mờ
|
||||
[SerializeField] float fullyHiddenDistance = 0.6f; // Khoảng cách biến mất hẳn
|
||||
[SerializeField] Renderer[] characterRenderers; // Kéo các Mesh của nhân vật vào đây
|
||||
|
||||
[Header("Side Bias")]
|
||||
[SerializeField] bool useSideBias = true;
|
||||
[SerializeField] float horizontalBiasAmount = 0.5f; // Độ lệch sang trái/phải
|
||||
[SerializeField] float biasSmoothTime = 3f; // Tốc độ chuyển đổi độ lệch
|
||||
|
||||
[Header("Camera Shake")]
|
||||
[SerializeField] bool useShake = true;
|
||||
private float shakeIntensity = 0f;
|
||||
private float shakeDuration = 0f;
|
||||
private float shakeTimer = 0f;
|
||||
private Vector3 shakeOffset;
|
||||
|
||||
[SerializeField] float minVerticalAngle = -45f;
|
||||
[SerializeField] float maxVerticalAngle = 45f;
|
||||
[SerializeField] float positionSmoothTime = 0.12f;
|
||||
[SerializeField] float rotationSmoothTime = 5f;
|
||||
[SerializeField] Vector2 framingOffset;
|
||||
[SerializeField] private bool invertX;
|
||||
[SerializeField] private bool invertY;
|
||||
|
||||
private float rotationX;
|
||||
private float rotationY;
|
||||
|
||||
private float invertXVal;
|
||||
private float invertYVal;
|
||||
[Header("Components")]
|
||||
[SerializeField] private CameraRotationHandler rotationHandler = new CameraRotationHandler();
|
||||
[SerializeField] private CameraZoomHandler zoomHandler = new CameraZoomHandler();
|
||||
[SerializeField] private CameraCollisionHandler collisionHandler = new CameraCollisionHandler();
|
||||
[SerializeField] private CameraOcclusionTransparency occlusionTransparency = new CameraOcclusionTransparency();
|
||||
[SerializeField] private CameraDynamicFOV dynamicFOV = new CameraDynamicFOV();
|
||||
[SerializeField] private CameraCharacterFading characterFading = new CameraCharacterFading();
|
||||
[SerializeField] private CameraSideBias sideBias = new CameraSideBias();
|
||||
[SerializeField] private CameraShakeManager shakeManager = new CameraShakeManager();
|
||||
|
||||
private float lastInputTime;
|
||||
private Vector3 currentVelocity;
|
||||
private Quaternion currentRotation;
|
||||
|
||||
private Camera cam;
|
||||
private Renderer lastFadedRenderer;
|
||||
private Color originalColor;
|
||||
|
||||
private float currentSideBias;
|
||||
private Vector3 _currentVelocity;
|
||||
private Camera _cam;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
cam = GetComponent<Camera>();
|
||||
_cam = GetComponent<Camera>();
|
||||
Cursor.visible = false;
|
||||
Cursor.lockState = CursorLockMode.Locked;
|
||||
|
||||
// Khởi tạo vòng xoay hiện tại
|
||||
rotationX = transform.eulerAngles.x;
|
||||
rotationY = transform.eulerAngles.y;
|
||||
currentRotation = transform.rotation;
|
||||
lastInputTime = Time.time;
|
||||
rotationHandler.Initialize(transform);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// Handle Input-related updates first
|
||||
if (inputReader != null)
|
||||
{
|
||||
// Kiểm tra xem có input xoay chuột không
|
||||
if (inputReader.LookInput.magnitude > 0.01f)
|
||||
{
|
||||
lastInputTime = Time.time;
|
||||
}
|
||||
|
||||
invertXVal = (invertX) ? -1 : 1;
|
||||
invertYVal = (invertY) ? -1 : 1;
|
||||
|
||||
rotationX -= inputReader.LookInput.y * invertYVal * sensitivity * Time.deltaTime;
|
||||
rotationX = Mathf.Clamp(rotationX, minVerticalAngle, maxVerticalAngle);
|
||||
|
||||
rotationY += inputReader.LookInput.x * invertXVal * sensitivity * Time.deltaTime;
|
||||
|
||||
// Logic Side Bias (Lệch khung hình khi di chuyển)
|
||||
if (useSideBias)
|
||||
{
|
||||
float targetBias = -inputReader.MoveInput.x * horizontalBiasAmount;
|
||||
currentSideBias = Mathf.Lerp(currentSideBias, targetBias, biasSmoothTime * Time.deltaTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentSideBias = 0;
|
||||
}
|
||||
|
||||
// Logic Tự động xoay sau lưng (Auto-Correction)
|
||||
if (useAutoRotation && Time.time - lastInputTime > autoRotateDelay)
|
||||
{
|
||||
// Chỉ xoay khi nhân vật đang di chuyển
|
||||
if (inputReader.MoveInput.magnitude > 0.1f)
|
||||
{
|
||||
// Lấy hướng nhân vật đang nhìn (Yaw)
|
||||
float targetYaw = followTarget.eulerAngles.y;
|
||||
// Dùng LerpAngle để xoay mượt mà về hướng đó
|
||||
rotationY = Mathf.LerpAngle(rotationY, targetYaw, autoRotateSpeed * Time.deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
float scrollDelta = inputReader.ScrollInput.y;
|
||||
if (Mathf.Abs(scrollDelta) > 0.1f)
|
||||
{
|
||||
distance -= scrollDelta * zoomSensitivity * Time.deltaTime;
|
||||
distance = Mathf.Clamp(distance, minDistance, maxDistance);
|
||||
}
|
||||
rotationHandler.HandleRotation(inputReader, followTarget, rotationSmoothTime);
|
||||
zoomHandler.HandleZoom(inputReader);
|
||||
sideBias.HandleSideBias(inputReader);
|
||||
dynamicFOV.HandleDynamicFOV(_cam, inputReader);
|
||||
}
|
||||
|
||||
// Update camera rotation
|
||||
transform.rotation = rotationHandler.CurrentRotation;
|
||||
|
||||
// Calculate focus position with framing offset and side bias
|
||||
Vector3 focusPosition = followTarget.position + rotationHandler.CurrentRotation * new Vector3(framingOffset.x + sideBias.CurrentSideBias, framingOffset.y, 0);
|
||||
|
||||
// Xoay chuột: Làm mượt bằng Slerp
|
||||
Quaternion targetRotation = Quaternion.Euler(rotationX, rotationY, 0f);
|
||||
currentRotation = Quaternion.Slerp(currentRotation, targetRotation, rotationSmoothTime * Time.deltaTime);
|
||||
// Handle collision
|
||||
float targetDistance = collisionHandler.CheckCollision(focusPosition, rotationHandler.CurrentRotation, zoomHandler.CurrentDistance, zoomHandler.MinDistance);
|
||||
|
||||
// Vị trí mục tiêu: Áp dụng offset + Side Bias
|
||||
Vector3 focusPosition = followTarget.position + currentRotation * new Vector3(framingOffset.x + currentSideBias, framingOffset.y, 0);
|
||||
// Handle character fading
|
||||
characterFading.HandleCharacterFading(targetDistance);
|
||||
|
||||
// Calculate target position
|
||||
Vector3 targetPosition = focusPosition - rotationHandler.CurrentRotation * new Vector3(0, 0, targetDistance);
|
||||
|
||||
// Collision Logic (Dùng currentRotation đã được làm mượt)
|
||||
float targetDistance = distance;
|
||||
RaycastHit hit;
|
||||
Vector3 rayStart = focusPosition;
|
||||
Vector3 rayDirection = currentRotation * Vector3.back;
|
||||
|
||||
if (Physics.SphereCast(rayStart, cameraRadius, rayDirection, out hit, distance, collisionLayers))
|
||||
{
|
||||
targetDistance = Mathf.Max(minDistance, hit.distance - 0.1f);
|
||||
}
|
||||
|
||||
// Logic Làm mờ nhân vật (Character Fading)
|
||||
if (useCharacterFading && characterRenderers != null && characterRenderers.Length > 0)
|
||||
{
|
||||
HandleCharacterFading(targetDistance);
|
||||
}
|
||||
|
||||
// Vị trí cuối cùng: Làm mượt bằng SmoothDamp để tạo độ trễ đuổi theo
|
||||
Vector3 targetPosition = focusPosition - currentRotation * new Vector3(0, 0, targetDistance);
|
||||
// Handle camera shake
|
||||
shakeManager.HandleShake();
|
||||
|
||||
// Xử lý Camera Shake
|
||||
if (useShake && shakeTimer > 0)
|
||||
{
|
||||
HandleShake();
|
||||
}
|
||||
else
|
||||
{
|
||||
shakeOffset = Vector3.zero;
|
||||
}
|
||||
|
||||
transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref currentVelocity, positionSmoothTime) + shakeOffset;
|
||||
transform.rotation = currentRotation;
|
||||
|
||||
// Logic Làm trong suốt vật cản (Occlusion Transparency)
|
||||
if (useTransparency)
|
||||
{
|
||||
HandleTransparency(focusPosition);
|
||||
}
|
||||
|
||||
// Logic FOV linh hoạt
|
||||
if (useDynamicFOV && cam != null)
|
||||
{
|
||||
HandleDynamicFOV();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDynamicFOV()
|
||||
{
|
||||
float targetFOV = baseFOV;
|
||||
|
||||
// Nếu đang di chuyển và nhấn giữ nút Sprint
|
||||
if (inputReader.MoveInput.magnitude > 0.1f && inputReader.IsSprintHeld)
|
||||
{
|
||||
targetFOV = sprintFOV;
|
||||
}
|
||||
|
||||
// Làm mượt quá trình thay đổi FOV
|
||||
cam.fieldOfView = Mathf.Lerp(cam.fieldOfView, targetFOV, fovSmoothTime * Time.deltaTime);
|
||||
}
|
||||
|
||||
private void HandleShake()
|
||||
{
|
||||
shakeTimer -= Time.deltaTime;
|
||||
// Apply final position and rotation
|
||||
transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref _currentVelocity, positionSmoothTime) + shakeManager.ShakeOffset;
|
||||
|
||||
// Cường độ rung giảm dần theo thời gian
|
||||
float currentIntensity = (shakeTimer / shakeDuration) * shakeIntensity;
|
||||
|
||||
// Dùng Perlin Noise để tạo rung động mượt mà
|
||||
float shakeX = (Mathf.PerlinNoise(Time.time * 25f, 0f) - 0.5f) * 2f;
|
||||
float shakeY = (Mathf.PerlinNoise(0f, Time.time * 25f) - 0.5f) * 2f;
|
||||
float shakeZ = (Mathf.PerlinNoise(Time.time * 25f, Time.time * 25f) - 0.5f) * 2f;
|
||||
|
||||
shakeOffset = new Vector3(shakeX, shakeY, shakeZ) * currentIntensity;
|
||||
// Handle occlusion transparency (needs camera's final position and rotation)
|
||||
occlusionTransparency.HandleTransparency(transform, focusPosition);
|
||||
}
|
||||
|
||||
public void Shake(float intensity, float duration)
|
||||
{
|
||||
shakeIntensity = intensity;
|
||||
shakeDuration = duration;
|
||||
shakeTimer = duration;
|
||||
shakeManager.Shake(intensity, duration);
|
||||
}
|
||||
|
||||
private void HandleCharacterFading(float currentDistance)
|
||||
public void TriggerFallImpactShake(float fallHeight)
|
||||
{
|
||||
// Tính độ mờ dựa trên khoảng cách
|
||||
float alpha = Mathf.InverseLerp(fullyHiddenDistance, minVisibleDistance, currentDistance);
|
||||
|
||||
foreach (var renderer in characterRenderers)
|
||||
{
|
||||
if (renderer != null)
|
||||
{
|
||||
Color color = renderer.material.color;
|
||||
color.a = alpha;
|
||||
renderer.material.color = color;
|
||||
}
|
||||
}
|
||||
shakeManager.TriggerFallImpactShake(fallHeight);
|
||||
}
|
||||
|
||||
private void HandleTransparency(Vector3 focusPosition)
|
||||
{
|
||||
Vector3 direction = focusPosition - transform.position;
|
||||
float distanceToPlayer = direction.magnitude;
|
||||
RaycastHit hit;
|
||||
|
||||
// Bắn một tia từ Camera đến Nhân vật
|
||||
if (Physics.Raycast(transform.position, direction.normalized, out hit, distanceToPlayer, transparencyLayers))
|
||||
{
|
||||
Renderer renderer = hit.collider.GetComponent<Renderer>();
|
||||
if (renderer != null && renderer != lastFadedRenderer)
|
||||
{
|
||||
// Nếu chạm vật mới, khôi phục vật cũ
|
||||
ResetLastRenderer();
|
||||
|
||||
// Lưu thông tin vật mới và làm mờ
|
||||
lastFadedRenderer = renderer;
|
||||
originalColor = renderer.material.color;
|
||||
Color fadedColor = originalColor;
|
||||
fadedColor.a = fadeAlpha;
|
||||
|
||||
// Lưu ý: Material cần hỗ trợ Transparency (Surface Type: Transparent trong URP)
|
||||
renderer.material.color = fadedColor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nếu không chạm gì, khôi phục vật cũ
|
||||
ResetLastRenderer();
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetLastRenderer()
|
||||
{
|
||||
if (lastFadedRenderer != null)
|
||||
{
|
||||
lastFadedRenderer.material.color = originalColor;
|
||||
lastFadedRenderer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Quaternion PlanarRotation => Quaternion.Euler(0f, rotationY, 0f);
|
||||
public Quaternion PlanarRotation => rotationHandler.PlanarRotation;
|
||||
}
|
||||
}
|
||||
28
Assets/Scripts/Camera Controller/CameraDynamicFOV.cs
Normal file
28
Assets/Scripts/Camera Controller/CameraDynamicFOV.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
[System.Serializable]
|
||||
public class CameraDynamicFOV
|
||||
{
|
||||
[Header("Dynamic FOV")]
|
||||
[SerializeField] private bool useDynamicFOV = true;
|
||||
[SerializeField] private float baseFOV = 60f;
|
||||
[SerializeField] private float sprintFOV = 70f;
|
||||
[SerializeField] private float fovSmoothTime = 5f;
|
||||
|
||||
public void HandleDynamicFOV(Camera cam, InputReader inputReader)
|
||||
{
|
||||
if (!useDynamicFOV || cam == null || inputReader == null) return;
|
||||
|
||||
float targetFOV = baseFOV;
|
||||
|
||||
if (inputReader.MoveInput.magnitude > 0.1f && inputReader.IsSprintHeld)
|
||||
{
|
||||
targetFOV = sprintFOV;
|
||||
}
|
||||
|
||||
cam.fieldOfView = Mathf.Lerp(cam.fieldOfView, targetFOV, fovSmoothTime * Time.deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 99cfe3471dc945a4fad66b3b440c2c12
|
||||
@@ -0,0 +1,58 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
[System.Serializable]
|
||||
public class CameraOcclusionTransparency
|
||||
{
|
||||
[Header("Occlusion Transparency")]
|
||||
[SerializeField] private bool useTransparency = true;
|
||||
[SerializeField] private LayerMask transparencyLayers;
|
||||
[SerializeField] private float fadeAlpha = 0.3f;
|
||||
|
||||
private Renderer _lastFadedRenderer;
|
||||
private Color _originalColor;
|
||||
|
||||
public void HandleTransparency(Transform cameraTransform, Vector3 focusPosition)
|
||||
{
|
||||
if (!useTransparency)
|
||||
{
|
||||
ResetLastRenderer();
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 direction = focusPosition - cameraTransform.position;
|
||||
float distanceToPlayer = direction.magnitude;
|
||||
RaycastHit hit;
|
||||
|
||||
if (Physics.Raycast(cameraTransform.position, direction.normalized, out hit, distanceToPlayer, transparencyLayers))
|
||||
{
|
||||
Renderer renderer = hit.collider.GetComponent<Renderer>();
|
||||
if (renderer != null && renderer != _lastFadedRenderer)
|
||||
{
|
||||
ResetLastRenderer();
|
||||
|
||||
_lastFadedRenderer = renderer;
|
||||
_originalColor = renderer.material.color;
|
||||
Color fadedColor = _originalColor;
|
||||
fadedColor.a = fadeAlpha;
|
||||
|
||||
renderer.material.color = fadedColor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetLastRenderer();
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetLastRenderer()
|
||||
{
|
||||
if (_lastFadedRenderer != null)
|
||||
{
|
||||
_lastFadedRenderer.material.color = _originalColor;
|
||||
_lastFadedRenderer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ad64ce0d02e30243acc55aa0ebc74a9
|
||||
66
Assets/Scripts/Camera Controller/CameraRotationHandler.cs
Normal file
66
Assets/Scripts/Camera Controller/CameraRotationHandler.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
[System.Serializable]
|
||||
public class CameraRotationHandler
|
||||
{
|
||||
[Header("Rotation Settings")]
|
||||
[SerializeField] private float sensitivity = 0.1f;
|
||||
[SerializeField] private float minVerticalAngle = -45f;
|
||||
[SerializeField] private float maxVerticalAngle = 45f;
|
||||
[SerializeField] private bool invertX;
|
||||
[SerializeField] private bool invertY;
|
||||
|
||||
[Header("Auto Rotation")]
|
||||
[SerializeField] private bool useAutoRotation = true;
|
||||
[SerializeField] private float autoRotateDelay = 2.5f;
|
||||
[SerializeField] private float autoRotateSpeed = 2f;
|
||||
|
||||
private float _rotationX;
|
||||
private float _rotationY;
|
||||
private float _lastInputTime;
|
||||
|
||||
public Quaternion CurrentRotation { get; private set; }
|
||||
public Quaternion PlanarRotation => Quaternion.Euler(0f, _rotationY, 0f);
|
||||
|
||||
public void Initialize(Transform cameraTransform)
|
||||
{
|
||||
_rotationX = cameraTransform.eulerAngles.x;
|
||||
_rotationY = cameraTransform.eulerAngles.y;
|
||||
_lastInputTime = Time.time;
|
||||
CurrentRotation = cameraTransform.rotation;
|
||||
}
|
||||
|
||||
public void HandleRotation(InputReader inputReader, Transform followTarget, float rotationSmoothTime)
|
||||
{
|
||||
if (inputReader == null) return;
|
||||
|
||||
if (inputReader.LookInput.magnitude > 0.01f)
|
||||
{
|
||||
_lastInputTime = Time.time;
|
||||
}
|
||||
|
||||
float invertXVal = (invertX) ? -1 : 1;
|
||||
float invertYVal = (invertY) ? -1 : 1;
|
||||
|
||||
_rotationX -= inputReader.LookInput.y * invertYVal * sensitivity * Time.deltaTime;
|
||||
_rotationX = Mathf.Clamp(_rotationX, minVerticalAngle, maxVerticalAngle);
|
||||
|
||||
_rotationY += inputReader.LookInput.x * invertXVal * sensitivity * Time.deltaTime;
|
||||
|
||||
// Auto-Correction
|
||||
if (useAutoRotation && Time.time - _lastInputTime > autoRotateDelay)
|
||||
{
|
||||
if (inputReader.MoveInput.magnitude > 0.1f)
|
||||
{
|
||||
float targetYaw = followTarget.eulerAngles.y;
|
||||
_rotationY = Mathf.LerpAngle(_rotationY, targetYaw, autoRotateSpeed * Time.deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
Quaternion targetRotation = Quaternion.Euler(_rotationX, _rotationY, 0f);
|
||||
CurrentRotation = Quaternion.Slerp(CurrentRotation, targetRotation, rotationSmoothTime * Time.deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92ec094a714577d49be74c0fedb3eeb3
|
||||
78
Assets/Scripts/Camera Controller/CameraShakeManager.cs
Normal file
78
Assets/Scripts/Camera Controller/CameraShakeManager.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
[System.Serializable]
|
||||
public class CameraShakeManager
|
||||
{
|
||||
[SerializeField] private bool useShake = true;
|
||||
[SerializeField] private float shakeFrequency = 25f; // How fast the shake oscillates
|
||||
|
||||
[SerializeField]
|
||||
private AnimationCurve decayCurve = AnimationCurve.EaseInOut(0, 1, 1, 0); // How intensity decays over duration
|
||||
|
||||
[Header("Fall Impact Settings")] [SerializeField]
|
||||
private bool enableFallImpactShake = true;
|
||||
|
||||
[SerializeField] private float minFallHeightForShake = 2f;
|
||||
[SerializeField] private float maxFallHeightForShake = 10f;
|
||||
[SerializeField] private float minFallShakeIntensity = 0.5f;
|
||||
[SerializeField] private float maxFallShakeIntensity = 3f;
|
||||
[SerializeField] private float minFallShakeDuration = 0.2f;
|
||||
[SerializeField] private float maxFallShakeDuration = 0.8f;
|
||||
[SerializeField] private AnimationCurve fallHeightToIntensityCurve = AnimationCurve.Linear(0, 0, 1, 1);
|
||||
[SerializeField] private AnimationCurve fallHeightToDurationCurve = AnimationCurve.Linear(0, 0, 1, 1);
|
||||
|
||||
private float _shakeIntensity = 0f;
|
||||
private float _shakeDuration = 0f;
|
||||
private float _shakeTimer = 0f; // Counts down from _shakeDuration
|
||||
private Vector3 _shakeOffset;
|
||||
|
||||
public Vector3 ShakeOffset => _shakeOffset;
|
||||
|
||||
public void HandleShake()
|
||||
{
|
||||
if (!useShake || _shakeTimer <= 0)
|
||||
{
|
||||
_shakeOffset = Vector3.zero;
|
||||
return;
|
||||
}
|
||||
|
||||
_shakeTimer -= Time.deltaTime;
|
||||
float progress = 1f - (_shakeTimer / _shakeDuration); // 0 at start, 1 at end
|
||||
|
||||
// Apply decay curve to intensity
|
||||
float currentIntensity = _shakeIntensity * decayCurve.Evaluate(progress);
|
||||
|
||||
// Use shakeFrequency for Perlin noise
|
||||
float shakeX = (Mathf.PerlinNoise(Time.time * shakeFrequency, 0f) - 0.5f) * 2f;
|
||||
float shakeY = (Mathf.PerlinNoise(0f, Time.time * shakeFrequency) - 0.5f) * 2f;
|
||||
float shakeZ = (Mathf.PerlinNoise(Time.time * shakeFrequency, Time.time * shakeFrequency) - 0.5f) * 2f;
|
||||
|
||||
_shakeOffset = new Vector3(shakeX, shakeY, shakeZ) * currentIntensity;
|
||||
}
|
||||
|
||||
public void Shake(float intensity, float duration)
|
||||
{
|
||||
_shakeIntensity = intensity;
|
||||
_shakeDuration = duration;
|
||||
_shakeTimer = duration; // Reset timer
|
||||
}
|
||||
|
||||
public void TriggerFallImpactShake(float fallHeight)
|
||||
{
|
||||
if (!enableFallImpactShake || fallHeight < minFallHeightForShake) return;
|
||||
|
||||
// Normalize fall height between 0 and 1 relative to min/max thresholds
|
||||
float normalizedFallHeight = Mathf.InverseLerp(minFallHeightForShake, maxFallHeightForShake, fallHeight);
|
||||
|
||||
// Calculate intensity and duration using curves and ranges
|
||||
float intensity = Mathf.Lerp(minFallShakeIntensity, maxFallShakeIntensity,
|
||||
fallHeightToIntensityCurve.Evaluate(normalizedFallHeight));
|
||||
float duration = Mathf.Lerp(minFallShakeDuration, maxFallShakeDuration,
|
||||
fallHeightToDurationCurve.Evaluate(normalizedFallHeight));
|
||||
|
||||
Shake(intensity, duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16e87ee63ce87ac4cb7b952772318c0e
|
||||
32
Assets/Scripts/Camera Controller/CameraSideBias.cs
Normal file
32
Assets/Scripts/Camera Controller/CameraSideBias.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
[System.Serializable]
|
||||
public class CameraSideBias
|
||||
{
|
||||
[Header("Side Bias")]
|
||||
[SerializeField] private bool useSideBias = true;
|
||||
[SerializeField] private float horizontalBiasAmount = 0.5f;
|
||||
[SerializeField] private float biasSmoothTime = 3f;
|
||||
|
||||
private float _currentSideBias;
|
||||
|
||||
public float CurrentSideBias => _currentSideBias;
|
||||
|
||||
public void HandleSideBias(InputReader inputReader)
|
||||
{
|
||||
if (inputReader == null) return;
|
||||
|
||||
if (useSideBias)
|
||||
{
|
||||
float targetBias = -inputReader.MoveInput.x * horizontalBiasAmount;
|
||||
_currentSideBias = Mathf.Lerp(_currentSideBias, targetBias, biasSmoothTime * Time.deltaTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentSideBias = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Camera Controller/CameraSideBias.cs.meta
Normal file
2
Assets/Scripts/Camera Controller/CameraSideBias.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6bf6a3797a87fb4fb086c1625b1e7bb
|
||||
29
Assets/Scripts/Camera Controller/CameraZoomHandler.cs
Normal file
29
Assets/Scripts/Camera Controller/CameraZoomHandler.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
[System.Serializable]
|
||||
public class CameraZoomHandler
|
||||
{
|
||||
[Header("Zoom Settings")]
|
||||
[SerializeField] private float distance = 5;
|
||||
[SerializeField] private float minDistance = 2f;
|
||||
[SerializeField] private float maxDistance = 15f;
|
||||
[SerializeField] private float zoomSensitivity = 1f;
|
||||
|
||||
public float CurrentDistance => distance;
|
||||
public float MinDistance => minDistance;
|
||||
|
||||
public void HandleZoom(InputReader inputReader)
|
||||
{
|
||||
if (inputReader == null) return;
|
||||
|
||||
float scrollDelta = inputReader.ScrollInput.y;
|
||||
if (Mathf.Abs(scrollDelta) > 0.1f)
|
||||
{
|
||||
distance -= scrollDelta * zoomSensitivity * Time.deltaTime;
|
||||
distance = Mathf.Clamp(distance, minDistance, maxDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 416c8d0f8adfcbc4390cd2e4e369e0db
|
||||
@@ -21,6 +21,7 @@ namespace OnlyScove.Scripts
|
||||
public event Action OnInteractEvent; // E Key
|
||||
public event Action OnNextInteractEvent; // R Key
|
||||
public event Action OnPreviousInteractEvent; // Q Key
|
||||
public event Action OnToggleViewEvent; // F2 - New event for toggling camera view
|
||||
|
||||
public void OnAttack(InputAction.CallbackContext context)
|
||||
{
|
||||
@@ -56,6 +57,15 @@ namespace OnlyScove.Scripts
|
||||
if (context.canceled) IsSprintHeld = false;
|
||||
}
|
||||
|
||||
// New method for ToggleView input
|
||||
public void OnToggleView(InputAction.CallbackContext context)
|
||||
{
|
||||
if (context.performed)
|
||||
{
|
||||
OnToggleViewEvent?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnJump(InputAction.CallbackContext context)
|
||||
{
|
||||
if (context.performed) OnJumpEvent?.Invoke();
|
||||
|
||||
Reference in New Issue
Block a user