使用Three.js与cannon-es开发骰子滚轮时凸多面体碰撞致崩溃求助
问题分析与解决方案
问题根源
你遇到的碰撞崩溃问题,本质是手动构造的CANNON.ConvexPolyhedron顶点与面数据存在错误:
STL文件的顶点是按三角形面重复存储的(同一个空间点会被多个相邻面重复记录),你直接将所有重复顶点存入points,并按顺序生成面索引,导致Cannon在碰撞裁剪时访问到异常的未定义向量,触发Cannot read properties of undefined (reading 'x')错误。
修复方案
方案一:自动计算凸包(推荐,最简单)
利用CANNON.ConvexHull自动处理顶点去重与凸多面体生成,无需手动维护面数据:
function createConvexHull(mesh: THREE.Mesh) { const positionArray = mesh.geometry.attributes.position.array; const points: CANNON.Vec3[] = []; // 收集STL所有顶点(重复也没关系,ConvexHull会自动去重) for (let i = 0; i < positionArray.length; i += 3) { points.push(new CANNON.Vec3( positionArray[i], positionArray[i + 1], positionArray[i + 2] )); } // 自动生成凸包的顶点与面 const convexHull = new CANNON.ConvexHull(); convexHull.build(points); const convexGeometry = new CANNON.ConvexPolyhedron({ vertices: convexHull.vertices, faces: convexHull.faces }); const body = new CANNON.Body({ mass: 1 }); body.addShape(convexGeometry); return body; }
方案二:手动去重顶点(适合需要自定义面数据的场景)
先对STL顶点做去重处理,再基于唯一顶点生成正确的面索引:
function createConvexHull(mesh: THREE.Mesh) { const positionArray = mesh.geometry.attributes.position.array; const vertexMap = new Map<string, number>(); const uniquePoints: CANNON.Vec3[] = []; const faces: number[][] = []; // 第一步:去重顶点,生成唯一顶点列表(用固定精度避免浮点误差) for (let i = 0; i < positionArray.length; i += 3) { const x = positionArray[i].toFixed(6); const y = positionArray[i + 1].toFixed(6); const z = positionArray[i + 2].toFixed(6); const key = `${x},${y},${z}`; if (!vertexMap.has(key)) { vertexMap.set(key, uniquePoints.length); uniquePoints.push(new CANNON.Vec3( positionArray[i], positionArray[i + 1], positionArray[i + 2] )); } } // 第二步:基于唯一顶点生成面索引 for (let i = 0; i < positionArray.length / 3; i += 3) { const faceIndices: number[] = []; for (let j = 0; j < 3; j++) { const idx = i + j; const x = positionArray[idx * 3].toFixed(6); const y = positionArray[idx * 3 + 1].toFixed(6); const z = positionArray[idx * 3 + 2].toFixed(6); const key = `${x},${y},${z}`; faceIndices.push(vertexMap.get(key)!); } faces.push(faceIndices); } // 第三步:创建凸多面体并补全内部计算 const convexGeometry = new CANNON.ConvexPolyhedron({ vertices: uniquePoints, faces: faces }); convexGeometry.computeNormals(); // 手动计算法线确保碰撞逻辑正常 convexGeometry.computeEdges(); const body = new CANNON.Body({ mass: 1 }); body.addShape(convexGeometry); return body; }
额外调试建议
- 生成
ConvexPolyhedron后,打印convexGeometry.vertices和convexGeometry.faces,检查是否存在索引越界或重复顶点问题; - 确保二十面骰子STL是凸多面体(本身符合),凹面体不能用
ConvexPolyhedron; - 可尝试调整Cannon世界的碰撞参数,比如将
broadphase设置为new CANNON.SAPBroadphase(world),提升碰撞检测稳定性。
内容的提问来源于stack exchange,提问作者munHunger
相关产品推荐
相关产品推荐

