一、射线投射技术基础解析
1.1 射线投射的数学本质
射线投射(Ray Casting)本质是基于空间几何的碰撞检测算法,通过从观察点发射一条无限延伸的射线,检测与场景中物体的交点。在3D空间中,射线可表示为参数方程:P(t) = O + t * D
其中O为起点(通常是摄像机位置),D为归一化方向向量,t为距离参数。当t>0且最小时的交点即为最近的命中点。
1.2 CocosCreator中的实现架构
CocosCreator 3.x版本通过PhysicsSystem和Ray类封装了射线投射功能。核心流程分为三步:
- 射线生成:通过摄像机坐标和屏幕触摸点计算世界空间射线
- 物理查询:调用物理引擎进行碰撞检测
- 结果处理:解析命中信息并触发交互逻辑
物理系统支持两种查询模式:
- 射线投射查询(Raycast):返回第一个命中的物体
- 球形投射查询(SphereCast):适用于需要碰撞体积的场景
二、核心实现步骤详解
2.1 射线生成与转换
// 获取主摄像机节点const camera = find('MainCamera')?.getComponent(Camera)!;// 将屏幕坐标转换为世界空间射线function screenToWorldRay(screenPos: Vec2): Ray {// 1. 将屏幕坐标归一化到[-1,1]范围const ndc = new Vec3(screenPos.x / canvas.width * 2 - 1,screenPos.y / canvas.height * 2 - 1,1);// 2. 通过摄像机反投影计算射线方向const ray = new Ray();camera.screenPointToRay(ndc, ray);return ray;}
关键点说明:
- 需处理不同分辨率下的坐标归一化
- 深度值(Z坐标)影响射线长度,通常设为1获取从摄像机出发的标准射线
2.2 物理查询实现
import { physics } from 'cc';function checkRaycast(ray: Ray): boolean {const result = new physics.RaycastResult();const hit = physics.raycast(ray.origin,ray.direction,1000, // 最大检测距离0x01, // 碰撞组(需与目标物体匹配)result);if (hit) {console.log(`命中物体: ${result.collider.node.name}`);// 处理选中逻辑...return true;}return false;}
参数配置要点:
- 最大距离:根据场景规模合理设置,避免无效计算
- 碰撞组:通过
physics.CollisionGroup控制检测范围 - 结果对象:包含碰撞点、法线、碰撞体等关键信息
2.3 触摸事件集成
// 在Canvas节点上添加触摸事件canvas.node.on(Node.EventType.TOUCH_START, (event: EventTouch) => {const pos = event.getUILocation();const ray = screenToWorldRay(new Vec2(pos.x, pos.y));checkRaycast(ray);});
移动端适配建议:
- 使用
getUILocation()而非getLocation()确保坐标系统一致 - 添加触摸区域判断,避免UI元素干扰
三、性能优化策略
3.1 分层检测技术
通过physics.Layer实现选择性检测:
// 定义可选中物体层const SELECTABLE_LAYER = 1 << 0;// 在物体上设置碰撞层this.node.group = SELECTABLE_LAYER;// 查询时指定层physics.raycast(ray.origin, ray.direction, 1000, SELECTABLE_LAYER, result);
效果:
- 减少不必要的碰撞检测
- 提升复杂场景下的帧率稳定性
3.2 空间分区优化
对于大规模场景:
- 使用
QuadTree或Octree进行空间划分 - 先进行粗粒度区域检测,再执行精确射线投射
-
典型实现:
class SpacePartitioner {private trees: Map<number, QuadTree>;constructor() {this.trees = new Map();}query(bounds: Rect, callback: (node: Node) => void) {// 实现空间分区查询逻辑...}}
3.3 批处理检测
针对多物体同时检测场景:
function batchRaycast(rays: Ray[]): RaycastResult[] {return rays.map(ray => {const result = new physics.RaycastResult();physics.raycast(ray.origin, ray.direction, 1000, 0x01, result);return result;});}
适用场景:
- VR/AR中的多指触控
- 策略游戏中的范围选择
四、高级应用场景
4.1 穿透检测实现
通过修改射线参数实现穿透效果:
function penetratingRaycast(ray: Ray, maxHits: number): RaycastResult[] {const results: RaycastResult[] = [];let currentRay = ray.clone();for (let i = 0; i < maxHits; i++) {const result = new physics.RaycastResult();const hit = physics.raycast(currentRay.origin,currentRay.direction,1000,0x01,result);if (!hit) break;results.push(result);// 将射线起点移动到碰撞点后方继续检测currentRay.origin.add(currentRay.direction.multiplyScalar(result.distance + 0.01));}return results;}
4.2 预测性射线投射
结合物体运动预测的改进方案:
function predictiveRaycast(ray: Ray,target: Node,deltaTime: number): RaycastResult | null {// 1. 获取目标当前速度const rb = target.getComponent(RigidBody);const velocity = rb?.linearVelocity || Vec3.ZERO;// 2. 预测未来位置const futurePos = target.worldPosition.add(velocity.multiplyScalar(deltaTime));// 3. 构建预测射线const predictiveRay = new Ray(ray.origin,futurePos.subtract(ray.origin).normalize());// 4. 执行检测const result = new physics.RaycastResult();return physics.raycast(predictiveRay.origin, predictiveRay.direction, 1000, 0x01, result)? result: null;}
五、常见问题解决方案
5.1 检测失效排查
-
摄像机投影矩阵问题:
- 检查
Camera组件的projection属性是否为PERSPECTIVE - 验证
nearClip和farClip值是否合理
- 检查
-
碰撞体配置错误:
- 确保目标物体有
Collider组件 - 检查
isTrigger属性是否符合需求
- 确保目标物体有
-
坐标系不匹配:
- 统一使用世界坐标进行计算
- 避免混合使用局部坐标和世界坐标
5.2 移动端适配建议
-
触摸精度优化:
// 增加触摸容错区域const touchThreshold = 10; // 像素function isNearPreviousTouch(pos: Vec2, prevPos: Vec2): boolean {return pos.distanceTo(prevPos) < touchThreshold;}
-
多指触控处理:
canvas.node.on(Node.EventType.TOUCH_MOVE, (event: EventTouch) => {const touches = event.getTouches();if (touches.length > 1) {// 处理双指缩放等手势}});
六、最佳实践总结
-
分层架构设计:
- 将射线检测逻辑封装为独立模块
- 使用事件系统解耦检测与响应
-
性能监控:
// 在关键位置添加性能标记if (CC_DEBUG) {console.time('raycast');// 执行检测...console.timeEnd('raycast');}
-
资源管理:
- 及时销毁不再需要的
RaycastResult对象 - 对静态场景预计算检测数据
- 及时销毁不再需要的
-
跨平台兼容:
- 统一输入处理接口
- 针对不同设备调整检测参数
通过系统掌握射线投射技术,开发者能够高效实现3D场景中的物体交互,为游戏和仿真应用构建更自然的操作体验。建议结合具体项目需求,在基础实现上逐步添加优化层,达到性能与效果的平衡。