Files
gold_dolphin/unity/Assets/camera/CameraBounds.cs
2026-06-28 12:25:22 +08:00

123 lines
4.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
namespace IndianOceanAssets.Engine2_5D
{
/// <summary>
/// 摄像机边界(空气墙)—— 框定摄像机可移动的矩形范围。
/// 摄像机碰到边界后停下,玩家仍可继续走。
///
/// 原理:在 LateUpdate晚于 CameraFollow 的 Update把摄像机位置钳制到
/// 以本物体为中心、size 为尺寸的矩形内(仅 XZ 平面Y 不限制)。
/// [DefaultExecutionOrder(10000)] 保证本脚本在其它 LateUpdate 之后执行,
/// 即使以后把 CameraFollow 改成 LateUpdate 也能正确钳制。
///
/// 为什么不会抖:
/// CameraFollow 每帧用 Lerp 把摄像机往目标推(可能推出边界),
/// 本脚本随后把它钳回边界。边界是固定直线,钳制结果每帧一致 → 稳定不抖。
///
/// 用法:
/// 1. 场景中创建空物体,挂上此脚本
/// 2. 把空物体放到摄像机活动范围的中心点
/// 3. 设置 sizeX=宽度Y=深度,以本物体位置为中心)
/// 4. targetCamera 留空则自动用 Camera.main
/// 5. Scene 视图会显示青色线框,方便对齐
/// </summary>
[DefaultExecutionOrder(10000)]
public class CameraBounds : MonoBehaviour
{
[Tooltip("要约束的摄像机(留空则使用 Camera.main")]
[SerializeField] private Camera targetCamera;
[Tooltip("边界尺寸X=宽度Y=深度,以本物体位置为中心)")]
[SerializeField] private Vector2 size = new Vector2(30f, 30f);
[Tooltip("Scene 视图是否显示边界线框")]
[SerializeField] private bool drawGizmo = true;
[Tooltip("线框颜色")]
[SerializeField] private Color gizmoColor = Color.cyan;
private Transform _camTransform;
private bool _resolved;
private void Start()
{
ResolveCamera();
}
private void ResolveCamera()
{
var cam = targetCamera != null ? targetCamera : Camera.main;
if (cam != null)
{
_camTransform = cam.transform;
_resolved = true;
}
else
{
_resolved = false;
}
}
private void LateUpdate()
{
if (!_resolved || _camTransform == null)
{
ResolveCamera();
if (_camTransform == null) return;
}
Vector3 pos = _camTransform.position;
Vector3 center = transform.position;
float minX = center.x - size.x * 0.5f;
float maxX = center.x + size.x * 0.5f;
float minZ = center.z - size.y * 0.5f;
float maxZ = center.z + size.y * 0.5f;
_camTransform.position = new Vector3(
Mathf.Clamp(pos.x, minX, maxX),
pos.y,
Mathf.Clamp(pos.z, minZ, maxZ)
);
}
/// <summary>
/// 把世界坐标钳制到边界内(仅 XZY 不变)。供外部调用。
/// </summary>
public Vector3 ClampPosition(Vector3 pos)
{
Vector3 center = transform.position;
return new Vector3(
Mathf.Clamp(pos.x, center.x - size.x * 0.5f, center.x + size.x * 0.5f),
pos.y,
Mathf.Clamp(pos.z, center.z - size.y * 0.5f, center.z + size.y * 0.5f)
);
}
private void OnDrawGizmos()
{
if (!drawGizmo) return;
Vector3 c = transform.position;
float hx = size.x * 0.5f;
float hz = size.y * 0.5f;
Vector3 p1 = new Vector3(c.x - hx, c.y, c.z - hz);
Vector3 p2 = new Vector3(c.x + hx, c.y, c.z - hz);
Vector3 p3 = new Vector3(c.x + hx, c.y, c.z + hz);
Vector3 p4 = new Vector3(c.x - hx, c.y, c.z + hz);
Gizmos.color = gizmoColor;
Gizmos.DrawLine(p1, p2);
Gizmos.DrawLine(p2, p3);
Gizmos.DrawLine(p3, p4);
Gizmos.DrawLine(p4, p1);
// 中心十字
Gizmos.color = new Color(gizmoColor.r, gizmoColor.g, gizmoColor.b, 0.6f);
const float k = 0.5f;
Gizmos.DrawLine(c - new Vector3(k, 0, 0), c + new Vector3(k, 0, 0));
Gizmos.DrawLine(c - new Vector3(0, 0, k), c + new Vector3(0, 0, k));
}
}
}