108 lines
3.4 KiB
C#
108 lines
3.4 KiB
C#
|
|
using UnityEngine;
|
||
|
|
using UnityEngine.InputSystem;
|
||
|
|
|
||
|
|
namespace MedievalShopkeeper
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// Static Top-Down Camera for Medieval Shopkeeper.
|
||
|
|
/// Positions the camera statically above the shop floor with a customizable height and pitch angle.
|
||
|
|
/// Does not follow the player, giving a fixed overview of the shop.
|
||
|
|
/// </summary>
|
||
|
|
[ExecuteAlways]
|
||
|
|
[RequireComponent(typeof(Camera))]
|
||
|
|
public class TopDownCamera : MonoBehaviour
|
||
|
|
{
|
||
|
|
[Header("View Settings")]
|
||
|
|
[Tooltip("World position the camera looks down at (e.g. center of the shop).")]
|
||
|
|
[SerializeField] private Vector3 focusPoint = Vector3.zero;
|
||
|
|
|
||
|
|
[Tooltip("Height of the camera above the shop floor.")]
|
||
|
|
[Range(5f, 50f)]
|
||
|
|
[SerializeField] private float cameraHeight = 16f;
|
||
|
|
|
||
|
|
[Tooltip("Pitch angle (degrees): 90 = straight down (birds-eye), 55-65 = top-down angled/isometric.")]
|
||
|
|
[Range(30f, 90f)]
|
||
|
|
[SerializeField] private float pitchAngle = 60f;
|
||
|
|
|
||
|
|
[Tooltip("Yaw angle (degrees) to rotate around the shop.")]
|
||
|
|
[Range(-180f, 180f)]
|
||
|
|
[SerializeField] private float yawAngle = 0f;
|
||
|
|
|
||
|
|
[Header("Optional Zoom (PC Scroll Wheel / Pinch)")]
|
||
|
|
[Tooltip("Allow zooming in/out while keeping the camera static over the shop.")]
|
||
|
|
[SerializeField] private bool enableZoom = true;
|
||
|
|
[SerializeField] private float zoomSpeed = 2f;
|
||
|
|
[SerializeField] private float minHeight = 8f;
|
||
|
|
[SerializeField] private float maxHeight = 30f;
|
||
|
|
|
||
|
|
private void Start()
|
||
|
|
{
|
||
|
|
ApplyCameraTransform();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void LateUpdate()
|
||
|
|
{
|
||
|
|
#if UNITY_EDITOR
|
||
|
|
if (!Application.isPlaying)
|
||
|
|
{
|
||
|
|
ApplyCameraTransform();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
if (enableZoom)
|
||
|
|
{
|
||
|
|
HandleZoomInput();
|
||
|
|
}
|
||
|
|
|
||
|
|
ApplyCameraTransform();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void HandleZoomInput()
|
||
|
|
{
|
||
|
|
float scroll = 0f;
|
||
|
|
|
||
|
|
if (Mouse.current != null)
|
||
|
|
{
|
||
|
|
scroll = Mouse.current.scroll.ReadValue().y;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (Mathf.Abs(scroll) > 0.01f)
|
||
|
|
{
|
||
|
|
cameraHeight -= Mathf.Sign(scroll) * zoomSpeed;
|
||
|
|
cameraHeight = Mathf.Clamp(cameraHeight, minHeight, maxHeight);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Positions and angles the camera so it statically frames the shop floor from above.
|
||
|
|
/// </summary>
|
||
|
|
[ContextMenu("Apply Camera Placement")]
|
||
|
|
public void ApplyCameraTransform()
|
||
|
|
{
|
||
|
|
Quaternion rotation = Quaternion.Euler(pitchAngle, yawAngle, 0f);
|
||
|
|
|
||
|
|
// Calculate distance backwards based on height and pitch
|
||
|
|
float rad = Mathf.Clamp(pitchAngle, 10f, 90f) * Mathf.Deg2Rad;
|
||
|
|
float distanceBack = pitchAngle >= 89.9f ? 0f : cameraHeight / Mathf.Tan(rad);
|
||
|
|
|
||
|
|
Vector3 offset = Quaternion.Euler(0f, yawAngle, 0f) * new Vector3(0f, cameraHeight, -distanceBack);
|
||
|
|
transform.position = focusPoint + offset;
|
||
|
|
transform.rotation = rotation;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void OnValidate()
|
||
|
|
{
|
||
|
|
ApplyCameraTransform();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void OnDrawGizmosSelected()
|
||
|
|
{
|
||
|
|
Gizmos.color = Color.green;
|
||
|
|
Gizmos.DrawWireSphere(focusPoint, 0.5f);
|
||
|
|
Gizmos.DrawLine(transform.position, focusPoint);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|