Untitled
unknown
plain_text
8 months ago
11 kB
10
Indexable
using UnityEngine;
using UnityEngine.InputSystem;
public class TopDownCameraController : MonoBehaviour
{
[Header("Target Settings")]
[SerializeField] private Transform target;
[Header("Camera Position")]
[Range(8f, 20f)]
[SerializeField] private float height = 10f;
[SerializeField] private float minHeight = 8f;
[SerializeField] private float maxHeight = 20f;
[SerializeField] private float scrollSensitivity = 2f;
[Header("Follow Settings")]
[SerializeField] private float followSmoothness = 5f;
[SerializeField] private float rotationSmoothness = 8f;
[SerializeField] private float movementThreshold = 0.1f;
[SerializeField] private float rotationStabilityThreshold = 0.05f;
[Header("Planetary Gravity Integration")]
[SerializeField] private bool usePlanetaryOrientation = true;
[SerializeField] private float gravityOrientationBlend = 3f;
[SerializeField] private float orientationStabilityTime = 1f;
[Header("Debug")]
[SerializeField] private bool showDebugRays = false;
private Camera cameraComponent;
private PlanetGravityAffected playerGravityComponent;
private Vector3 currentUpDirection = Vector3.up;
private Vector3 targetUpDirection = Vector3.up;
private Vector3 stableUpDirection = Vector3.up;
// Cache for smooth calculations
private Vector3 smoothedPlayerPosition;
private Quaternion smoothedCameraRotation;
private Vector3 lastPlayerPosition;
private float timeSinceLastMovement = 0f;
private bool isPlayerMoving = false;
private float orientationStabilityTimer = 0f;
// Input handling
private Mouse mouse;
private void Start()
{
cameraComponent = GetComponent<Camera>();
mouse = Mouse.current;
// Find player if not assigned
if (target == null)
{
GameObject player = GameObject.FindGameObjectWithTag("Player");
if (player != null)
{
target = player.transform;
}
}
// Get the player's gravity component for planetary orientation
if (target != null)
{
playerGravityComponent = target.GetComponent<PlanetGravityAffected>();
lastPlayerPosition = target.position;
}
// Initialize positions
if (target != null)
{
smoothedPlayerPosition = target.position;
UpdateCameraPosition();
}
smoothedCameraRotation = transform.rotation;
stableUpDirection = currentUpDirection;
}
private void Update()
{
if (target == null) return;
HandleScrollInput();
CheckPlayerMovement();
if (showDebugRays)
{
DrawDebugRays();
}
}
private void FixedUpdate()
{
if (target == null) return;
UpdatePlanetaryOrientation();
UpdateCameraPosition();
UpdateCameraRotation();
}
private void HandleScrollInput()
{
if (mouse != null && mouse.scroll.ReadValue() != Vector2.zero)
{
float scrollValue = mouse.scroll.ReadValue().y;
float heightChange = scrollValue * scrollSensitivity * Time.deltaTime;
SetHeight(height - heightChange);
}
}
private void CheckPlayerMovement()
{
if (target == null) return;
float movementDistance = Vector3.Distance(target.position, lastPlayerPosition);
isPlayerMoving = movementDistance > movementThreshold * Time.deltaTime;
if (isPlayerMoving)
{
timeSinceLastMovement = 0f;
}
else
{
timeSinceLastMovement += Time.deltaTime;
}
lastPlayerPosition = target.position;
}
private void UpdatePlanetaryOrientation()
{
if (!usePlanetaryOrientation || playerGravityComponent == null)
{
targetUpDirection = Vector3.up;
currentUpDirection = Vector3.up;
stableUpDirection = Vector3.up;
return;
}
// Get the current planet affecting the player
PlanetaryGravity currentPlanet = GetNearestPlanet();
if (currentPlanet != null)
{
// Calculate the "up" direction from the planet's center to the player
Vector3 directionFromPlanet = (target.position - currentPlanet.transform.position).normalized;
targetUpDirection = directionFromPlanet;
}
else
{
targetUpDirection = Vector3.up;
}
// Only update orientation if player is moving or enough time has passed for stability
bool shouldUpdateOrientation = isPlayerMoving || timeSinceLastMovement < orientationStabilityTime;
if (shouldUpdateOrientation)
{
// Check if the change in orientation is significant enough to warrant an update
float orientationDifference = Vector3.Angle(currentUpDirection, targetUpDirection);
if (orientationDifference > rotationStabilityThreshold || isPlayerMoving)
{
// Smoothly blend to the new orientation
float blendSpeed = isPlayerMoving ? gravityOrientationBlend : gravityOrientationBlend * 0.5f;
currentUpDirection = Vector3.Slerp(currentUpDirection, targetUpDirection, blendSpeed * Time.fixedDeltaTime);
orientationStabilityTimer = 0f;
}
else
{
orientationStabilityTimer += Time.fixedDeltaTime;
}
}
else
{
// When player is stationary for a while, lock to stable orientation
if (orientationStabilityTimer >= orientationStabilityTime)
{
stableUpDirection = currentUpDirection;
}
currentUpDirection = stableUpDirection;
}
}
private void UpdateCameraPosition()
{
// Use different smoothing based on whether player is moving
float positionSmoothness = isPlayerMoving ? followSmoothness : followSmoothness * 2f;
// Smooth the player position to reduce camera jitter
smoothedPlayerPosition = Vector3.Lerp(smoothedPlayerPosition, target.position, positionSmoothness * Time.fixedDeltaTime);
// Calculate the desired camera position above the player
Vector3 desiredPosition = smoothedPlayerPosition + (currentUpDirection * height);
// Move the camera to the desired position with appropriate smoothing
transform.position = Vector3.Lerp(transform.position, desiredPosition, positionSmoothness * Time.fixedDeltaTime);
}
private void UpdateCameraRotation()
{
// Calculate the rotation to look down at the player while maintaining proper "up" orientation
Vector3 directionToPlayer = (smoothedPlayerPosition - transform.position).normalized;
// Ensure we're looking down at the player with the correct up direction
Quaternion targetRotation = Quaternion.LookRotation(directionToPlayer, GetCameraUpDirection());
// Use different rotation smoothing based on movement and stability
float rotationSpeed = rotationSmoothness;
if (!isPlayerMoving && timeSinceLastMovement > orientationStabilityTime)
{
rotationSpeed *= 0.3f; // Much slower rotation when stationary
}
// Smooth the rotation
smoothedCameraRotation = Quaternion.Slerp(smoothedCameraRotation, targetRotation, rotationSpeed * Time.fixedDeltaTime);
transform.rotation = smoothedCameraRotation;
}
private Vector3 GetCameraUpDirection()
{
if (!usePlanetaryOrientation)
{
return Vector3.up;
}
// For planetary cameras, we want the "up" to be tangent to the planet surface
// This means the camera's up should be perpendicular to both the planet's radius and the camera's forward direction
Vector3 planetRadial = currentUpDirection;
Vector3 cameraForward = (smoothedPlayerPosition - transform.position).normalized;
// Calculate a tangent vector on the planet surface
Vector3 tangent = Vector3.Cross(planetRadial, Vector3.Cross(cameraForward, planetRadial)).normalized;
return tangent;
}
private PlanetaryGravity GetNearestPlanet()
{
PlanetaryGravity nearestPlanet = null;
float nearestDistance = float.MaxValue;
foreach (var planet in PlanetaryGravity.AllPlanets)
{
if (planet == null) continue;
float distance = Vector3.Distance(target.position, planet.transform.position);
if (distance < nearestDistance)
{
nearestDistance = distance;
nearestPlanet = planet;
}
}
return nearestPlanet;
}
private void DrawDebugRays()
{
if (target == null) return;
// Draw the up direction
Debug.DrawRay(target.position, currentUpDirection * height, Color.blue, 0f);
// Draw the camera to player direction
Debug.DrawLine(transform.position, target.position, Color.red, 0f);
// Draw camera's up direction
Debug.DrawRay(transform.position, transform.up * 2f, Color.green, 0f);
// Draw camera's forward direction
Debug.DrawRay(transform.position, transform.forward * 3f, Color.yellow, 0f);
}
public void SetTarget(Transform newTarget)
{
target = newTarget;
if (target != null)
{
playerGravityComponent = target.GetComponent<PlanetGravityAffected>();
smoothedPlayerPosition = target.position;
lastPlayerPosition = target.position;
timeSinceLastMovement = 0f;
}
}
public void SetHeight(float newHeight)
{
height = Mathf.Clamp(newHeight, minHeight, maxHeight);
}
// Method to enable/disable planetary orientation at runtime
public void SetPlanetaryOrientation(bool enable)
{
usePlanetaryOrientation = enable;
if (!enable)
{
targetUpDirection = Vector3.up;
}
}
// Validate height range in inspector
private void OnValidate()
{
minHeight = 8f;
maxHeight = 20f;
height = Mathf.Clamp(height, minHeight, maxHeight);
if (minHeight >= maxHeight)
{
maxHeight = minHeight + 1f;
}
}
}Editor is loading...
Leave a Comment