Gameplay / Navigation
Navigation Class
Navigation
Pathfinding is like giving game characters a map and a set of instructions on how to walk from one place to another and avoid obstacles.
Imagine you are in a maze and need to find an exit. The navigation system is your assistant, telling you which direction you should go in order to find the exit as quickly as possible.
In the game, characters need to move around the game world, and the pathfinding function helps them calculate the best path. It will consider the walkable areas and obstacles on the map, and then find the shortest path to reach the destination that avoids obstacles.
Navigation is like a smart navigation system. It will check the surrounding terrain to see where it is passable and where it is obstructed. Then it calculates a path that tells the character which direction to move in and how many degrees to turn at each point.
In this way, the character can follow the guidance given by pathfinding, proceed along a walkable path, avoid obstacles, and ultimately reach their destination.
How to use the pathfinding function?
The logical object of the Navigation Volume in the left column can be dragged into the scene to draw a Navigation Volume, where achieve navigation function can be realized.
The runtime pathfinding data is not dynamically generated, but is local pathfinding data published with the project, which is loaded after the scene initialization is completed.
When dynamic pathfinding is enabled in the "pathfinding settings" option, semi dynamic pathfinding can only be achieved by controlling the pathfinding modifier area to achieve customized pathfinding effects during runtime.
Table of contents
Methods
findPath(startPos : Vector , endPos : Vector ): Vector [] other |
---|
Find the shortest moving path between the start point and the end point, and return the main path points in the form of an array |
follow(relatedObject : GameObject , target : GameObject , radius? : number , OnSuccess? : () => void , OnFail? : () => void ): boolean other |
Follow the target |
getClosestReachablePoint(targetPoint : Vector , queryExtent : Vector ): Vector other |
Automatically find the nearest navigation position to the target point |
getRandomReachablePointInRadius(targetPoint : Vector , radius : number ): Vector other |
Generate a randomly reachable position in the navigable area within the specified position limit radius |
navigateTo(relatedObject : GameObject , position : Vector , radius? : number , OnSuccess? : () => void , OnFail? : () => void ): void other |
Pathfinding movement |
navigationRaycast(rayStart : Vector , rayEnd : Vector ): boolean other |
Judge whether there is any obstacle or beyond the range of the Navigation Volume on the connection between two points |
stopFollow(relatedObject : GameObject ): void other |
Stop following |
stopNavigateTo(relatedObject : GameObject ): void other |
Navigation stopped |
Methods
findPath
• Static
findPath(startPos
, endPos
): Vector
[] other
Find the shortest moving path between the start point and the end point, and return the main path points in the form of an array
Parameters
startPos Vector | Usage: starting point |
---|---|
endPos Vector | Usage: End Point |
Returns
Vector [] | Main waypoints |
---|
Usage example: Drag a Navigation Volume into the scene with coordinates of (0, 0, 0) and scale of (50, 10, 3). At the same time, drag three cube with scale of (1, 7, 1) and place them respectively at coordinates of (400, -150, 0), (1000, 150, 0), and (1700, -450, 0). Finally, drag a target object to close the collision and place it at coordinates (2400, -400, 0). Create a script and mount it under the target object. Copy and save the following code for 'Exemplar-Naviation_SindPath' in the script, run the game, press button '1' to move the character's pathfinding to the target position, press button '2' to stop the character's pathfinding. The code is as follows:
@Component
export default class Example_Navigation_FindPath extends Script {
protected onStart(): void {
// The following logic is only executed on the client side
if(SystemUtil.isClient()) {
//Obtain the target object
let signs = this.gameObject;
//Get the current Player's character
let myChara = Player.localPlayer.character;
//Add a button method: Press button "1" to generate the character's pathfinding trajectory
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 {
// The following logic is only executed on the client side
if(SystemUtil.isClient()) {
//Obtain the target object
let signs = this.gameObject;
//Get the current Player's character
let myChara = Player.localPlayer.character;
//Add a button method: Press button "1" to generate the character's pathfinding trajectory
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
other
Follow the target
Parameters
relatedObject GameObject | Usage: navigation target |
---|---|
target GameObject | Usage: being followed by the target |
radius? number | Usage: radius from target default: 0 range: unlimited type: floating point |
OnSuccess? () => void | Usage: Successful callback (triggered when following the set target range - can be repeated) default: null |
OnFail? () => void | Usage: Failed callback (triggered when the following target disappears or leaves the pathfinding area range - can be multiple times) default: null |
Returns
boolean | Whether the following request was successful |
---|
The roles and client NPCs take effect when called by the client, and the dual end takes effect when called by the server
Usage example: Drag a Navigation Volume into the scene with coordinates of (0, 0, 0) and scale of (50, 10, 3). At the same time, drag three cube with scale of (1, 7, 1) and place them respectively at coordinates of (400, -150, 0), (1000, 150, 0), and (1700, -450, 0). Generate an npc at coordinates (2400, -400, 0). create a script to mount it under the target object. copy the following code of "Example_Navigation_Follow" in the script, save it, run the game, press key "1", npc navigation follows Player, press key "2", npc stops following. The code is as follows:
@Component
export default class Example_Navigation_Follow extends Script {
protected async onStart(): Promise<void> {
// The following logic is only executed on the server side
if(SystemUtil.isServer()) {
// Enable the periodic function for each frame
this.useUpdate = true;
// Generate NPC
let npc = await Player.spawnDefaultCharacter().asyncReady();
npc.worldTransform.position = new Vector(2400, -400, 130)
// Add a listener for the client event "FOLLOW" to allow NPC pathfinding to follow the client player character (NPC pathfinding needs to be called on the server, and player characters cannot use Follow)
Event.addClientListener("FOLLOW", (player) => {
Navigation.follow(npc, player.character, 50, () => { EffectService.playOnGameObject("151570", npc, {slotType: HumanoidSlotType.Rings})});
});
// Add a listener for the client event 'STOPFOLLOW' to stop NPC from following
Event.addClientListener("STOPFOLLOW", (player) => {
Navigation.stopFollow(npc);
});
}
// The following logic is only executed on the client side
if(SystemUtil.isClient()) {
//Add a key method: press key "1" to send a "FOLLOW" event to the server
InputUtil.onKeyDown(Keys.One, () => {
Event.dispatchToServer("FOLLOW");
});
//Add a key method: press key "2" to send "STOPFOLLOW" event to the server
InputUtil.onKeyDown(Keys.Two, () => {
Event.dispatchToServer("STOPFOLLOW");
});
}
}
}
@Component
export default class Example_Navigation_Follow extends Script {
protected async onStart(): Promise<void> {
// The following logic is only executed on the server side
if(SystemUtil.isServer()) {
// Enable the periodic function for each frame
this.useUpdate = true;
// Generate NPC
let npc = await Player.spawnDefaultCharacter().asyncReady();
npc.worldTransform.position = new Vector(2400, -400, 130)
// Add a listener for the client event "FOLLOW" to allow NPC pathfinding to follow the client player character (NPC pathfinding needs to be called on the server, and player characters cannot use Follow)
Event.addClientListener("FOLLOW", (player) => {
Navigation.follow(npc, player.character, 50, () => { EffectService.playOnGameObject("151570", npc, {slotType: HumanoidSlotType.Rings})});
});
// Add a listener for the client event 'STOPFOLLOW' to stop NPC from following
Event.addClientListener("STOPFOLLOW", (player) => {
Navigation.stopFollow(npc);
});
}
// The following logic is only executed on the client side
if(SystemUtil.isClient()) {
//Add a key method: press key "1" to send a "FOLLOW" event to the server
InputUtil.onKeyDown(Keys.One, () => {
Event.dispatchToServer("FOLLOW");
});
//Add a key method: press key "2" to send "STOPFOLLOW" event to the server
InputUtil.onKeyDown(Keys.Two, () => {
Event.dispatchToServer("STOPFOLLOW");
});
}
}
}
getClosestReachablePoint
• Static
getClosestReachablePoint(targetPoint
, queryExtent
): Vector
other
Automatically find the nearest navigation position to the target point
Parameters
targetPoint Vector | Usage: destination position |
---|---|
queryExtent Vector | Usage: Destination search range |
Returns
Vector | The nearest navigation position within the range (if null, it is not found) |
---|
Usage example: Drag the pathfinding area into the total resource library at position (0,0,0) and set its scaling to (50,50,3). Drag two spheres to be placed at positions (29401960,0) and (44, -3330,0), and then drag a cube to be placed at position (201020,0) and set its scaling to (12,1,1). Create a default script, copy the following code into the script and save it. Drag the script to Ground, save and run the game. Press the script settings button to see the effect
@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;
//Automatically find the nearest navigation position to the target point
InputUtil.onKeyDown(Keys.One,()=>{
let TargetPoint = Navigation.getClosestReachablePoint(NAVabox.worldTransform.position,new Vector(500,500,10));
if(TargetPoint == null){
Console. log (` The navigation position is empty `);
}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;
//Automatically find the nearest navigation position to the target point
InputUtil.onKeyDown(Keys.One,()=>{
let TargetPoint = Navigation.getClosestReachablePoint(NAVabox.worldTransform.position,new Vector(500,500,10));
if(TargetPoint == null){
Console. log (` The navigation position is empty `);
}else{
Navigation.navigateTo(player.character,TargetPoint);
}
});
}
}
getRandomReachablePointInRadius
• Static
getRandomReachablePointInRadius(targetPoint
, radius
): Vector
other
Generate a randomly reachable position in the navigable area within the specified position limit radius
Parameters
targetPoint Vector | Usage: destination position |
---|---|
radius number | Usage: radius range range: unlimited type: floating-point number |
Returns
Vector | Randomly reachable position within the range (null means not found) |
---|
Usage example: Drag the pathfinding area into the total resource library at position (0,0,0) and set its scaling to (50,50,3). Drag two spheres to be placed at positions (29401960,0) and (44, -3330,0), and then drag a cube to be placed at position (201020,0) and set its scaling to (12,1,1). Create a default script, copy the following code into the script and save it. Drag the script to Ground, save and run the game. Press the script settings button to see the effect
@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;
//Generate a randomly reachable position in the navigable area within the specified position limit radius
InputUtil.onKeyDown(Keys.Two,()=>{
let TargetPoint = Navigation.getRandomReachablePointInRadius(new mw.Vector(0,0,0),8000);
if(TargetPoint == null){
Console. log (` The navigation position is empty `);
}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;
//Generate a randomly reachable position in the navigable area within the specified position limit radius
InputUtil.onKeyDown(Keys.Two,()=>{
let TargetPoint = Navigation.getRandomReachablePointInRadius(new mw.Vector(0,0,0),8000);
if(TargetPoint == null){
Console. log (` The navigation position is empty `);
}else{
Navigation.navigateTo(player.character,TargetPoint);
}
});
}
}
navigateTo
• Static
navigateTo(relatedObject
, position
, radius?
, OnSuccess?
, OnFail?
): void
other
Pathfinding movement
Parameters
relatedObject GameObject | Usage: navigation target |
---|---|
position Vector | Usage: target position |
radius? number | Usage: Distance from target radius default: 0 range: No restrictions type: Floating point type |
OnSuccess? () => void | Usage: Successful callback default: null |
OnFail? () => void | Usage: Failed callback default: null |
Usage example: Drag a Navigation Volume into the scene with coordinates of (0, 0, 0) and scale of (50, 10, 3). At the same time, drag three cube with scale of (1, 7, 1) and place them respectively at coordinates of (400, -150, 0), (1000, 150, 0), and (1700, -450, 0). Finally, drag in a target object, close the collision, and place it at coordinates (2400, -400, 0). create a script to mount it under the target object. copy the following code of "Example_Navigation_NavigateTo" in the script, save it, run the game, press key "1", the character navigation moves to the target position, press key "2", and the character stops navigation. The code is as follows:
@Component
export default class Example_Navigation_NavigateTo extends Script {
protected async onStart(): Promise<void> {
// The following logic is only executed on the client side
if(SystemUtil.isClient()) {
//Obtain the target object
let signs = this.gameObject;
//Get the current Player's character
let myChara = Player.localPlayer.character;
//Add a key method: press key "1", character navigation to target position, and play a effect
InputUtil.onKeyDown(Keys.One, () => {
Navigation.navigateTo(myChara, signs.worldTransform.position, 50, () => { EffectService.playOnGameObject("151570", myChara, {slotType: HumanoidSlotType.Rings})});
});
//Add a key method: press key "2", character stops navigation
InputUtil.onKeyDown(Keys.Two, () => {
Navigation.stopNavigateTo(myChara);
});
}
}
}
@Component
export default class Example_Navigation_NavigateTo extends Script {
protected async onStart(): Promise<void> {
// The following logic is only executed on the client side
if(SystemUtil.isClient()) {
//Obtain the target object
let signs = this.gameObject;
//Get the current Player's character
let myChara = Player.localPlayer.character;
//Add a key method: press key "1", character navigation to target position, and play a effect
InputUtil.onKeyDown(Keys.One, () => {
Navigation.navigateTo(myChara, signs.worldTransform.position, 50, () => { EffectService.playOnGameObject("151570", myChara, {slotType: HumanoidSlotType.Rings})});
});
//Add a key method: press key "2", character stops navigation
InputUtil.onKeyDown(Keys.Two, () => {
Navigation.stopNavigateTo(myChara);
});
}
}
}
navigationRaycast
• Static
navigationRaycast(rayStart
, rayEnd
): boolean
other
Judge whether there is any obstacle or beyond the range of the Navigation Volume on the connection between two points
Parameters
rayStart Vector | Usage: starting point |
---|---|
rayEnd Vector | Usage: End Point |
Returns
boolean | Is there any obstacle or out of range in the two-point connection |
---|
Usage example: Drag the pathfinding area into the total resource library at position (0,0,0) and set its scaling to (50,50,3). Drag two spheres to be placed at positions (29401960,0) and (44, -3330,0), and then drag a cube to be placed at position (201020,0) and set its scaling to (12,1,1). Create a default script, copy the following code into the script and save it. Drag the script to Ground, save and run the game. Press the script settings button to see the effect
@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;
//Judge whether there is any obstacle or beyond the range of the Navigation Volume on the connection between two points
InputUtil.onKeyDown(Keys.Three,()=>{
let NavigationCAV = Navigation.navigationRaycast(new Vector(0,0,0),NAVasphere.worldTransform.position)
Console. log (` Is there any obstacle on the straight path: `, 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;
//Judge whether there is any obstacle or beyond the range of the Navigation Volume on the connection between two points
InputUtil.onKeyDown(Keys.Three,()=>{
let NavigationCAV = Navigation.navigationRaycast(new Vector(0,0,0),NAVasphere.worldTransform.position)
Console. log (` Is there any obstacle on the straight path: `, NavigationCAV);
})
}
}
stopFollow
• Static
stopFollow(relatedObject
): void
other
Stop following
Parameters
relatedObject GameObject | Usage: navigation target |
---|
The roles and client NPCs take effect when called by the client, and the dual end takes effect when called by the server
Usage example: Drag a Navigation Volume into the scene with coordinates of (0, 0, 0) and scale of (50, 10, 3). At the same time, drag three cube with scale of (1, 7, 1) and place them respectively at coordinates of (400, -150, 0), (1000, 150, 0), and (1700, -450, 0). Generate an npc at coordinates (2400, -400, 0). create a script to mount it under the target object. copy the following code of "Example_Navigation_Follow" in the script, save it, run the game, press key "1", npc navigation follows Player, press key "2", npc stops following. The code is as follows:
@Component
export default class Example_Navigation_Follow extends Script {
protected async onStart(): Promise<void> {
// The following logic is only executed on the server side
if(SystemUtil.isServer()) {
// Enable the periodic function for each frame
this.useUpdate = true;
// Generate NPC
let npc = await Player.spawnDefaultCharacter().asyncReady();
npc.worldTransform.position = new Vector(2400, -400, 130)
// Add a listener for the client event "FOLLOW" to allow NPC pathfinding to follow the client player character (NPC pathfinding needs to be called on the server, and player characters cannot use Follow)
Event.addClientListener("FOLLOW", (player) => {
Navigation.follow(npc, player.character, 50, () => { EffectService.playOnGameObject("151570", npc, {slotType: HumanoidSlotType.Rings})});
});
// Add a listener for the client event 'STOPFOLLOW' to stop NPC from following
Event.addClientListener("STOPFOLLOW", (player) => {
Navigation.stopFollow(npc);
});
}
// The following logic is only executed on the client side
if(SystemUtil.isClient()) {
//Add a key method: press key "1" to send a "FOLLOW" event to the server
InputUtil.onKeyDown(Keys.One, () => {
Event.dispatchToServer("FOLLOW");
});
//Add a key method: press key "2" to send "STOPFOLLOW" event to the server
InputUtil.onKeyDown(Keys.Two, () => {
Event.dispatchToServer("STOPFOLLOW");
});
}
}
}
@Component
export default class Example_Navigation_Follow extends Script {
protected async onStart(): Promise<void> {
// The following logic is only executed on the server side
if(SystemUtil.isServer()) {
// Enable the periodic function for each frame
this.useUpdate = true;
// Generate NPC
let npc = await Player.spawnDefaultCharacter().asyncReady();
npc.worldTransform.position = new Vector(2400, -400, 130)
// Add a listener for the client event "FOLLOW" to allow NPC pathfinding to follow the client player character (NPC pathfinding needs to be called on the server, and player characters cannot use Follow)
Event.addClientListener("FOLLOW", (player) => {
Navigation.follow(npc, player.character, 50, () => { EffectService.playOnGameObject("151570", npc, {slotType: HumanoidSlotType.Rings})});
});
// Add a listener for the client event 'STOPFOLLOW' to stop NPC from following
Event.addClientListener("STOPFOLLOW", (player) => {
Navigation.stopFollow(npc);
});
}
// The following logic is only executed on the client side
if(SystemUtil.isClient()) {
//Add a key method: press key "1" to send a "FOLLOW" event to the server
InputUtil.onKeyDown(Keys.One, () => {
Event.dispatchToServer("FOLLOW");
});
//Add a key method: press key "2" to send "STOPFOLLOW" event to the server
InputUtil.onKeyDown(Keys.Two, () => {
Event.dispatchToServer("STOPFOLLOW");
});
}
}
}
stopNavigateTo
• Static
stopNavigateTo(relatedObject
): void
other
Navigation stopped
Parameters
relatedObject GameObject | Usage: navigation target |
---|
Usage example: Drag a pathfinding area in the scene with coordinates (0, 0, 0) and scale to (50, 10, 3). At the same time, drag three cubes scaled to (1, 7, 1) and place them at coordinates (400, -150, 0), (1000, 150, 0), and (1700, -450, 0), respectively. Finally, drag in a target object, close the collision, and place it at coordinates (2400, -400, 0). create a script to mount it under the target object. copy the following code of "Example_Navigation_NavigateTo" in the script, save it, run the game, press key "1", the character navigation moves to the target position, press key "2", and the character stops navigation. The code is as follows:
@Component
export default class Example_Navigation_NavigateTo extends Script {
protected async onStart(): Promise<void> {
// The following logic is only executed on the client side
if(SystemUtil.isClient()) {
//Obtain the target object
let signs = this.gameObject;
//Get the current Player's character
let myChara = Player.localPlayer.character;
//Add a key method: press key "1", character navigation to target position, and play a effect
InputUtil.onKeyDown(Keys.One, () => {
Navigation.navigateTo(myChara, signs.worldTransform.position, 50, () => { EffectService.playOnGameObject("151570", myChara, {slotType: HumanoidSlotType.Rings})});
});
//Add a key method: press key "2", character stops navigation
InputUtil.onKeyDown(Keys.Two, () => {
Navigation.stopNavigateTo(myChara);
});
}
}
}
@Component
export default class Example_Navigation_NavigateTo extends Script {
protected async onStart(): Promise<void> {
// The following logic is only executed on the client side
if(SystemUtil.isClient()) {
//Obtain the target object
let signs = this.gameObject;
//Get the current Player's character
let myChara = Player.localPlayer.character;
//Add a key method: press key "1", character navigation to target position, and play a effect
InputUtil.onKeyDown(Keys.One, () => {
Navigation.navigateTo(myChara, signs.worldTransform.position, 50, () => { EffectService.playOnGameObject("151570", myChara, {slotType: HumanoidSlotType.Rings})});
});
//Add a key method: press key "2", character stops navigation
InputUtil.onKeyDown(Keys.Two, () => {
Navigation.stopNavigateTo(myChara);
});
}
}
}