using UnityEngine; namespace IndianOceanAssets.Engine2_5D { /// /// 摄像机边界(空气墙)—— 框定摄像机可移动的矩形范围。 /// 摄像机碰到边界后停下,玩家仍可继续走。 /// /// 原理:在 LateUpdate(晚于 CameraFollow 的 Update)把摄像机位置钳制到 /// 以本物体为中心、size 为尺寸的矩形内(仅 XZ 平面,Y 不限制)。 /// [DefaultExecutionOrder(10000)] 保证本脚本在其它 LateUpdate 之后执行, /// 即使以后把 CameraFollow 改成 LateUpdate 也能正确钳制。 /// /// 为什么不会抖: /// CameraFollow 每帧用 Lerp 把摄像机往目标推(可能推出边界), /// 本脚本随后把它钳回边界。边界是固定直线,钳制结果每帧一致 → 稳定不抖。 /// /// 用法: /// 1. 场景中创建空物体,挂上此脚本 /// 2. 把空物体放到摄像机活动范围的中心点 /// 3. 设置 size(X=宽度,Y=深度,以本物体位置为中心) /// 4. targetCamera 留空则自动用 Camera.main /// 5. Scene 视图会显示青色线框,方便对齐 /// [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) ); } /// /// 把世界坐标钳制到边界内(仅 XZ,Y 不变)。供外部调用。 /// 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)); } } }