118 lines
3.8 KiB
C#
118 lines
3.8 KiB
C#
using System.Collections;
|
|
using UnityEngine;
|
|
|
|
namespace BulletHellTemplate
|
|
{
|
|
/// <summary>
|
|
/// Controls a 2D top-down camera that follows a target tagged as "Character".
|
|
/// Includes smoothing, zoom control, bounds restriction and camera shake.
|
|
/// </summary>
|
|
[ExecuteInEditMode]
|
|
public class TopDown2DCameraController : MonoBehaviour
|
|
{
|
|
public static TopDown2DCameraController Instance { get; private set; }
|
|
|
|
[Header("Camera Target")]
|
|
[Tooltip("The camera will follow this target. If left null, it will search for GameObject with tag 'Character'.")]
|
|
public Transform target;
|
|
|
|
[Header("Camera Settings")]
|
|
[Tooltip("Camera offset relative to the target.")]
|
|
public Vector3 targetOffset = new Vector3(0f, 0f, -10f);
|
|
|
|
[Tooltip("Enable smooth camera following.")]
|
|
public bool smoothFollow = true;
|
|
|
|
[Tooltip("Smoothing speed (higher = slower).")]
|
|
public float followSmoothing = 10f;
|
|
|
|
[Tooltip("Orthographic size (zoom) of the camera.")]
|
|
public float cameraZoom = 5f;
|
|
|
|
[Header("Bounds Restriction")]
|
|
[Tooltip("Restrict camera movement within these world bounds (optional).")]
|
|
public bool limitCameraBounds = false;
|
|
public Vector2 minBounds;
|
|
public Vector2 maxBounds;
|
|
|
|
[Header("Camera Shake")]
|
|
[Tooltip("Shake intensity for camera shake effect.")]
|
|
public float shakeIntensity = 0.5f;
|
|
|
|
[Tooltip("Duration of the shake in seconds.")]
|
|
public float shakeDuration = 0.5f;
|
|
|
|
private Camera cam;
|
|
private Vector3 targetPosition;
|
|
private Vector3 finalCameraPosition;
|
|
private Vector3 shakeOffset;
|
|
private bool isShaking = false;
|
|
|
|
private void Awake()
|
|
{
|
|
if (Instance == null) Instance = this;
|
|
else Destroy(gameObject);
|
|
|
|
cam = Camera.main;
|
|
}
|
|
|
|
private void LateUpdate()
|
|
{
|
|
if (target == null)
|
|
target = GameObject.FindWithTag("Character")?.transform;
|
|
|
|
if (target == null || cam == null)
|
|
return;
|
|
|
|
cam.orthographicSize = cameraZoom;
|
|
targetPosition = target.position + targetOffset;
|
|
|
|
if (smoothFollow)
|
|
finalCameraPosition = Vector3.Lerp(transform.position, targetPosition, followSmoothing * Time.deltaTime);
|
|
else
|
|
finalCameraPosition = targetPosition;
|
|
|
|
// Apply shake offset
|
|
finalCameraPosition += isShaking ? shakeOffset : Vector3.zero;
|
|
|
|
// Apply bounds
|
|
if (limitCameraBounds)
|
|
{
|
|
float camHeight = cam.orthographicSize;
|
|
float camWidth = camHeight * cam.aspect;
|
|
|
|
finalCameraPosition.x = Mathf.Clamp(finalCameraPosition.x, minBounds.x + camWidth, maxBounds.x - camWidth);
|
|
finalCameraPosition.y = Mathf.Clamp(finalCameraPosition.y, minBounds.y + camHeight, maxBounds.y - camHeight);
|
|
}
|
|
|
|
// Only X and Y are affected in 2D
|
|
transform.position = new Vector3(finalCameraPosition.x, finalCameraPosition.y, targetOffset.z);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Starts a camera shake with the default intensity and duration.
|
|
/// </summary>
|
|
public void TriggerCameraShake()
|
|
{
|
|
if (!isShaking)
|
|
StartCoroutine(CameraShakeCoroutine());
|
|
}
|
|
|
|
private IEnumerator CameraShakeCoroutine()
|
|
{
|
|
isShaking = true;
|
|
float timer = 0f;
|
|
|
|
while (timer < shakeDuration)
|
|
{
|
|
shakeOffset = (Vector3)(Random.insideUnitCircle * shakeIntensity);
|
|
timer += Time.deltaTime;
|
|
yield return null;
|
|
}
|
|
|
|
shakeOffset = Vector3.zero;
|
|
isShaking = false;
|
|
}
|
|
}
|
|
}
|