深入分析物理引擎后,他写了一个轻量的 Cocos 3D 碰撞检测优化方案

引言:碰撞检测是游戏开发中一个非常重要的技术点,优化碰撞检测性能,是提升游戏体验不可或缺的一环。开发者「我叫98K」写了一个轻量碰撞系统,用以改善 3D 游戏在不同平台遇到的碰撞性能问题和包体问题。下载和在线体验地址见文末。

98K物理-轻量碰撞系统是一个高性能轻量 3D 碰撞管理器,适用于 Cocos Creator 3.4 及以上版本,对 Mesh 模型和基本几何体提供高效的碰撞系统和射线检测系统,以提升游戏在不同平台上的 3D 碰撞检测性能,减少包体大小(尤其是 H5 平台)。

先通过在线体验,看几组应用效果:

http://www.cocospro.com/98K/

2b28c39deddb84731a7548a1788653ff.gif

场景1,碰撞测试

d1b1ce0a3cb2bdddac117806b8a54e36.gif

场景1,1000射线测试

4fa4156dd8c51b109dc43cbd16ff371e.gif

场景2,碰撞测试

516946324d4e3565e162e7789116a6a9.gif

场景2,1000射线测试

本文主要和大家详细介绍 98K 中最为重要的自定义碰撞系统的功能与使用、及其实现思路与技术要点,感兴趣的小伙伴可以深入了解。

功能特点

98K 的主要功能特点有:

  • 多物体场景管理:Octree,对场景物体进行高效划分查询。

  • 模型三角化管理:Kdtree,对物体表面进行高效划分查询。

  • 通用 3D 碰撞计算:GJK+EPA,精确计算修正碰撞后的物体。

  • 3D 角色控制器:3D 物体在场景碰撞系统下的自由移动。

  • 高效射线检测:基于 Octree 和 Kdtree 对射线检测加速。

我们在 H5 环境下,对比 98K 和 Bullet,PhysX(由于 Cannon 对 mesh collider 支持不完善,不参与比较)。

b29fc799fb8a927bc884d4cb0a02d5c1.png

虽然和 Bullet、PhysX 相比,98K 的功能还不够全面,但是在需要使用 3D 碰撞检测和射线检测的 MMO、SLG、FPS 等 3D 场景的游戏中,目前 9

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用四叉树优化碰撞检测的示例代码: ```javascript // 定义四叉树节点类 class QuadTreeNode { constructor(x, y, width, height, maxObjects, maxLevels, level) { this.x = x; // 节点左上角的 x 坐标 this.y = y; // 节点左上角的 y 坐标 this.width = width; // 节点的宽度 this.height = height; // 节点的高度 this.maxObjects = maxObjects; // 节点最多存储的对象数 this.maxLevels = maxLevels; // 四叉树最大深度 this.level = level; // 节点深度 this.objects = []; // 存储在节点中的所有对象 this.nodes = []; // 存储子节点 } // 分裂节点,将节点分成四个子节点 split() { const subWidth = this.width / 2; const subHeight = this.height / 2; const x = this.x; const y = this.y; this.nodes[0] = new QuadTreeNode(x + subWidth, y, subWidth, subHeight, this.maxObjects, this.maxLevels, this.level + 1); this.nodes[1] = new QuadTreeNode(x, y, subWidth, subHeight, this.maxObjects, this.maxLevels, this.level + 1); this.nodes[2] = new QuadTreeNode(x, y + subHeight, subWidth, subHeight, this.maxObjects, this.maxLevels, this.level + 1); this.nodes[3] = new QuadTreeNode(x + subWidth, y + subHeight, subWidth, subHeight, this.maxObjects, this.maxLevels, this.level + 1); } // 获取对象所在的子节点 getIndex(rect) { const verticalMidpoint = this.x + this.width / 2; const horizontalMidpoint = this.y + this.height / 2; const topQuadrant = rect.y < horizontalMidpoint && rect.y + rect.height < horizontalMidpoint; const bottomQuadrant = rect.y > horizontalMidpoint; let index = -1; if (rect.x < verticalMidpoint && rect.x + rect.width < verticalMidpoint) { if (topQuadrant) { index = 1; } else if (bottomQuadrant) { index = 2; } } else if (rect.x > verticalMidpoint) { if (topQuadrant) { index = 0; } else if (bottomQuadrant) { index = 3; } } return index; } // 插入对象到四叉树中 insert(rect) { if (this.nodes.length) { const index = this.getIndex(rect); if (index !== -1) { this.nodes[index].insert(rect); return; } } this.objects.push(rect); if (this.objects.length > this.maxObjects && this.level < this.maxLevels) { if (!this.nodes.length) { this.split(); } let i = 0; while (i < this.objects.length) { const index = this.getIndex(this.objects[i]); if (index !== -1) { this.nodes[index].insert(this.objects.splice(i, 1)[0]); } else { i++; } } } } // 获取所有与指定对象碰撞的对象 retrieve(rect) { const index = this.getIndex(rect); let foundObjects = this.objects; if (this.nodes.length) { if (index !== -1) { foundObjects = foundObjects.concat(this.nodes[index].retrieve(rect)); } else { for (let i = 0; i < this.nodes.length; i++) { foundObjects = foundObjects.concat(this.nodes[i].retrieve(rect)); } } } return foundObjects; } } // 定义游戏场景类 class GameScene { constructor() { this.snake = null; // 贪吃蛇 this.foods = []; // 食物 this.quadTree = new QuadTreeNode(0, 0, 960, 640, 10, 5, 0); // 四叉树 } // 初始化游戏场景 init() { // 创建贪吃蛇和食物 this.snake = new Snake(); for (let i = 0; i < 10; i++) { const food = new Food(); this.foods.push(food); this.quadTree.insert(food.rect); } } // 更新游戏场景 update(dt) { // 移动贪吃蛇 this.snake.move(dt); // 检测贪吃蛇与食物的碰撞 const snakeRect = this.snake.getRect(); const collidedFoods = this.quadTree.retrieve(snakeRect); for (let i = 0; i < collidedFoods.length; i++) { const food = collidedFoods[i]; if (this.snake.checkCollision(food.rect)) { // 贪吃蛇吃到了食物 this.snake.eat(food); // 从场景中移除食物 this.foods.splice(this.foods.indexOf(food), 1); this.quadTree.objects.splice(this.quadTree.objects.indexOf(food.rect), 1); // 创建新的食物并加入场景 const newFood = new Food(); this.foods.push(newFood); this.quadTree.insert(newFood.rect); } } } // 渲染游戏场景 render() { // 渲染贪吃蛇和食物 this.snake.render(); for (let i = 0; i < this.foods.length; i++) { this.foods[i].render(); } } } // 定义食物类 class Food { constructor() { this.rect = new Rect(Math.floor(Math.random() * 960), Math.floor(Math.random() * 640), 10, 10); } render() { // 渲染食物 } } // 定义矩形类 class Rect { constructor(x, y, width, height) { this.x = x; this.y = y; this.width = width; this.height = height; } } // 定义贪吃蛇类 class Snake { constructor() { this.body = [new Rect(100, 100, 10, 10), new Rect(90, 100, 10, 10), new Rect(80, 100, 10, 10)]; this.direction = 'right'; } // 移动贪吃蛇 move(dt) { // 根据方向移动贪吃蛇 } // 检测贪吃蛇与其他对象的碰撞 checkCollision(rect) { // 检测贪吃蛇头部是否与指定矩形相交 } // 贪吃蛇吃到食物 eat(food) { // 在贪吃蛇尾部添加一个新的矩形 } // 获取贪吃蛇的矩形 getRect() { // 返回贪吃蛇头部的矩形 } render() { // 渲染贪吃蛇 } } // 创建游戏场景对象并初始化 const gameScene = new GameScene(); gameScene.init(); // 游戏循环 function gameLoop(dt) { // 更新游戏场景 gameScene.update(dt); // 渲染游戏场景 gameScene.render(); // 继续下一帧循环 requestAnimationFrame(gameLoop); } // 开始游戏循环 requestAnimationFrame(gameLoop); ``` 以上代码仅为示例,实际使用时需要根据项目需求进行修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值