4. 角色漫游(八叉树碰撞检测)
# 角色漫游例子(八叉树碰撞检测)
前面章节,给大家介绍过第一、三人称漫游,不过是在一个平面上,通过WASD键控制角色前后左右运动。
这节课给大家讲解如何控制角色,在一个高度变化地形上运动,比如上坡,上楼梯之类的。这就要用到前面几节谈到的胶囊碰撞体Capsule
与八叉树Octree
。
# 八叉树Octree
下面接着前一个章节代码给大家演示。
// model.js文件
const gltfGround = await loader.loadAsync("../../地形.glb");
const ground = gltfGround.scene;//地形
model.add(ground)
用八叉树表示地形,用于碰撞检测。
// player.js文件
import { Octree } from 'three/examples/jsm/math/Octree.js';
const worldOctree = new Octree();
worldOctree.fromGraphNode(地形场景模型);
# 胶囊碰撞体Capsule
用胶囊碰撞体Capsule
表示人物角色的网格模型,注意这里就是近似表示。
你可以根据角色模型的尺寸,设置胶囊碰撞体的尺寸,比如人1.7m身高,胶囊高度就可以用1.7的高度表示,有一点误差也不影响用,胶棒本身就是对角色模型的简化表示。(角色gltf模型的尺寸你可以通过blender去测量)
import { Capsule } from 'three/examples/jsm/math/Capsule.js';
// 胶囊碰撞体近似表示角色网格模型capsule
const R = 0.4;//角色近似半径
const H = 1.7;//角色近似高度
const start = new THREE.Vector3(0, R, 0);//底部半球球心坐标
const end = new THREE.Vector3(0, H - R, 0);//顶部半球球心坐标
const capsule = new Capsule(start, end, R);
注意:胶囊碰撞体要与人体刚好重合
# 碰撞检测
碰撞检测思路很简单,比如人走到斜坡一瞬间,人会与斜坡模型有一定的交叉重合,这时候用八叉树和胶囊碰撞体计算重合情况,把交叉部分平移回来即可。
在运动过程中,一直进行碰撞检测,一旦发生重合,就把碰撞检测部分偏移回来。
// playerUpdate:player.js文件角色位置更新的函数
function playerUpdate(deltaTime) {
...
...
v.addScaledVector(v, damping);
const deltaPos = v.clone().multiplyScalar(deltaTime);
//注释掉,用下面代码代替
// player.position.add(deltaPos);//角色模型位置更新
// capsule表示player,wasd控制capsule移动
capsule.translate(deltaPos);
// 每次更新发生移动,随时碰撞检测
const result = worldOctree.capsuleIntersect(capsule);
if (result) {
// 把交叉重合偏移回来
capsule.translate(result.normal.multiplyScalar(result.depth));
}
// 胶囊碰撞体capsule的位置同步到角色模型player
player.position.copy(capsule.start);
// 偏移量根据人坐标原点相对胶囊底部球心设置
player.position.y -= R;
}
人的坐标原点在脚丫子部位,执行player.position.copy(capsule.start)
,相当于把人的脚丫子放在了胶囊底部球心位置。
测试上面代码,你可以发现人可以在斜坡走,可以上上楼梯,如果遇到墙体也会被挡住。
下节课会给大家讲解下楼梯或斜坡的情况。