游戏物理与碰撞检测基础
概述
弹射、跑酷、射击类游戏都离不开碰撞检测:弹珠撞墙反弹、子弹命中目标、角色踩到机关。服务端做碰撞检测,一方面是要计算权威结果(伤害、反弹),另一方面是验证客户端上报的行为(反外挂)。本文介绍 2D 碰撞检测的常见实现、AABB 碰撞检测、服务端碰撞验证与反外挂检测。
一、2D 碰撞检测概览
1.1 常见碰撞体
| 形状 | 表示 | 碰撞检测成本 | 适用 |
|---|---|---|---|
| 点 | (x, y) | 极低 | 命中判定 |
| 圆 | 圆心 + 半径 | 低 | 弹射、角色 |
| AABB 矩形 | 中心 + 半宽高 | 低 | 平台、障碍 |
| OBB 旋转矩形 | 中心 + 向量 + 角度 | 中 | 旋转物体 |
| 多边形 | 顶点列表 | 高 | 复杂碰撞体 |
2D 游戏优先用圆/AABB:
计算简单、误差可接受
性能好(海量碰撞体)
OBB/多边形用于少数关键物体1.2 粗检测与精检测
两阶段:
粗检测(Broad Phase):
快速排除不相交的物体对
空间索引:网格/四叉树(与 AOI 同思路)
精检测(Narrow Phase):
对候选对做精确几何检测
圆 vs 圆 / AABB vs AABB碰撞性能关键:
粗检测把 O(N²) 降到 O(N log N) 或 O(N)
空间网格是最简单有效的粗检测二、AABB 碰撞检测
2.1 AABB 定义
AABB(Axis-Aligned Bounding Box)轴对齐包围盒:
边与世界坐标轴平行的矩形
表示:中心点 + 半宽高(或 min/max 顶点)
碰撞条件(两个 AABB):
A 与 B 相交 ⟺
|A.cx - B.cx| < A.hw + B.hw (X 轴投影重叠)
且 |A.cy - B.cy| < A.hh + B.hh (Y 轴投影重叠)2.2 检测实现
java
public class Aabb {
public final int cx, cy; // 中心
public final int hw, hh; // 半宽、半高
// 相交检测:两轴投影都重叠即相交
public boolean intersects(Aabb other) {
return Math.abs(this.cx - other.cx) < this.hw + other.hw
&& Math.abs(this.cy - other.cy) < this.hh + other.hh;
}
// 与圆相交(弹射/子弹用)
public boolean intersectsCircle(int ox, int oy, int radius) {
// 找 AABB 上距圆心最近的点,距离 <= radius
int px = Math.max(this.cx - this.hw, Math.min(ox, this.cx + this.hw));
int py = Math.max(this.cy - this.hh, Math.min(oy, this.cy + this.hh));
int dx = ox - px;
int dy = oy - py;
return dx * dx + dy * dy <= radius * radius;
}
}要点:
用整数坐标(确定性 + 避免浮点)
相交检测只有 2 次比较(AABB 极快)
中心+半宽高的表示便于移动更新2.3 圆与圆
圆 vs 圆:
distance(圆心A, 圆心B) < rA + rB → 碰撞
平方比较避免开根号:
dx² + dy² < (rA + rB)²弹射物理:
碰撞后速度沿法线反射
法线 = 两圆心连线方向
反射公式(速度投影翻转)三、碰撞响应基础
3.1 反弹
圆/球弹射:
入射速度 v,法线 n
反射速度 v' = v - 2(v·n)n
应用于弹珠、弹射类游戏
墙壁碰撞:法线为墙面方向,翻转对应轴速度
弹性系数:v' *= 恢复系数(0.9 等)3.2 阻挡与穿透修正
角色与障碍(AABB vs AABB):
碰撞后把角色推回(穿透修正)
修正方向 = 重叠最小的轴
防止角色陷入墙内
平台:
单向平台(只能从上方踩)→ 只测下向速度3.3 触发与命中
触发型碰撞(不反弹):
踩机关、拾取道具、进入区域
碰撞 → 触发事件(加 buff、掉血)
命中判定:
子弹 vs 目标:AABB/圆 相交 → 命中
技能范围:圆/AABB 覆盖检测四、服务端碰撞验证
4.1 为什么服务端要测碰撞
客户端上报的问题:
外挂改位置(瞬移/穿墙)
外挂改伤害/命中率
服务端验证:
用权威状态复算碰撞 → 判断上报是否合理
反外挂检测的核心手段4.2 验证方式
方式一:全量权威计算(状态同步)
服务器本来就算逻辑 → 自然验证
方式二:随机抽查(帧同步)
服务器不跑逻辑,但随机选帧复算
复算结果与客户端上报比对 → 差异即作弊
方式三:规则校验(轻量)
上报的位置变化必须在速度上限内
命中必须有"子弹/技能"先决条件java
public class AntiCheatValidator {
private static final int MAX_SPEED = 500; // 每 Tick 最大位移
// 移动验证:上报位移是否超速
public boolean validMove(long playerId, int fromX, int fromY, int toX, int toY) {
int dx = Math.abs(toX - fromX);
int dy = Math.abs(toY - fromY);
return dx * dx + dy * dy <= MAX_SPEED * MAX_SPEED;
}
// 命中验证:必须有合法弹道
public boolean validHit(long shooterId, long targetId, double distance) {
return distance < shooterRange(shooterId); // 射程校验
}
}验证分层:
移动:速度上限(防瞬移)
命中:射程/视线(防无中生有)
碰撞:权威复算(防穿墙)
概率:结果校验(防改概率)4.3 服务端碰撞计算成本
完整碰撞计算很重:
海量实体 × 每帧
优化:
只验证"上报触发的事件"(命中、碰撞反弹)
事件场景才复算(不是全图每帧)
空间网格缩小候选集
采样验证(随机抽 10% 事件)五、反外挂检测
5.1 常见外挂类型
| 外挂 | 表现 | 检测 |
|---|---|---|
| 瞬移 | 位移超速 | 速度上限校验 |
| 穿墙 | 进入不可达区域 | 碰撞复算 |
| 透视 | 命中视野外目标 | 射程/视线校验 |
| 自瞄 | 命中率异常 | 命中率统计 |
| 加速 | 动作频率异常 | 频控 + 行为分析 |
5.2 检测体系
实时检测:
位移/命中/概率逐条校验
超限 → 拒绝 + 记录
统计检测:
命中率 > 阈值
反应时间异常短
胜率模式异常
→ 人工复核/封禁
联动:
检测结果 → 行为日志(第 4 周)
可疑玩家进风控队列
封禁需分级:警告/限功能/封禁5.3 服务端权威的取舍
完全权威 vs 成本:
状态同步 → 全权威(算力换安全)
帧同步 → 抽查权威(性能换风险)
休闲游戏实践:
状态同步为主 → 天然权威
帧同步场景 → 抽查 + 规则校验
平衡:玩家体验与安全投入六、常见问题
| 问题 | 处理 |
|---|---|
| 碰撞穿模(高速移动) | 连续检测(细分步进) |
| 卡墙抖动 | 穿透修正 + 预留间隙 |
| 碰撞性能差 | 空间网格粗检测 |
| 上报与权威不符 | 以服务器为准 + 纠正 |
| 误封 | 分级处理 + 人工复核 |
高速穿透:
高速子弹可能一帧穿过薄墙
解决:射线检测(线段 vs AABB)
或把运动拆成多步细分检测七、小结
碰撞检测是游戏物理的基础设施:AABB 用中心+半宽高表示,相交判定只需两次投影比较,是 2D 游戏性价比最高的碰撞体;粗检测用空间网格缩小候选集,精检测再精确判定;碰撞响应涵盖反弹(法线反射)、穿透修正、触发三类。在服务端,碰撞不仅是玩法计算,更是反外挂的裁判——通过速度上限、射程校验、随机复算三层验证客户端上报,检测瞬移、穿墙、透视等作弊。选型上状态同步天然权威,帧同步用抽查补位,在体验与安全间取得平衡。