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; } } }