导航
导航沿着一条避开被阻挡格子的路线,把 agent 移动到某个世界坐标,底层用 A* 在一个 NavGrid
上寻路。
NavGrid 是一个由可走/被阻挡格子构成的矩形网格。把它装到 Nav 资源上,agent 才能对它寻路:
import { defineSystem, Res, Nav, NavGrid } from 'esengine';
export const setupNav = defineSystem([Res(Nav)], (nav) => { nav.setGrid(new NavGrid({ width: 60, // 列数 height: 44, // 行数 cellSize: 20, // 每格的世界像素 origin: { x: -600, y: -440 }, // 格子 (0,0) 中心的世界坐标 }));}, { name: 'SetupNav' });NavGrid 选项 |
类型 | 说明 |
|---|---|---|
width |
number | 格子列数。 |
height |
number | 格子行数。 |
cellSize |
number | 每格的世界像素(正方形)。 |
origin |
Vec2 |
格子 (0,0) 中心的世界坐标。默认 (0,0)。 |
walkable |
Uint8Array |
可选的行主序 width*height 掩码,1 = 可走,0 = 阻挡。省略则全部可走。 |
若想从已绘制的瓦片地图推导网格,而不是手写掩码,用 navGridFromTilemapLayer(把某图层的
实心瓦片视作阻挡)或 navGridFromTiles。
移动 agent
Section titled “移动 agent”加一个 NavAgent,然后给它指一个目的地。内置的导航插件会规划路径并每帧沿路径推进 agent——
你只管设目标。
import { NavAgent, setNavDestination, stopNavAgent } from 'esengine';
cmds.spawn() .insert(Transform, { position: { x: 0, y: 0, z: 0 } }) .insert(NavAgent, { speed: 140, arriveRadius: 8 });
// 从任何能访问 world 的系统/动作里——每帧调用都安全:setNavDestination(world, entity, { x: 320, y: -120 });// ……以及原地停下:stopNavAgent(world, entity);NavAgent 字段 |
默认 | 说明 |
|---|---|---|
speed |
120 |
移动速度(世界像素/秒)。 |
radius |
0 |
身体有多宽(像素)。寻路会绕开它挤不过去的地方;0 按质点寻路。 |
arriveRadius |
6 |
距最终目标的停止距离。 |
repathInterval |
0.5 |
移动中两次重规划的间隔秒数;0 = 只在目标改变时重规划。 |
hasTarget |
false |
是否已设目的地(自动管理)。 |
targetX、targetY |
0 |
当前目的地(世界像素)。 |
arrived |
false |
agent 到达目标的那一帧被置真。 |
setNavDestination 每帧调用都安全,可用于追一个移动的目标——只有当目标真的移动、或
repathInterval 到点时,agent 才重新规划。读 agent.arrived(或 Perception)来判断何时
切换行为。
agent 是有宽度的
Section titled “agent 是有宽度的”把 radius 设成身体的实际半宽,寻路就会绕开它挤不过去的缝隙。留在 0 表示按质点寻路——
没有碰撞体的东西就该这样;而这也正是那个经典画面的成因:敌人信心十足地走进门洞然后
永远停在那里,因为它被规划到的那一格是可走的,而挂在隔壁那一格上的半个身子不是。
身体站不下的目标(墙角里的拾取物)依然到得了:路径规划到身体站得下的最近处为止,
剩下的交给 arriveRadius。