using System.Collections;
using UnityEngine;
namespace BulletHellTemplate
{
///
/// A script for controlling a top-down camera in Unity. The camera follows a target object
/// (tagged as "Character") with smooth or fixed movement and allows for adjustable lateral view angle,
/// as well as rotation adjustments on the X and Y axes.
///
[ExecuteInEditMode]
public class TopDownCameraController : MonoBehaviour
{
public static TopDownCameraController Singleton { get; private set; } // Singleton instance
///
/// The camera to be controlled by this script.
///
public Camera targetCamera;
///
/// Offset from the target's position. Adjusts the camera's height and lateral position.
///
public Vector3 targetOffset;
///
/// Determines if the camera should follow the target smoothly or instantly.
///
public bool smoothFollow = true;
///
/// The smoothing factor used for smooth following. Higher values result in slower movement.
///
public float followSmoothing = 10.0f;
///
/// The angle to adjust the camera's lateral view.
///
public float lateralViewAngle = 0f;
///
/// The angle to rotate the camera around the X axis.
///
public float xRotation = 90f;
///
/// The angle to rotate the camera around the Y axis.
///
public float yRotation = 0f;
///
/// The intensity of the camera shake effect.
///
public float shakeIntensity = 0.5f;
///
/// The duration of the camera shake effect in seconds.
///
public float shakeDuration = 0.5f;
///
/// Caches the camera transform for optimization.
///
private Transform cacheCameraTransform;
///
/// Reference to the target transform (the character).
///
private Transform target;
///
/// Stores the original camera position before shaking.
///
private Vector3 originalPosition;
///
/// Flag to control the camera shake coroutine.
///
private bool isShaking = false;
///
/// Stores the target camera position after following the character, but before shake.
///
private Vector3 finalCameraPosition;
protected virtual void Awake()
{
// Implement Singleton pattern
if (Singleton == null)
{
Singleton = this;
}
else if (Singleton != this)
{
Destroy(gameObject);
return;
}
InitializeCamera();
}
///
/// Initializes the camera reference and caches the camera's transform.
///
private void InitializeCamera()
{
if (targetCamera == null)
targetCamera = GetComponent();
cacheCameraTransform = targetCamera.transform;
originalPosition = cacheCameraTransform.localPosition; // Cache the original position for shake
}
///
/// Finds the target object with the "Character" tag in the scene.
///
public void SetTarget(Transform _target)
{
target = _target;
}
protected virtual void LateUpdate()
{
if (target != null)
{
Vector3 targetPosition = target.position + targetOffset;
if (smoothFollow)
{
finalCameraPosition = Vector3.Lerp(cacheCameraTransform.position, targetPosition, followSmoothing * Time.deltaTime);
}
else
{
finalCameraPosition = targetPosition;
}
// Apply lateral view angle
finalCameraPosition += cacheCameraTransform.right * lateralViewAngle;
// Apply rotation to the camera
cacheCameraTransform.rotation = Quaternion.Euler(xRotation, yRotation, 0);
// Ensure the camera looks down on the target
cacheCameraTransform.rotation = Quaternion.Euler(xRotation, yRotation, 0);
// Set the camera's final position before applying shake
cacheCameraTransform.position = finalCameraPosition;
// Apply camera shake, if active
if (isShaking)
{
ApplyCameraShake();
}
}
}
///
/// Triggers the camera shake effect with the specified intensity and duration.
///
public void TriggerCameraShake()
{
if (!isShaking)
{
StartCoroutine(CameraShakeCoroutine());
}
}
///
/// Coroutine that handles the camera shake effect.
///
/// IEnumerator for the coroutine.
private IEnumerator CameraShakeCoroutine()
{
isShaking = true;
float elapsedTime = 0f;
while (elapsedTime < shakeDuration)
{
ApplyCameraShake();
elapsedTime += Time.deltaTime;
yield return null;
}
// Reset the camera's position after shaking
cacheCameraTransform.position = finalCameraPosition;
isShaking = false;
}
///
/// Applies the camera shake effect to the final camera position.
///
private void ApplyCameraShake()
{
Vector3 randomOffset = Random.insideUnitSphere * shakeIntensity;
randomOffset.z = 0; // Prevent shaking in the Z-axis for 2D purposes
cacheCameraTransform.position = finalCameraPosition + randomOffset;
}
}
}