Update EdgePanningController.cs

dev-hazim
Hazim 4 weeks ago
parent 6590654c59
commit 1caef53f9d

@ -5,43 +5,51 @@ using Unity.BossRoom.CameraUtils;
public class EdgePanningController : MonoBehaviour public class EdgePanningController : MonoBehaviour
{ {
[Header("Camera References")]
public CinemachineFreeLook virtualCamera; public CinemachineFreeLook virtualCamera;
private CinemachineCameraOffset cameraOffset; private CinemachineCameraOffset cameraOffset;
private Transform cameraTransform;
private Coroutine shakeCoroutine;
[Header("Edge Panning Settings")]
public float edgeThreshold = 50f; // Edge detection distance from screen edges public float edgeThreshold = 10f; // Distance from screen edge to trigger panning
public float resetRadiusPercentage = 3f; // Center reset radius in percentage of screen public float panSpeed = 20f; // World units per second
public float panSpeed = 0.1f; // How much to offset per frame public float maxPanDistance = 30f; // Maximum distance from player
public float resetSpeed = 2f; // Speed of resetting panning public float returnSpeed = 15f; // Speed when returning to player
public Vector2 panLimit = new Vector2(0.5f, 0.5f); // Max panning limit (x, y) public float returnDelay = 0.5f; // Delay before auto-return starts
// --- Added fields for free roam mode --- [Header("Smoothing")]
public float freeRoamSpeed = 10f; // Speed of free roam movement public float smoothTime = 0.1f; // Camera movement smoothing
private bool isFreeRoamMode = false; // Toggle for free roam mode
private Vector3 savedCameraPosition; // Stores camera position before free roam // Camera state
private Quaternion savedCameraRotation; // Stores camera rotation before free roam private Transform targetCharacter;
private Transform savedFollowTarget; // Stores the original follow target private Vector3 panOffset = Vector3.zero;
private Vector3 currentVelocity = Vector3.zero;
private Vector3 defaultOffset; private float timeSincePanStopped = 0f;
private Vector3 panningOffset;
private bool isPanning = false; private bool isPanning = false;
private bool targetFound = false; private bool isReturning = false;
private bool shouldReset = false; // Flag to trigger reset immediately private Vector3 lastValidPosition;
private Transform targetCharacter; // The character the camera follows // Free camera mode (for debugging)
private bool isFreeRoamMode = false;
public float freeRoamSpeed = 20f;
// Camera shake
private Coroutine shakeCoroutine;
private void Start() private void Start()
{ {
// Get camera offset component
cameraOffset = virtualCamera.GetComponent<CinemachineCameraOffset>(); cameraOffset = virtualCamera.GetComponent<CinemachineCameraOffset>();
if (cameraOffset == null) if (cameraOffset == null)
{ {
Debug.LogError("[EdgePanningController] No CinemachineCameraOffset component found! Add it to the FreeLook Camera."); cameraOffset = virtualCamera.gameObject.AddComponent<CinemachineCameraOffset>();
return;
} }
defaultOffset = cameraOffset.m_Offset; // Cache camera transform
panningOffset = defaultOffset; cameraTransform = virtualCamera.transform;
// Initialize offset
cameraOffset.m_Offset = Vector3.zero;
} }
private void Awake() private void Awake()
@ -57,144 +65,183 @@ public class EdgePanningController : MonoBehaviour
private void SetTargetCharacter(Transform characterTransform) private void SetTargetCharacter(Transform characterTransform)
{ {
targetCharacter = characterTransform.parent; targetCharacter = characterTransform.parent;
targetFound = true; lastValidPosition = targetCharacter.position;
} }
private void Update() private void LateUpdate()
{ {
// Check for backspace key to toggle free roam mode // Toggle free roam mode
if (Input.GetKeyDown(KeyCode.Backspace)) if (Input.GetKeyDown(KeyCode.Backspace))
{ {
ToggleFreeRoamMode(); ToggleFreeRoamMode();
} }
// When in free roam mode, handle free movement and bypass normal edge panning
if (isFreeRoamMode) if (isFreeRoamMode)
{ {
HandleFreeRoam(); HandleFreeRoam();
return; // Skip the rest of Update so the panning logic is not executed return;
} }
// Normal camera behavior (edge panning) if a target is set if (targetCharacter == null) return;
if (targetFound)
HandleEdgePanning(); HandleDotaStylePanning();
} }
/// <summary> private void HandleDotaStylePanning()
/// Toggles the free roam mode on/off.
/// </summary>
private void ToggleFreeRoamMode()
{ {
isFreeRoamMode = !isFreeRoamMode; Vector3 mousePos = Input.mousePosition;
if (isFreeRoamMode) Vector3 panDirection = Vector3.zero;
// Check screen edges
bool nearEdge = false;
// Left edge
if (mousePos.x <= edgeThreshold)
{ {
// Activate free roam: save current camera state and disable following float strength = 1f - (mousePos.x / edgeThreshold);
savedCameraPosition = virtualCamera.transform.position; panDirection.x = -strength;
savedCameraRotation = virtualCamera.transform.rotation; nearEdge = true;
savedFollowTarget = virtualCamera.Follow; // Save the current follow target if any
virtualCamera.Follow = null; // Detach so the camera can be controlled directly
} }
else // Right edge
else if (mousePos.x >= Screen.width - edgeThreshold)
{ {
// Deactivate free roam: restore the previous camera state and resume following target float strength = 1f - ((Screen.width - mousePos.x) / edgeThreshold);
virtualCamera.transform.position = savedCameraPosition; panDirection.x = strength;
virtualCamera.transform.rotation = savedCameraRotation; nearEdge = true;
virtualCamera.Follow = savedFollowTarget; // Reattach the follow target
// Reset panning variables so the camera aligns with its default offset
cameraOffset.m_Offset = defaultOffset;
isPanning = false;
panningOffset = defaultOffset;
} }
}
/// <summary>
/// Handles free roam camera movement when free roam mode is active.
/// Uses the Horizontal/Vertical axes and Q/E keys to move up and down.
/// </summary>
private void HandleFreeRoam()
{
float horizontal = Input.GetAxis("Horizontal"); // A/D or Left/Right
float vertical = Input.GetAxis("Vertical"); // W/S or Up/Down
// Use E to ascend and Q to descend (you can change these keys if needed) // Bottom edge
float ascent = 0f; if (mousePos.y <= edgeThreshold)
if (Input.GetKey(KeyCode.E)) {
ascent = 1f; float strength = 1f - (mousePos.y / edgeThreshold);
else if (Input.GetKey(KeyCode.Q)) panDirection.z = -strength;
ascent = -1f; nearEdge = true;
}
// Compute movement relative to current camera orientation // Top edge
Vector3 movement = new Vector3(horizontal, ascent, vertical); else if (mousePos.y >= Screen.height - edgeThreshold)
virtualCamera.transform.position += virtualCamera.transform.rotation * movement * freeRoamSpeed * Time.deltaTime; {
} float strength = 1f - ((Screen.height - mousePos.y) / edgeThreshold);
panDirection.z = strength;
nearEdge = true;
}
private void HandleEdgePanning() // Handle panning state
{ if (nearEdge)
if (targetCharacter == null) return; {
isPanning = true;
isReturning = false;
timeSincePanStopped = 0f;
Vector3 newOffset = panningOffset; // Convert pan direction to world space (relative to camera orientation)
Vector3 mousePos = Input.mousePosition; Vector3 forward = cameraTransform.forward;
forward.y = 0;
forward.Normalize();
float screenWidth = Screen.width; Vector3 right = cameraTransform.right;
float screenHeight = Screen.height; right.y = 0;
right.Normalize();
// Center Reset Radius (3% of screen size) Vector3 worldPanDirection = (right * panDirection.x + forward * panDirection.z).normalized * panDirection.magnitude;
float resetRadiusX = screenWidth * (resetRadiusPercentage / 100f);
float resetRadiusY = screenHeight * (resetRadiusPercentage / 100f);
Vector2 screenCenter = new Vector2(screenWidth / 2, screenHeight / 2);
bool isNearCenter = Mathf.Abs(mousePos.x - screenCenter.x) <= resetRadiusX && // Apply panning
Mathf.Abs(mousePos.y - screenCenter.y) <= resetRadiusY; Vector3 targetOffset = panOffset + worldPanDirection * panSpeed * Time.deltaTime;
bool isHoveringTarget = IsCursorOverTargetCharacter(); // Clamp to max distance
if (targetOffset.magnitude > maxPanDistance)
{
targetOffset = targetOffset.normalized * maxPanDistance;
}
// If the cursor touches the center or target even for a moment, trigger reset panOffset = targetOffset;
if (isNearCenter || isHoveringTarget)
{
shouldReset = true;
} }
else
// When resetting, smoothly return to the default position
if (shouldReset && isPanning)
{ {
cameraOffset.m_Offset = Vector3.Lerp(cameraOffset.m_Offset, defaultOffset, Time.deltaTime * resetSpeed); // Not near edge - handle return to player
if (isPanning)
// Once the camera gets close enough to default, stop panning and reset the flag
if (Vector3.Distance(cameraOffset.m_Offset, defaultOffset) < 0.01f)
{ {
isPanning = false; isPanning = false;
panningOffset = defaultOffset; timeSincePanStopped = 0f;
shouldReset = false; }
// 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);
}
} }
return;
} }
// If the cursor is near the edges, adjust the offset (within limits) // Apply the offset smoothly
if (mousePos.x <= edgeThreshold) newOffset.x = Mathf.Max(defaultOffset.x - panLimit.x, newOffset.x - panSpeed); Vector3 targetPosition = targetCharacter.position + panOffset;
if (mousePos.x >= screenWidth - edgeThreshold) newOffset.x = Mathf.Min(defaultOffset.x + panLimit.x, newOffset.x + panSpeed); cameraOffset.m_Offset = Vector3.SmoothDamp(cameraOffset.m_Offset, panOffset, ref currentVelocity, smoothTime);
if (mousePos.y <= edgeThreshold) newOffset.y = Mathf.Max(defaultOffset.y - panLimit.y, newOffset.y - panSpeed);
if (mousePos.y >= screenHeight - edgeThreshold) newOffset.y = Mathf.Min(defaultOffset.y + panLimit.y, newOffset.y + panSpeed);
if (newOffset != panningOffset) // Double-click to center on hero (Dota feature)
if (Input.GetMouseButtonDown(0) && Input.GetMouseButtonDown(0)) // Simplified double-click detection
{ {
isPanning = true; CenterOnHero();
}
// Space bar to instantly center (another Dota feature)
if (Input.GetKeyDown(KeyCode.Space))
{
CenterOnHero();
} }
}
cameraOffset.m_Offset = Vector3.Lerp(cameraOffset.m_Offset, newOffset, Time.deltaTime * 3f); private void CenterOnHero()
panningOffset = cameraOffset.m_Offset; {
panOffset = Vector3.zero;
cameraOffset.m_Offset = Vector3.zero;
currentVelocity = Vector3.zero;
isPanning = false;
isReturning = false;
timeSincePanStopped = returnDelay; // Skip the delay
} }
private bool IsCursorOverTargetCharacter() private void ToggleFreeRoamMode()
{ {
if (targetCharacter == null) return false; isFreeRoamMode = !isFreeRoamMode;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); if (isFreeRoamMode)
if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity))
{ {
return hit.transform == targetCharacter; virtualCamera.Follow = null;
virtualCamera.LookAt = null;
}
else
{
if (targetCharacter != null)
{
virtualCamera.Follow = targetCharacter;
virtualCamera.LookAt = targetCharacter;
}
CenterOnHero();
} }
return false; }
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) public void ShakeCamera(float duration = 0.3f, float magnitude = 0.2f)
@ -209,7 +256,6 @@ public class EdgePanningController : MonoBehaviour
private IEnumerator ShakeRoutine(float duration, float magnitude) private IEnumerator ShakeRoutine(float duration, float magnitude)
{ {
float elapsed = 0f; float elapsed = 0f;
Vector3 originalOffset = defaultOffset;
while (elapsed < duration) while (elapsed < duration)
{ {
@ -217,11 +263,40 @@ public class EdgePanningController : MonoBehaviour
float y = Random.Range(-1f, 1f) * magnitude; float y = Random.Range(-1f, 1f) * magnitude;
float z = Random.Range(-1f, 1f) * magnitude; float z = Random.Range(-1f, 1f) * magnitude;
cameraOffset.m_Offset = originalOffset + new Vector3(x, y, z); // Apply shake on top of existing offset
Vector3 shakeOffset = new Vector3(x, y, z);
cameraOffset.m_Offset = panOffset + shakeOffset;
elapsed += Time.deltaTime; elapsed += Time.deltaTime;
yield return null; yield return null;
} }
cameraOffset.m_Offset = originalOffset; // Reset to default offset // 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);
}
} }
} }
Loading…
Cancel
Save