You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
HighGroundRoyaleNetcode/Assets/Scripts/Gameplay/GameplayObjects/Character/EdgePanningController.cs

302 lines
9.3 KiB
C#

using UnityEngine;
using Cinemachine;
using System.Collections;
using Unity.BossRoom.CameraUtils;
public class EdgePanningController : MonoBehaviour
{
[Header("Camera References")]
public CinemachineFreeLook virtualCamera;
private CinemachineCameraOffset cameraOffset;
private Transform cameraTransform;
[Header("Edge Panning Settings")]
public float edgeThreshold = 10f; // Distance from screen edge to trigger panning
public float panSpeed = 20f; // World units per second
public float maxPanDistance = 30f; // Maximum distance from player
public float returnSpeed = 15f; // Speed when returning to player
public float returnDelay = 0.5f; // Delay before auto-return starts
[Header("Smoothing")]
public float smoothTime = 0.1f; // Camera movement smoothing
// Camera state
private Transform targetCharacter;
private Vector3 panOffset = Vector3.zero;
private Vector3 currentVelocity = Vector3.zero;
private float timeSincePanStopped = 0f;
private bool isPanning = false;
private bool isReturning = false;
private Vector3 lastValidPosition;
// Free camera mode (for debugging)
private bool isFreeRoamMode = false;
public float freeRoamSpeed = 20f;
// Camera shake
private Coroutine shakeCoroutine;
private void Start()
{
// Get camera offset component
cameraOffset = virtualCamera.GetComponent<CinemachineCameraOffset>();
if (cameraOffset == null)
{
cameraOffset = virtualCamera.gameObject.AddComponent<CinemachineCameraOffset>();
}
// Cache camera transform
cameraTransform = virtualCamera.transform;
// Initialize offset
cameraOffset.m_Offset = Vector3.zero;
}
private void Awake()
{
CameraController.OnCameraAttached += SetTargetCharacter;
}
private void OnDestroy()
{
CameraController.OnCameraAttached -= SetTargetCharacter;
}
private void SetTargetCharacter(Transform characterTransform)
{
targetCharacter = characterTransform.parent;
lastValidPosition = targetCharacter.position;
}
private void LateUpdate()
{
// Toggle free roam mode
if (Input.GetKeyDown(KeyCode.Backspace))
{
ToggleFreeRoamMode();
}
if (isFreeRoamMode)
{
HandleFreeRoam();
return;
}
if (targetCharacter == null) return;
HandleDotaStylePanning();
}
private void HandleDotaStylePanning()
{
Vector3 mousePos = Input.mousePosition;
Vector3 panDirection = Vector3.zero;
// Check screen edges
bool nearEdge = false;
// Left edge
if (mousePos.x <= edgeThreshold)
{
float strength = 1f - (mousePos.x / edgeThreshold);
panDirection.x = -strength;
nearEdge = true;
}
// Right edge
else if (mousePos.x >= Screen.width - edgeThreshold)
{
float strength = 1f - ((Screen.width - mousePos.x) / edgeThreshold);
panDirection.x = strength;
nearEdge = true;
}
// Bottom edge
if (mousePos.y <= edgeThreshold)
{
float strength = 1f - (mousePos.y / edgeThreshold);
panDirection.z = -strength;
nearEdge = true;
}
// Top edge
else if (mousePos.y >= Screen.height - edgeThreshold)
{
float strength = 1f - ((Screen.height - mousePos.y) / edgeThreshold);
panDirection.z = strength;
nearEdge = true;
}
// Handle panning state
if (nearEdge)
{
isPanning = true;
isReturning = false;
timeSincePanStopped = 0f;
// Convert pan direction to world space (relative to camera orientation)
Vector3 forward = cameraTransform.forward;
forward.y = 0;
forward.Normalize();
Vector3 right = cameraTransform.right;
right.y = 0;
right.Normalize();
Vector3 worldPanDirection = (right * panDirection.x + forward * panDirection.z).normalized * panDirection.magnitude;
// Apply panning
Vector3 targetOffset = panOffset + worldPanDirection * panSpeed * Time.deltaTime;
// Clamp to max distance
if (targetOffset.magnitude > maxPanDistance)
{
targetOffset = targetOffset.normalized * maxPanDistance;
}
panOffset = targetOffset;
}
else
{
// Not near edge - handle return to player
if (isPanning)
{
isPanning = false;
timeSincePanStopped = 0f;
}
// Check if we should return to player
timeSincePanStopped += Time.deltaTime;
if (timeSincePanStopped >= returnDelay && panOffset.magnitude > 0.1f)
{
isReturning = true;
// Smoothly return to player
float returnAmount = returnSpeed * Time.deltaTime;
if (panOffset.magnitude <= returnAmount)
{
panOffset = Vector3.zero;
isReturning = false;
}
else
{
panOffset = Vector3.Lerp(panOffset, Vector3.zero, returnAmount / panOffset.magnitude);
}
}
}
// Apply the offset smoothly
Vector3 targetPosition = targetCharacter.position + panOffset;
cameraOffset.m_Offset = Vector3.SmoothDamp(cameraOffset.m_Offset, panOffset, ref currentVelocity, smoothTime);
// Double-click to center on hero (Dota feature)
if (Input.GetMouseButtonDown(0) && Input.GetMouseButtonDown(0)) // Simplified double-click detection
{
CenterOnHero();
}
// Space bar to instantly center (another Dota feature)
if (Input.GetKeyDown(KeyCode.Space))
{
CenterOnHero();
}
}
private void CenterOnHero()
{
panOffset = Vector3.zero;
cameraOffset.m_Offset = Vector3.zero;
currentVelocity = Vector3.zero;
isPanning = false;
isReturning = false;
timeSincePanStopped = returnDelay; // Skip the delay
}
private void ToggleFreeRoamMode()
{
isFreeRoamMode = !isFreeRoamMode;
if (isFreeRoamMode)
{
virtualCamera.Follow = null;
virtualCamera.LookAt = null;
}
else
{
if (targetCharacter != null)
{
virtualCamera.Follow = targetCharacter;
virtualCamera.LookAt = targetCharacter;
}
CenterOnHero();
}
}
private void HandleFreeRoam()
{
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
float ascent = 0f;
if (Input.GetKey(KeyCode.E)) ascent = 1f;
else if (Input.GetKey(KeyCode.Q)) ascent = -1f;
Vector3 movement = new Vector3(horizontal, ascent, vertical);
cameraTransform.position += cameraTransform.rotation * movement * freeRoamSpeed * Time.deltaTime;
}
public void ShakeCamera(float duration = 0.3f, float magnitude = 0.2f)
{
if (shakeCoroutine != null)
{
StopCoroutine(shakeCoroutine);
}
shakeCoroutine = StartCoroutine(ShakeRoutine(duration, magnitude));
}
private IEnumerator ShakeRoutine(float duration, float magnitude)
{
float elapsed = 0f;
while (elapsed < duration)
{
float x = Random.Range(-1f, 1f) * magnitude;
float y = Random.Range(-1f, 1f) * magnitude;
float z = Random.Range(-1f, 1f) * magnitude;
// Apply shake on top of existing offset
Vector3 shakeOffset = new Vector3(x, y, z);
cameraOffset.m_Offset = panOffset + shakeOffset;
elapsed += Time.deltaTime;
yield return null;
}
// Reset to current pan offset
cameraOffset.m_Offset = panOffset;
}
// Helper method to check if cursor is over UI
private bool IsCursorOverUI()
{
return UnityEngine.EventSystems.EventSystem.current != null &&
UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject();
}
// Debug visualization
private void OnDrawGizmos()
{
if (!Application.isPlaying || targetCharacter == null) return;
// Draw max pan distance
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(targetCharacter.position, maxPanDistance);
// Draw current offset
if (panOffset.magnitude > 0.1f)
{
Gizmos.color = Color.red;
Gizmos.DrawLine(targetCharacter.position, targetCharacter.position + panOffset);
Gizmos.DrawWireSphere(targetCharacter.position + panOffset, 1f);
}
}
}