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):booleanother | 
| Follow the target | 
| getClosestReachablePoint( targetPoint:Vector,queryExtent:Vector):Vectorother | 
| Automatically find the nearest navigation position to the target point | 
| getRandomReachablePointInRadius( targetPoint:Vector,radius:number):Vectorother | 
| 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):voidother | 
| Pathfinding movement | 
| navigationRaycast( rayStart:Vector,rayEnd:Vector):booleanother | 
| Judge whether there is any obstacle or beyond the range of the Navigation Volume on the connection between two points | 
| stopFollow( relatedObject:GameObject):voidother | 
| Stop following | 
| stopNavigateTo( relatedObject:GameObject):voidother | 
| 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 
| startPosVector | Usage: starting point | 
|---|---|
| endPosVector | 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 
| relatedObjectGameObject | Usage: navigation target | 
|---|---|
| targetGameObject | 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 
| targetPointVector | Usage: destination position | 
|---|---|
| queryExtentVector | 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 
| targetPointVector | Usage: destination position | 
|---|---|
| radiusnumber | 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 
| relatedObjectGameObject | Usage: navigation target | 
|---|---|
| positionVector | 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 
| rayStartVector | Usage: starting point | 
|---|---|
| rayEndVector | 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 
| relatedObjectGameObject | 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 
| relatedObjectGameObject | 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);
              });
          }
      }
  } Editor API
Editor API