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

282 lines
8.9 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using UnityEngine;
using Cinemachine;
using System.Collections;
using Unity.BossRoom.CameraUtils;
public class EdgePanningController : MonoBehaviour
{
[Header("Camera References")]
[Tooltip("Assign your Cinemachine FreeLook here.")]
public CinemachineFreeLook virtualCamera;
// This is the hero (or unit) we want to eventually recenter on.
private Transform targetCharacter;
// We create (or assign) a separate GameObject that the FreeLook will Follow/LookAt.
private Transform cameraTarget;
[Header("Edge Panning Settings")]
[Tooltip("Distance in pixels from screen edge to start panning.")]
public float edgeThreshold = 10f;
[Tooltip("Pan speed in world units per second.")]
public float panSpeed = 20f;
[Tooltip("Maximum distance (in world units) the camera can pan away from the hero.")]
public float maxPanDistance = 30f;
[Header("Centering / Return Settings")]
[Tooltip("Delay (seconds) after panning stops before auto-centering on hero begins.")]
public float returnDelay = 0.5f;
[Tooltip("Speed at which cameraTarget moves back to hero when auto-returning.")]
public float returnSpeed = 15f;
// Smoothing for the automatic “return to hero” motion
[Header("Smoothing")]
[Tooltip("Time for SmoothDamp when auto-centering back to hero.")]
public float smoothTime = 0.1f;
// State-tracking
private bool isPanning = false;
private bool isReturning = false;
private float timeSincePanStopped = 0f;
private Vector3 currentVelocity = Vector3.zero;
// Free camera (debug) mode
private bool isFreeRoamMode = false;
[Tooltip("Speed for free-roam mode.")]
public float freeRoamSpeed = 20f;
private void Awake()
{
CameraController.OnCameraAttached += SetTargetCharacter;
}
private void OnDestroy()
{
CameraController.OnCameraAttached -= SetTargetCharacter;
}
private void Start()
{
if (virtualCamera == null)
{
Debug.LogError("EdgePanningController: You must assign a CinemachineFreeLook to virtualCamera.");
enabled = false;
return;
}
// Initially, well leave cameraTarget null until we know who the hero is.
}
/// <summary>
/// Called (by BossRooms CameraController) when the hero/character is attached.
/// </summary>
private void SetTargetCharacter(Transform characterTransform)
{
// characterTransform is actually the child transform of the player prefab.
// We'll treat its parent as the "hero root".
targetCharacter = characterTransform.parent;
// Create a new, empty GameObject to serve as the cameras focus point.
if (cameraTarget == null)
{
GameObject go = new GameObject("CameraTarget");
cameraTarget = go.transform;
}
// Place the cameraTarget initially on the heros position.
cameraTarget.position = targetCharacter.position;
// Tell the FreeLook to Follow/LookAt this cameraTarget
virtualCamera.Follow = cameraTarget;
virtualCamera.LookAt = cameraTarget;
}
private void LateUpdate()
{
// Toggle free-roam for debugging
if (Input.GetKeyDown(KeyCode.Backspace))
{
ToggleFreeRoamMode();
}
if (isFreeRoamMode)
{
HandleFreeRoam();
return;
}
if (targetCharacter == null || cameraTarget == null)
return;
HandleDotaStylePanning();
}
private void HandleDotaStylePanning()
{
Vector3 mousePos = Input.mousePosition;
Vector3 panDirection = Vector3.zero;
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;
}
if (nearEdge)
{
// We are actively panning
isPanning = true;
isReturning = false;
timeSincePanStopped = 0f;
// Compute a world-space direction on the XZ plane relative to cameras facing
Vector3 forward = virtualCamera.transform.forward;
forward.y = 0;
forward.Normalize();
Vector3 right = virtualCamera.transform.right;
right.y = 0;
right.Normalize();
Vector3 worldPanDir = (right * panDirection.x + forward * panDirection.z).normalized;
// Move cameraTarget along that direction (scaled by panSpeed and deltaTime)
Vector3 newPos = cameraTarget.position + worldPanDir * panSpeed * Time.deltaTime;
// Clamp so cameraTarget never goes further than maxPanDistance from the hero
Vector3 offsetFromHero = newPos - targetCharacter.position;
if (offsetFromHero.magnitude > maxPanDistance)
{
offsetFromHero = offsetFromHero.normalized * maxPanDistance;
newPos = targetCharacter.position + offsetFromHero;
}
cameraTarget.position = newPos;
}
else
{
// No edge-pan input. Start the return-to-hero timer if we were panning.
if (isPanning)
{
isPanning = false;
timeSincePanStopped = 0f;
}
timeSincePanStopped += Time.deltaTime;
// After returnDelay, smoothly move cameraTarget back toward hero
if (timeSincePanStopped >= returnDelay && !isReturning)
{
isReturning = true;
}
if (isReturning)
{
// Smoothly interpolate cameraTarget back to the heros position
Vector3 targetPos = targetCharacter.position;
cameraTarget.position = Vector3.SmoothDamp(
cameraTarget.position,
targetPos,
ref currentVelocity,
smoothTime,
returnSpeed
);
// If close enough, stop returning
if (Vector3.Distance(cameraTarget.position, targetPos) < 0.1f)
{
cameraTarget.position = targetPos;
isReturning = false;
currentVelocity = Vector3.zero;
}
}
}
// Double-click to instantly center on hero
if (Input.GetMouseButtonDown(0) && Input.GetMouseButtonDown(0))
{
CenterOnHero();
}
// Space bar to instantly center on hero
if (Input.GetKeyDown(KeyCode.Space))
{
CenterOnHero();
}
}
private void CenterOnHero()
{
if (targetCharacter == null || cameraTarget == null)
return;
cameraTarget.position = targetCharacter.position;
isPanning = false;
isReturning = false;
timeSincePanStopped = returnDelay; // So it wont start returning immediately
currentVelocity = Vector3.zero;
}
private void ToggleFreeRoamMode()
{
isFreeRoamMode = !isFreeRoamMode;
if (isFreeRoamMode)
{
// Detach camera from any Follow target
virtualCamera.Follow = null;
virtualCamera.LookAt = null;
}
else
{
// Reattach to our cameraTarget
if (cameraTarget != null)
{
virtualCamera.Follow = cameraTarget;
virtualCamera.LookAt = cameraTarget;
}
CenterOnHero();
}
}
private void HandleFreeRoam()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
float upDown = 0f;
if (Input.GetKey(KeyCode.E)) upDown = 1f;
else if (Input.GetKey(KeyCode.Q)) upDown = -1f;
Vector3 move = new Vector3(h, upDown, v);
virtualCamera.transform.position += virtualCamera.transform.rotation * move * freeRoamSpeed * Time.deltaTime;
}
// Optional: If you have camera shake functionality, you'd need to re-implement it differently,
// because we no longer use CinemachineCameraOffset for panning. Left out here for brevity.
}