使用yuka控制物体运动
Yuka
游戏中存在很多可以交互的物体,
沿固定路径前进的NPC、主动追踪角色的怪物、巡逻搜查区域的敌人,
这些有物体一般统称Entity,
具有不同程度的AI能力,
yuka.js中提供了很多实现游戏ai的方法。
要实现实体沿固定路线移动,
需要做如下准备:
- 创建一个实体Mesh(肉体)
- 创建Mesh对应的Vehicle(灵魂)
- 绑定Mesh和Vehicle
- 创建路径path
- 绑定路径和Vehicle
- 创建实体管理器,管理实体
- 随时间更新实体
首先,创建一个圆锥形的实体,
注意需要关闭实体矩阵的自动更新选项。
1 | const vehicleGeo = new THREE.ConeGeometry(0.1, 0.8, 8) |
创建Vehicle,将更新后的entity与mesh进行同步
1 | const vehicle = new YUKA.Vehicle() |
创建循环路径
1 | const path = new YUKA.Path() |
添加实体管理器
1 | const entityManager = new YUKA.EntityManager() |
YUKA控制GLTF模型
gltf模型加载后,由于将matrixAutoUpdate关闭,无法对modelMatrix进行修改,
因此不能通过model.scale.set()的方式进行缩放,
需要通过YUKA.Vehicle提供的scale属性进行缩放
1 | gltfLoader.load('/assets/Debris_BrokenCar.gltf', gltf => { |
物体寻路 SeekBehavior
使用yuka做出A物体寻找B物体的效果,
需要使用到SeekBehavior类
需要指定目标物体的坐标,
将SeekBehavior添加到动作主体中:
创建seek主体
1 | const vehicle = new YUKA.Vehicle() |
创建target实体
1 | const target = new YUKA.GameEntity() |
添加追踪行为
1 | const seekBehavior = new YUKA.SeekBehavior(target.position) |
添加实体管理器并逐帧更新
1 | const entityManager = new YUKA.EntityManager() |
物体抵达 ArriveBehavior
ArriveBehavior和SeekBehavior类似,
Arrive在到达目的地后停止,
Seek依旧保持搜寻,
创建Arrive行为需要3个参数:
- target 目的地
- deceleration 减速率
- tolerance 容差值
1 | const arriveBehavior = new YUKA.ArriveBehavior(target.position,1,3) |
yuka控制物体移动到鼠标处
控制角色移动到鼠标点击处的游戏有很多,
使用yuka的ArriveBehavior,
结合raycaster获取鼠标坐标,就能实现这个功能。
基本思路就是:
- 给追踪角色绑定Vehicle实体
- 给被追踪物体绑定GameEntity实体
- 绑定追踪关系,激活实体管理器
- 创建辅助平面,监听鼠标点位
- 点击屏幕,切换被追踪物坐标
这里给追踪的坦克模型添加了一个颠簸动画,
直接添加到vehicle或者target上会导致追踪物体上下翻滚的问题,
这里需要给vehicle外套一层group,
使用group控制上下颠簸效果,
追踪物位置依然由vehicle控制
1 | const gltfLoader = new GLTFLoader() |
物体逃避 Flee Behavior
FleeBehavior的用法和其他行为相似,
需要2个参数:
- target
- panicDistance 规避范围
1 | const fleeBehavior = new YUKA.FleeBehavior(target.position,5) |
障碍物规避 Orbstacle Avoidance
执行障碍物规避,需要知道一些关键条件:
- 规避主体的碰撞体积
- 被规避物体列表,以及它们对应的碰撞体积
计算碰撞体积需要配合Three.js中Geometry类型的computeBoundingSphere一起使用。
1 | const geo = new BoxGeometry(1,1,1) |
按照上面的计算方式,计算出规避物和障碍物的碰撞体积
1 | // 碰撞物 |
最终绑定ObstacleAvoidanceBehavior
1 | const obstacleAvoidanceBehavior = new YUKA.ObstacleAvoidanceBehavior([ |
引入的gltf模型无法使用getBoundingSphere方式直接计算包围盒,
GPT说可以遍历gltf的每一个node计算包围盒(听上去像个馊主意)
试错试出一个boundingRadius之后,汽车绕开障碍物的轨迹非常僵硬,
需要增加运行轨迹的smooth程度:
1 | const smoother = new YUKA.Smoother(30) |