Three.js Octree碰撞检测求助:角色穿模与GLB地图配置疑问
解决GLB地图与角色的碰撞检测问题
先修正你的代码核心问题
你当前代码的关键错误是生成Octree之后才调整模型位置,导致Octree存储的碰撞数据和模型实际位置不匹配,直接引发穿模问题。调整后的代码如下:
const worldOctree = new Octree(); export function setupLevel(scene) { const loader = new GLTFLoader(); loader.load( "../../models/level.glb", (gltf) => { // 先调整模型位置,再生成Octree gltf.scene.position.y += 0.1; scene.add(gltf.scene); // 基于调整好位置的模型生成碰撞数据 worldOctree.fromGraphNode(gltf.scene); const helper = new OctreeHelper(worldOctree); helper.visible = true; scene.add(helper); }, (xhr) => { console.log((xhr.loaded / xhr.total) * 100 + "% loaded"); }, (error) => { console.error(error); } ); return worldOctree; }
额外注意:fromGraphNode只会遍历处理场景中的Mesh对象,若GLB里有嵌套的Group会自动递归,但SkinnedMesh(骨骼动画模型)无法被Octree识别,这类模型需要单独提取静态碰撞几何体。
地图GLB文件的必要设置
- 确保碰撞区域是闭合实心几何体:如果模型存在面缺失、镂空或非闭合结构,Octree生成的碰撞体会有漏洞,直接导致角色穿模。
- 简化碰撞网格:如果地图面数过多,建议导出时单独制作一套低面数的碰撞网格(可隐藏在GLB中,仅用于碰撞检测),避免Octree生成过慢或占用过多内存。
- 分离动态部件:地图中的动态物体不要加入全局
worldOctree,需单独处理其碰撞逻辑。
碰撞检测的正确调用逻辑
生成Octree后,需要在角色每帧移动时执行碰撞检测并修正位置,参考FPS示例的胶囊体碰撞逻辑:
// 假设角色使用胶囊体碰撞,需定义position、height、radius属性 const capsule = new Capsule( character.position.clone().add(new THREE.Vector3(0, character.height/2, 0)), character.position.clone().add(new THREE.Vector3(0, -character.height/2, 0)), character.radius ); // 移动前检测碰撞 const collisionResult = worldOctree.capsuleIntersect(capsule); if (collisionResult) { // 修正角色位置,避免穿模 capsule.translate(collisionResult.normal.multiplyScalar(collisionResult.distance)); character.position.copy(capsule.start).sub(new THREE.Vector3(0, character.height/2, 0)); }
常见排查点
- 通过
OctreeHelper查看碰撞体范围是否与地图匹配,若不匹配,检查模型位置调整时机或是否有未被识别的Mesh。 - 检查模型缩放:若模型有缩放,需在生成Octree前应用缩放并调用
mesh.updateMatrixWorld(),否则碰撞数据会与实际模型大小不符。 - 调整角色碰撞体参数:碰撞体不要过小(易穿模)或过大(易被卡住),需匹配角色模型的实际尺寸。
内容的提问来源于stack exchange,提问作者user20637858
相关产品推荐
相关产品推荐

