Skip to content
Navigation

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 VectorUsage: starting point
endPos VectorUsage: 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:

ts
@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 GameObjectUsage: navigation target
target GameObjectUsage: being followed by the target
radius? numberUsage: radius from target default: 0 range: unlimited type: floating point
OnSuccess? () => voidUsage: Successful callback (triggered when following the set target range - can be repeated) default: null
OnFail? () => voidUsage: Failed callback (triggered when the following target disappears or leaves the pathfinding area range - can be multiple times) default: null

Returns

booleanWhether 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:

ts
@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 VectorUsage: destination position
queryExtent VectorUsage: Destination search range

Returns

VectorThe 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

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;

    //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 VectorUsage: destination position
radius numberUsage: radius range
range: unlimited
type: floating-point number

Returns

VectorRandomly 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

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;

    //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);
        }
    });
}
}

Static navigateTo(relatedObject, position, radius?, OnSuccess?, OnFail?): void other

Pathfinding movement

Parameters

relatedObject GameObjectUsage: navigation target
position VectorUsage: target position
radius? numberUsage: Distance from target radius default: 0 range: No restrictions type: Floating point type
OnSuccess? () => voidUsage: Successful callback default: null
OnFail? () => voidUsage: 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:

ts
@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);
              });
          }
      }
  }

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 VectorUsage: starting point
rayEnd VectorUsage: End Point

Returns

booleanIs 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

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;

    //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 GameObjectUsage: 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:

ts
@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 GameObjectUsage: 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:

ts
@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);
              });
          }
      }
  }