玩法 / Navigation
Navigation Class
寻路
寻路就像是给游戏角色一张地图和一套指导,让它们知道如何从一个地方走到另一个地方,避开障碍物。
想象一下,你在一个迷宫中,需要找到出口。寻路系统就是你的助手,告诉你应该往哪个方向走,以便最快地找到出口。
在游戏中,角色需要在游戏世界中移动,而寻路功能帮助角色计算出最佳路径。它会考虑地图上的可行走区域和障碍物,然后找到一条避开障碍物、最短的路径来到目的地。
寻路就像是一个聪明的导航系统。它会检查周围的地形,看看哪里是可以走的,哪里是被阻挡的。然后,它会计算出一条路径,告诉角色应该朝着哪个方向移动,以及在每个点上应该转向多少度。
这样,角色就可以根据寻路给出的指引,沿着可行走的路径前进,避开障碍物,并最终到达目的地。
如何使用寻路功能呢 ?
左侧栏中寻路区域逻辑对象拖入场景中,可绘制出一个寻路区域,在此区域可实现寻路功能。
运行时寻路数据并不是动态生成的,而是随项目发布的本地寻路数据,寻路数据在场景初始化完成后加载。
当在设置项“寻路设置”中的动态寻路开启时,开启半动态寻路,仅可通过控制寻路修饰区,来达到运行时自定义的寻路效果。
Table of contents
Methods
findPath(startPos: Vector, endPos: Vector): Vector[] |
|---|
| 查找起点与终点之间的最短移动路径,并以数组的方式返回主要路径点 |
follow(relatedObject: GameObject, target: GameObject, radius?: number, OnSuccess?: () => void, OnFail?: () => void): boolean |
| 跟随目标 |
getClosestReachablePoint(targetPoint: Vector, queryExtent: Vector): Vector |
| 自动寻找与目标点距离最近的可寻路位置 |
getRandomReachablePointInRadius(targetPoint: Vector, radius: number): Vector |
| 在指定位置限制半径内的可导航区域中生成一个随机可到达的位置 |
navigateTo(relatedObject: GameObject, position: Vector, radius?: number, OnSuccess?: () => void, OnFail?: () => void): void |
| 寻路移动 |
navigationRaycast(rayStart: Vector, rayEnd: Vector): boolean |
| 判断两点连线上是否存在障碍或超出寻路区域范围 |
stopFollow(relatedObject: GameObject): void |
| 停止跟随 |
stopNavigateTo(relatedObject: GameObject): void |
| 导航停止 |
Methods
findPath
• Static findPath(startPos, endPos): Vector[]
查找起点与终点之间的最短移动路径,并以数组的方式返回主要路径点
Parameters
startPos Vector | 起点 |
|---|---|
endPos Vector | 终点 |
Returns
Vector[] | 主要路径点 |
|---|
ts
@Component
export default class Example_Navigation_FindPath extends Script {
protected onStart(): void {
// 下列逻辑仅在客户端执行
if(SystemUtil.isClient()) {
//获取目标对象
let signs = this.gameObject;
//获取当前玩家角色
let myChara = Player.localPlayer.character;
//添加一个按键方法:按下按键“1”,生成角色寻路轨迹
InputUtil.onKeyDown(Keys.One, () => {
let points = Navigation.findPath(myChara.worldTransform.position, signs.worldTransform.position);
points.forEach((v,i) => {
console.error("loc " + v);
GameObject.asyncSpawn("84121").then((obj: Model) => {
obj.worldTransform.position = v;
obj.worldTransform.scale = new Vector(0.2, 0.2, 0.2);
obj.setCollision(CollisionStatus.Off);
});
});
});
}
}
} @Component
export default class Example_Navigation_FindPath extends Script {
protected onStart(): void {
// 下列逻辑仅在客户端执行
if(SystemUtil.isClient()) {
//获取目标对象
let signs = this.gameObject;
//获取当前玩家角色
let myChara = Player.localPlayer.character;
//添加一个按键方法:按下按键“1”,生成角色寻路轨迹
InputUtil.onKeyDown(Keys.One, () => {
let points = Navigation.findPath(myChara.worldTransform.position, signs.worldTransform.position);
points.forEach((v,i) => {
console.error("loc " + v);
GameObject.asyncSpawn("84121").then((obj: Model) => {
obj.worldTransform.position = v;
obj.worldTransform.scale = new Vector(0.2, 0.2, 0.2);
obj.setCollision(CollisionStatus.Off);
});
});
});
}
}
}follow
• Static follow(relatedObject, target, radius?, OnSuccess?, OnFail?): boolean
跟随目标
Parameters
relatedObject GameObject | 寻路作用对象 |
|---|---|
target GameObject | 被跟随目标 |
radius? number | 距目标半径 default:0 range: 不做限制 type: 浮点型 |
OnSuccess? () => void | 成功回调(当跟随到设定的目标范围内时触发——可多次) default:null |
OnFail? () => void | 失败回调(当跟随的目标消失或离开寻路区域范围触发——可多次) default:null |
Returns
boolean | 跟随请求是否成功 |
|---|
角色和客户端NPC在客户端调用时生效,双端在服务端调用时生效
使用示例:在场景中拖入一个寻路区域,坐标为(0, 0, 0),缩放为(50, 10, 3).同时拖入三个缩放为(1,7,1)的立方体,并分别放置在坐标(400,-150,0),(1000, 150, 0)和(1700, -450,0)。在坐标(2400,-400,0)处生成一个npc.创建一个脚本挂载在目标对象下.在脚本中复制下列"Example_Navigation_Follow"的代码保存,运行游戏,按下按键”1“,npc寻路跟随玩家,按下按键“2”,npc停止跟随。代码如下:ts
@Component
export default class Example_Navigation_Follow extends Script {
protected async onStart(): `Promise`<`void`\> {
// 下列逻辑仅在服务端执行
if(SystemUtil.isServer()) {
// 开启每帧周期函数
this.useUpdate = true;
// 生成NPC
let npc = await Player.spawnDefaultCharacter().asyncReady();
npc.worldTransform.position = new Vector(2400, -400, 130)
// 添加一个客户端事件”FOLLOW“的监听器,让npc寻路跟随客户端玩家角色(NPC寻路需要在服务器调用,玩家角色无法使用Follow)
Event.addClientListener("FOLLOW", (player) => {
Navigation.follow(npc, player.character, 50, () => { EffectService.playOnGameObject("151570", npc, {slotType: HumanoidSlotType.Rings})});
});
// 添加一个客户端事件”STOPFOLLOW“的监听器,让npc停止跟随
Event.addClientListener("STOPFOLLOW", (player) => {
Navigation.stopFollow(npc);
});
}
// 下列逻辑仅在客户端执行
if(SystemUtil.isClient()) {
//添加一个按键方法:按下按键“1”,向服务端发送”FOLLOW“事件
InputUtil.onKeyDown(Keys.One, () => {
Event.dispatchToServer("FOLLOW");
});
//添加一个按键方法:按下按键“2”,向服务端发送”STOPFOLLOW“事件
InputUtil.onKeyDown(Keys.Two, () => {
Event.dispatchToServer("STOPFOLLOW");
});
}
}
} @Component
export default class Example_Navigation_Follow extends Script {
protected async onStart(): `Promise`<`void`\> {
// 下列逻辑仅在服务端执行
if(SystemUtil.isServer()) {
// 开启每帧周期函数
this.useUpdate = true;
// 生成NPC
let npc = await Player.spawnDefaultCharacter().asyncReady();
npc.worldTransform.position = new Vector(2400, -400, 130)
// 添加一个客户端事件”FOLLOW“的监听器,让npc寻路跟随客户端玩家角色(NPC寻路需要在服务器调用,玩家角色无法使用Follow)
Event.addClientListener("FOLLOW", (player) => {
Navigation.follow(npc, player.character, 50, () => { EffectService.playOnGameObject("151570", npc, {slotType: HumanoidSlotType.Rings})});
});
// 添加一个客户端事件”STOPFOLLOW“的监听器,让npc停止跟随
Event.addClientListener("STOPFOLLOW", (player) => {
Navigation.stopFollow(npc);
});
}
// 下列逻辑仅在客户端执行
if(SystemUtil.isClient()) {
//添加一个按键方法:按下按键“1”,向服务端发送”FOLLOW“事件
InputUtil.onKeyDown(Keys.One, () => {
Event.dispatchToServer("FOLLOW");
});
//添加一个按键方法:按下按键“2”,向服务端发送”STOPFOLLOW“事件
InputUtil.onKeyDown(Keys.Two, () => {
Event.dispatchToServer("STOPFOLLOW");
});
}
}
}getClosestReachablePoint
• Static getClosestReachablePoint(targetPoint, queryExtent): Vector
自动寻找与目标点距离最近的可寻路位置
Parameters
targetPoint Vector | 目的地位置 |
|---|---|
queryExtent Vector | 目的地搜索范围 |
Returns
Vector | 范围内最近可寻路位置(null则未找到) |
|---|
ts
@Component
export default class NewScript extends Script {
protected async onStart(): `Promise`<`void`\> {
let player = Player.localPlayer;
let NAVabox = await GameObject.asyncFindGameObjectById("12E74B24") as Model;
let NAVasphere = await GameObject.asyncFindGameObjectById("05DD737A") as Model;
//自动寻找与目标点距离最近的可寻路位置
InputUtil.onKeyDown(Keys.One,()=>{
let TargetPoint = Navigation.getClosestReachablePoint(NAVabox.worldTransform.position,new Vector(500,500,10));
if(TargetPoint == null){
console.log(`寻路位置是个空的`);
}else{
Navigation.navigateTo(player.character,TargetPoint);
}
});
}
}@Component
export default class NewScript extends Script {
protected async onStart(): `Promise`<`void`\> {
let player = Player.localPlayer;
let NAVabox = await GameObject.asyncFindGameObjectById("12E74B24") as Model;
let NAVasphere = await GameObject.asyncFindGameObjectById("05DD737A") as Model;
//自动寻找与目标点距离最近的可寻路位置
InputUtil.onKeyDown(Keys.One,()=>{
let TargetPoint = Navigation.getClosestReachablePoint(NAVabox.worldTransform.position,new Vector(500,500,10));
if(TargetPoint == null){
console.log(`寻路位置是个空的`);
}else{
Navigation.navigateTo(player.character,TargetPoint);
}
});
}
}getRandomReachablePointInRadius
• Static getRandomReachablePointInRadius(targetPoint, radius): Vector
在指定位置限制半径内的可导航区域中生成一个随机可到达的位置
Parameters
targetPoint Vector | 目的地位置 |
|---|---|
radius number | 半径范围 range: 不限制 type: 浮点数 |
Returns
Vector | 范围内随机可到达位置(null则未找到) |
|---|
ts
@Component
export default class NewScript extends Script {
protected async onStart(): `Promise`<`void`\> {
let player = Player.localPlayer;
let NAVabox = await GameObject.asyncFindGameObjectById("12E74B24") as Model;
let NAVasphere = await GameObject.asyncFindGameObjectById("05DD737A") as Model;
//在指定位置限制半径内的可导航区域中生成一个随机可到达的位置
InputUtil.onKeyDown(Keys.Two,()=>{
let TargetPoint = Navigation.getRandomReachablePointInRadius(new mw.Vector(0,0,0),8000);
if(TargetPoint == null){
console.log(`寻路位置是个空的`);
}else{
Navigation.navigateTo(player.character,TargetPoint);
}
});
}
}@Component
export default class NewScript extends Script {
protected async onStart(): `Promise`<`void`\> {
let player = Player.localPlayer;
let NAVabox = await GameObject.asyncFindGameObjectById("12E74B24") as Model;
let NAVasphere = await GameObject.asyncFindGameObjectById("05DD737A") as Model;
//在指定位置限制半径内的可导航区域中生成一个随机可到达的位置
InputUtil.onKeyDown(Keys.Two,()=>{
let TargetPoint = Navigation.getRandomReachablePointInRadius(new mw.Vector(0,0,0),8000);
if(TargetPoint == null){
console.log(`寻路位置是个空的`);
}else{
Navigation.navigateTo(player.character,TargetPoint);
}
});
}
}navigateTo
• Static navigateTo(relatedObject, position, radius?, OnSuccess?, OnFail?): void
寻路移动
Parameters
relatedObject GameObject | 寻路作用对象 |
|---|---|
position Vector | 目标位置 |
radius? number | 距目标半径 default:0 range: 不做限制 type: 浮点型 |
OnSuccess? () => void | 成功回调 default:null |
OnFail? () => void | 失败回调 default:null |
ts
@Component
export default class Example_Navigation_NavigateTo extends Script {
protected async onStart(): `Promise`<`void`\> {
// 下列逻辑仅在客户端执行
if(SystemUtil.isClient()) {
//获取目标对象
let signs = this.gameObject;
//获取当前玩家角色
let myChara = Player.localPlayer.character;
//添加一个按键方法:按下按键“1”,角色寻路至目标位置,并播放一个特效
InputUtil.onKeyDown(Keys.One, () => {
Navigation.navigateTo(myChara, signs.worldTransform.position, 50, () => { EffectService.playOnGameObject("151570", myChara, {slotType: HumanoidSlotType.Rings})});
});
//添加一个按键方法:按下按键“2”,角色停止寻路
InputUtil.onKeyDown(Keys.Two, () => {
Navigation.stopNavigateTo(myChara);
});
}
}
}@Component
export default class Example_Navigation_NavigateTo extends Script {
protected async onStart(): `Promise`<`void`\> {
// 下列逻辑仅在客户端执行
if(SystemUtil.isClient()) {
//获取目标对象
let signs = this.gameObject;
//获取当前玩家角色
let myChara = Player.localPlayer.character;
//添加一个按键方法:按下按键“1”,角色寻路至目标位置,并播放一个特效
InputUtil.onKeyDown(Keys.One, () => {
Navigation.navigateTo(myChara, signs.worldTransform.position, 50, () => { EffectService.playOnGameObject("151570", myChara, {slotType: HumanoidSlotType.Rings})});
});
//添加一个按键方法:按下按键“2”,角色停止寻路
InputUtil.onKeyDown(Keys.Two, () => {
Navigation.stopNavigateTo(myChara);
});
}
}
}navigationRaycast
• Static navigationRaycast(rayStart, rayEnd): boolean
判断两点连线上是否存在障碍或超出寻路区域范围
Parameters
rayStart Vector | 起始点 |
|---|---|
rayEnd Vector | 结束点 |
Returns
boolean | 是否两点连线存在障碍或超出范围 |
|---|
ts
@Component
export default class NewScript extends Script {
protected async onStart(): `Promise`<`void`\> {
let player = Player.localPlayer;
let NAVabox = await GameObject.asyncFindGameObjectById("12E74B24") as Model;
let NAVasphere = await GameObject.asyncFindGameObjectById("05DD737A") as Model;
//判断两点连线上是否存在障碍或超出寻路区域范围
InputUtil.onKeyDown(Keys.Three,()=>{
let NavigationCAV = Navigation.navigationRaycast(new Vector(0,0,0),NAVasphere.worldTransform.position)
console.log(`直线路径上是否有障碍:`,NavigationCAV);
})
}
}@Component
export default class NewScript extends Script {
protected async onStart(): `Promise`<`void`\> {
let player = Player.localPlayer;
let NAVabox = await GameObject.asyncFindGameObjectById("12E74B24") as Model;
let NAVasphere = await GameObject.asyncFindGameObjectById("05DD737A") as Model;
//判断两点连线上是否存在障碍或超出寻路区域范围
InputUtil.onKeyDown(Keys.Three,()=>{
let NavigationCAV = Navigation.navigationRaycast(new Vector(0,0,0),NAVasphere.worldTransform.position)
console.log(`直线路径上是否有障碍:`,NavigationCAV);
})
}
}stopFollow
• Static stopFollow(relatedObject): void
停止跟随
Parameters
relatedObject GameObject | 寻路作用对象 |
|---|
角色和客户端NPC在客户端调用时生效,双端在服务端调用时生效
使用示例:在场景中拖入一个寻路区域,坐标为(0, 0, 0),缩放为(50, 10, 3).同时拖入三个缩放为(1,7,1)的立方体,并分别放置在坐标(400,-150,0),(1000, 150, 0)和(1700, -450,0)。在坐标(2400,-400,0)处生成一个npc.创建一个脚本挂载在目标对象下.在脚本中复制下列"Example_Navigation_Follow"的代码保存,运行游戏,按下按键”1“,npc寻路跟随玩家,按下按键“2”,npc停止跟随。代码如下:ts
@Component
export default class Example_Navigation_Follow extends Script {
protected async onStart(): `Promise`<`void`\> {
// 下列逻辑仅在服务端执行
if(SystemUtil.isServer()) {
// 开启每帧周期函数
this.useUpdate = true;
// 生成NPC
let npc = await Player.spawnDefaultCharacter().asyncReady();
npc.worldTransform.position = new Vector(2400, -400, 130)
// 添加一个客户端事件”FOLLOW“的监听器,让npc寻路跟随客户端玩家角色(NPC寻路需要在服务器调用,玩家角色无法使用Follow)
Event.addClientListener("FOLLOW", (player) => {
Navigation.follow(npc, player.character, 50, () => { EffectService.playOnGameObject("151570", npc, {slotType: HumanoidSlotType.Rings})});
});
// 添加一个客户端事件”STOPFOLLOW“的监听器,让npc停止跟随
Event.addClientListener("STOPFOLLOW", (player) => {
Navigation.stopFollow(npc);
});
}
// 下列逻辑仅在客户端执行
if(SystemUtil.isClient()) {
//添加一个按键方法:按下按键“1”,向服务端发送”FOLLOW“事件
InputUtil.onKeyDown(Keys.One, () => {
Event.dispatchToServer("FOLLOW");
});
//添加一个按键方法:按下按键“2”,向服务端发送”STOPFOLLOW“事件
InputUtil.onKeyDown(Keys.Two, () => {
Event.dispatchToServer("STOPFOLLOW");
});
}
}
} @Component
export default class Example_Navigation_Follow extends Script {
protected async onStart(): `Promise`<`void`\> {
// 下列逻辑仅在服务端执行
if(SystemUtil.isServer()) {
// 开启每帧周期函数
this.useUpdate = true;
// 生成NPC
let npc = await Player.spawnDefaultCharacter().asyncReady();
npc.worldTransform.position = new Vector(2400, -400, 130)
// 添加一个客户端事件”FOLLOW“的监听器,让npc寻路跟随客户端玩家角色(NPC寻路需要在服务器调用,玩家角色无法使用Follow)
Event.addClientListener("FOLLOW", (player) => {
Navigation.follow(npc, player.character, 50, () => { EffectService.playOnGameObject("151570", npc, {slotType: HumanoidSlotType.Rings})});
});
// 添加一个客户端事件”STOPFOLLOW“的监听器,让npc停止跟随
Event.addClientListener("STOPFOLLOW", (player) => {
Navigation.stopFollow(npc);
});
}
// 下列逻辑仅在客户端执行
if(SystemUtil.isClient()) {
//添加一个按键方法:按下按键“1”,向服务端发送”FOLLOW“事件
InputUtil.onKeyDown(Keys.One, () => {
Event.dispatchToServer("FOLLOW");
});
//添加一个按键方法:按下按键“2”,向服务端发送”STOPFOLLOW“事件
InputUtil.onKeyDown(Keys.Two, () => {
Event.dispatchToServer("STOPFOLLOW");
});
}
}
}stopNavigateTo
• Static stopNavigateTo(relatedObject): void
导航停止
Parameters
relatedObject GameObject | 寻路作用对象 |
|---|
ts
@Component
export default class Example_Navigation_NavigateTo extends Script {
protected async onStart(): `Promise`<`void`\> {
// 下列逻辑仅在客户端执行
if(SystemUtil.isClient()) {
//获取目标对象
let signs = this.gameObject;
//获取当前玩家角色
let myChara = Player.localPlayer.character;
//添加一个按键方法:按下按键“1”,角色寻路至目标位置,并播放一个特效
InputUtil.onKeyDown(Keys.One, () => {
Navigation.navigateTo(myChara, signs.worldTransform.position, 50, () => { EffectService.playOnGameObject("151570", myChara, {slotType: HumanoidSlotType.Rings})});
});
//添加一个按键方法:按下按键“2”,角色停止寻路
InputUtil.onKeyDown(Keys.Two, () => {
Navigation.stopNavigateTo(myChara);
});
}
}
}@Component
export default class Example_Navigation_NavigateTo extends Script {
protected async onStart(): `Promise`<`void`\> {
// 下列逻辑仅在客户端执行
if(SystemUtil.isClient()) {
//获取目标对象
let signs = this.gameObject;
//获取当前玩家角色
let myChara = Player.localPlayer.character;
//添加一个按键方法:按下按键“1”,角色寻路至目标位置,并播放一个特效
InputUtil.onKeyDown(Keys.One, () => {
Navigation.navigateTo(myChara, signs.worldTransform.position, 50, () => { EffectService.playOnGameObject("151570", myChara, {slotType: HumanoidSlotType.Rings})});
});
//添加一个按键方法:按下按键“2”,角色停止寻路
InputUtil.onKeyDown(Keys.Two, () => {
Navigation.stopNavigateTo(myChara);
});
}
}
}