Recast Navigation

Yo @Cedric … I got a couple of questions about the recast.js implementation.

First… this is more of an FYI… But the if you use plugin.computePath to show debug path to the destination point and then use crowd.agentGoto to the same destination point. The paths CAN SOMETIMES be different. Like the agentGoto is using a slightly SHORTER path the the compute path… So if you are using crowd.agentGoto and recastPlugin.computePath to the draw debug lines to show the route… they CAN BE DIFFERENT

IF you can check that out… that would be awesome.

Number two… how do you MOVE ALONG A PATH…
What if i want to use computePath to get the array of positions for path… how would move the agent transform along that path… and what is recastPlugin.moveAlong for ??? Is that different than trying to move the navigation agent transform along the computed path array of points ???

Number three… How do i Cancel or STOP a navigation ???

And Last… how can tell when the navigation has reached the destination point ???

  1. yes, that’s expected. Nothing can guarantee that the agent will walk that exact path. Some obstacles (other agents) can make the agent path differ than the expectation. Also, the agent will try to smooth that path. Use it more like an indication than a respected path.

The agents are independant. Once you ask them to move to a destination, they do it on their own (with acceleration, path smoothing,…). If you want to do it yourself, then, with the path points array, you can do so interpolation based on distance. once the walked length is > to a path segment length, then your are on the next path segment

  1. simply ask the agent to go to its current position.
    recast checks the distance between the agent position and the destination position. Once it’s < to a threshold, you are on destination

Thanks For The Info Bro :slight_smile:

1 Like

YO @Cedric
What does plugin.moveAlong Do ???

I did it to solve this issue : Constraining a UniversalCamera on a Navigation Mesh

Yo @Cedric … Do you have an example using the agentGoto ???

How do you handle rotation …

Here is how i am trying to handle agentGoto…

This class basically using a DUMMY Transform node to create the crowd agent… then every frame i get the agent position and calculate a rotation (of that dummy mesh as the agent) …

then i sync the real transform node i want to move.

FYI
My NavigationAgent Class :

module BABYLON {
    /**
     * Babylon navigation agent pro class (Unity Style Navigation Agent System)
     * @class NavigationAgent - All rights reserved (c) 2020 Mackey Kinard
     */
    export class NavigationAgent extends BABYLON.ScriptComponent {
        private type: number;
        private radius: number;
        private height: number;
        private climb: number;
        private slope: number;
        private speed: number;
        private baseOffset: number;
        private avoidRadius: number;
        private avoidHeight: number;
        private acceleration: number;
        private angularSpeed: number;
        private areaMask: number;
        private autoRepath: boolean;
        private autoBraking: boolean;
        private autoTraverseOffMeshLink: boolean;
        private avoidancePriority: number;
        private obstacleAvoidanceType: number;
        private stoppingDistance: number;
        private startPosition:BABYLON.Vector3 = BABYLON.Vector3.Zero();

        public heightOffset:number = 0;
        public updatePosition:boolean = true;
        public updateRotation:boolean = true;
        public updateVelocity:number = 0.05;
        public getAgentType():number { return this.type; }
        public getAgentIndex():number { return this.m_agentIndex; }
        public getAgentRadius():number { return this.radius; }
        public getAgentHeight():number { return this.height; }
        public getAgentClimb():number { return this.climb; }
        public getAgentSlope():number { return this.slope; }
        public getAgentSpeed():number { return this.speed; }
        public getAgentOffset():number { return this.baseOffset; }

        protected m_agentIndex:number = -1;
        protected m_agentTrans:BABYLON.TransformNode = null;
        protected m_agentParams:BABYLON.IAgentParameters = null;
        protected m_agentMovement:BABYLON.Vector3 = new BABYLON.Vector3(0.0, 0.0, 0.0);
        protected m_agentDirection:BABYLON.Vector3 = new BABYLON.Vector3(0.0, 0.0, 1.0);
        public constructor(transform: BABYLON.TransformNode, scene: BABYLON.Scene, properties: any = {}) {
            super(transform, scene, properties);
            this.type = this.getProperty("type", this.type);
            this.radius = this.getProperty("radius", this.radius);
            this.height = this.getProperty("height", this.height);
            this.climb = this.getProperty("climb", this.climb);
            this.slope = this.getProperty("slope", this.slope);
            this.speed = this.getProperty("speed", this.speed);
            this.baseOffset = this.getProperty("offset", this.baseOffset);
            this.angularSpeed = this.getProperty("angularspeed", this.angularSpeed);
            this.acceleration = this.getProperty("acceleration", this.acceleration);
            this.stoppingDistance = this.getProperty("stoppingdistance", this.stoppingDistance);
            this.autoBraking = this.getProperty("autobraking", this.autoBraking);
            this.avoidRadius = this.getProperty("avoidradius", this.avoidRadius);
            this.avoidHeight = this.getProperty("avoidheight", this.avoidHeight);
            this.obstacleAvoidanceType = this.getProperty("avoidquality", this.obstacleAvoidanceType);
            this.avoidancePriority = this.getProperty("avoidpriority", this.avoidancePriority);
            this.autoTraverseOffMeshLink = this.getProperty("autotraverse", this.autoTraverseOffMeshLink);
            this.autoRepath = this.getProperty("autopepath", this.autoRepath);
            this.areaMask = this.getProperty("areamask", this.areaMask);
        }
        protected awake(): void { this.awakeNavigationAgent(); }
        protected update(): void { this.updateNavigationAgent(); }
        protected destroy(): void { this.destroyNavigationAgent(); }

        //////////////////////////////////////////////////////
        // Navigation Private Functions                     //
        //////////////////////////////////////////////////////

        private awakeNavigationAgent():void {
            BABYLON.Utilities.ValidateTransformQuaternion(this.transform);
            //// DEBUG: this.m_agentTrans = BABYLON.Mesh.CreateBox((this.transform.name + "Agent"), 2, this.scene);
            this.m_agentTrans = new BABYLON.TransformNode((this.transform.name + ".Agent"), this.scene);
            this.m_agentTrans.position = new BABYLON.Vector3(0.0, 0.0, 0.0);
            this.m_agentTrans.rotation = new BABYLON.Vector3(0.0, 0.0, 0.0);
            BABYLON.Utilities.ValidateTransformQuaternion(this.m_agentTrans);
        }

        private updateNavigationAgent():void {
            const plugin:BABYLON.RecastJSPlugin = BABYLON.SceneManager.GetNavigationTools();
            const crowd:BABYLON.ICrowd = BABYLON.SceneManager.GetCrowdInterface(this.scene);
            if (crowd == null) return; // Note: No Detour Navigation Mesh Available Yet
            if (this.m_agentIndex < 0) {
                this.m_agentParams = {
                    radius: this.radius,
                    height: this.height,
                    maxSpeed: this.speed,
                    maxAcceleration: this.acceleration,
                    collisionQueryRange: 0.5,
                    pathOptimizationRange: 0.0,
                    separationWeight: 1.0
                };
                BABYLON.Utilities.GetAbsolutePositionToRef(this.transform, this.startPosition);
                this.m_agentIndex = crowd.addAgent(this.startPosition, this.m_agentParams, this.m_agentTrans);
            }
            if (this.updateRotation === true) {
                const velocity:BABYLON.Vector3 = this.getAgentVelocity();
                // ..
                // TODO - Make Better Rotation Code that does not turn around or jitter at destination point
                // ..
                if (velocity.length() >= this.updateVelocity) {
                    velocity.normalize();
                    // TODO - Optimize compute direction
                    const dir:BABYLON.Vector3 = this.m_agentDirection.clone();
                    const factor:number = (0.005 * this.angularSpeed);
                    this.m_agentDirection = new BABYLON.Vector3((dir.x + (velocity.x - dir.x) * factor), (dir.y + (velocity.y - dir.y) * factor), (dir.z + (velocity.z - dir.z) * factor));
                    this.m_agentDirection.normalize();
                    // TODO - Optimize euler angle rotation
                    const targetAngle:number = (Math.PI * 0.5 - Math.atan2(this.m_agentDirection.z, this.m_agentDirection.x));
                    BABYLON.Quaternion.FromEulerAnglesToRef(0.0, targetAngle, 0.0, this.transform.rotationQuaternion);
                }
            }
            if (this.updatePosition === true) {
                const position:BABYLON.Vector3 = this.getAgentPosition();
                position.y += (this.baseOffset + this.heightOffset);
                this.transform.position.copyFrom(position);
            }
        }
        private destroyNavigationAgent():void {
            this.m_agentIndex = -1;
            this.m_agentMovement = null;
            this.m_agentDirection = null;
            if (this.m_agentTrans != null) {
                this.m_agentTrans.dispose();
                this.m_agentTrans = null;
            }
        }

        //////////////////////////////////////////////////////
        // Navigation Public Functions                      //
        //////////////////////////////////////////////////////

        /** Move agent relative to current position. */
        public move(offset: BABYLON.Vector3, closetPoint:boolean = true): void {
            const plugin:BABYLON.RecastJSPlugin = BABYLON.SceneManager.GetNavigationTools();
            const crowd:BABYLON.ICrowd = BABYLON.SceneManager.GetCrowdInterface(this.scene);
            if (plugin != null && crowd != null) {
                crowd.getAgentPosition(this.m_agentIndex).addToRef(offset, this.m_agentMovement);
                if (this.m_agentIndex >= 0) crowd.agentGoto(this.m_agentIndex, (closetPoint === true) ? plugin.getClosestPoint(this.m_agentMovement) : this.m_agentMovement);
            }
        }
        /** Teleport agent to destination point. */
        public teleport(destination: BABYLON.Vector3, closetPoint:boolean = true): void {
            const plugin:BABYLON.RecastJSPlugin = BABYLON.SceneManager.GetNavigationTools();
            const crowd:BABYLON.ICrowd = BABYLON.SceneManager.GetCrowdInterface(this.scene);
            if (crowd != null && this.m_agentIndex >= 0) crowd.agentTeleport(this.m_agentIndex, (closetPoint === true) ? plugin.getClosestPoint(destination) : destination);
        }
        /** Sets agent current destination point. */
        public setDestination(destination: BABYLON.Vector3, closetPoint:boolean = true): void {
            const plugin:BABYLON.RecastJSPlugin = BABYLON.SceneManager.GetNavigationTools();
            const crowd:BABYLON.ICrowd = BABYLON.SceneManager.GetCrowdInterface(this.scene);
            if (plugin != null && crowd != null && this.m_agentIndex >= 0) crowd.agentGoto(this.m_agentIndex, (closetPoint === true) ? plugin.getClosestPoint(destination) : destination);
        }
        /** Gets agent current world space position. */
        public getAgentPosition(): BABYLON.Vector3 {
            const crowd:BABYLON.ICrowd = BABYLON.SceneManager.GetCrowdInterface(this.scene);
            if (crowd != null && this.m_agentIndex >= 0) return crowd.getAgentPosition(this.m_agentIndex);
            else return null;
        }
        /** Gets agent current world space velocity. */
        public getAgentVelocity(): BABYLON.Vector3 {
            const crowd:BABYLON.ICrowd = BABYLON.SceneManager.GetCrowdInterface(this.scene);
            if (crowd != null && this.m_agentIndex >= 0) return crowd.getAgentVelocity(this.m_agentIndex);
            else return null;
        }
        /** Cancel current waypoint path navigation. */
        public cancelNavigation():void {
            // TODO - Cancel Navigation
        }
    }
}

So using script… i could do something like this:

const agent = this.getComponent("BABYLON.NavigationAgent");
agent.setDestination(pickedPosition);

But i get a bit rotation jitter at the end of the navigation… Im sure its cause how i am rotating the real mesh to try and sync to the GHOST transform node i used to create the agent.

Anyways… How are you moving a mesh/player around using agentGoTo ???

goto example here : https://www.babylonjs-playground.com/#X5XCVT#240

the jittering might be because the distance between the 2 points is too small. and normalizing the vector leads to jiterring. pretty classic. before normalizing check the length of the vector is > to a threshold (like 0.01). if not, use previously computed normalized vector.

1 Like

Thanks again @Cedric :slight_smile:

1 Like

BTW, you can get the agent velocity to help you compute a direction angle (atan(velocity.z, velocity.x) IIRC)…if the velocity length is not too small.

What is IIRC ?

“if I recall correctly”^

1 Like

I believe i am currently calculating the direction angle already:

const targetAngle:number = (Math.PI * 0.5 - Math.atan2(this.m_agentDirection.z, this.m_agentDirection.x));
BABYLON.Quaternion.FromEulerAnglesToRef(0.0, targetAngle, 0.0, this.transform.rotationQuaternion);

Yo @Cedric … I am still getting jitter when trying to rotate the agent using the agentVelocity.

Do you know of ANY other way to handle rotation ???

Is there a way to expose the current Waypoint position so i can rotate the agent in that direction ???

Also… using the agent velocity to get the direction does not work to well when moving across off mesh links… Whatever the rotation the agent was in when it hits the off mesh link it stays in until the hit the end off mesh link… then it correctly rotates

Please take a look at how i am rotating the agent on each frame based of agent velocity

private static DEG_UNIT:number = (Math.PI / 360);
private static TARGET_ANGLE:number = (Math.PI * 0.5);
public minimumVelocity:number = 1.5;
const position:BABYLON.Vector3 = this.getAgentPosition();
const velocity:BABYLON.Vector3 = this.getAgentVelocity();
if (this.updateRotation === true) {
    if (velocity.length() >= this.minimumVelocity) {
        velocity.normalize();
        // ..
        const dir:BABYLON.Vector3 = this.m_agentDirection.clone();
        const factor:number = (BABYLON.NavigationAgent.DEG_UNIT * this.angularSpeed * 0.5);
        this.m_agentDirection.set((dir.x + (velocity.x - dir.x) * factor), (dir.y + (velocity.y - dir.y) * factor), (dir.z + (velocity.z - dir.z) * factor));
        this.m_agentDirection.normalize();
        // ..
        const targetAngle:number = (BABYLON.NavigationAgent.TARGET_ANGLE - Math.atan2(this.m_agentDirection.z, this.m_agentDirection.x));
        BABYLON.Quaternion.FromEulerAnglesToRef(0.0, targetAngle, 0.0, this.transform.rotationQuaternion);
    }
}
if (this.updatePosition === true) {
    position.y += (this.baseOffset + this.heightOffset);
    this.transform.position.copyFrom(position);
}

But still jitters… I saw the example you listed before… It does not handle rotation though. Do you know of any recast/detour examples that rotate the agent when moving in response to a agent.goto call ???

I’ve updated the PG to support interpolated rotation. https://www.babylonjs-playground.com/#X5XCVT#241

check line 112.
Basically, I removed the parenting between the mesh and the agent transform. Then, each frame, I set the transform position to the cube position and I do a lerp between the current rotation angle to the desired one that is computed from the velocity.

Also, in order to remove the jittering, I set the threshold value to 0.2. A lower value will give more jittering.

1 Like

Yo @Cedric … So i could not get your demo working… there was alot of extra rotation and sometimes the wrong way… But i get what you are saying.

There is still the issue of traversing off mesh links… When doing so… the agent velocity MUST be different because the rotation is locked at whatever it was when you hit the off mesh link start position.

Is there any way you know of to get the actual waypoints or path points the agent is traversing from the agent.goTo calls?

FYI… I finally got it to work using a BOTH a velocity threshold for rotation and an overall distance to final destination point threshold … 0.2 seems to work good for both thresholds… But now NO JITTER … Yeah :slight_smile:

module BABYLON {
    /**
     * Babylon navigation agent pro class (Unity Style Navigation Agent System)
     * @class NavigationAgent - All rights reserved (c) 2020 Mackey Kinard
     */
    export class NavigationAgent extends BABYLON.ScriptComponent {
        private static DEG_UNIT:number = (Math.PI / 360);
        private static ANGLE_FACTOR:number = (Math.PI * 0.5);
        private static UNITY_SPEED_FACTOR:number = 0.5;
        private type: number;
        private radius: number;
        private height: number;
        private climb: number;
        private slope: number;
        private speed: number;
        private baseOffset: number;
        private avoidRadius: number;
        private avoidHeight: number;
        private acceleration: number;
        private angularSpeed: number;
        private areaMask: number;
        private autoRepath: boolean;
        private autoBraking: boolean;
        private autoTraverseOffMeshLink: boolean;
        private avoidancePriority: number;
        private obstacleAvoidanceType: number;
        private stoppingDistance: number;
        private moveDirection:BABYLON.Vector3 = BABYLON.Vector3.Zero();
        private resetPosition:BABYLON.Vector3 = BABYLON.Vector3.Zero();
        
        public heightOffset:number = 0;
        public updatePosition:boolean = true;
        public updateRotation:boolean = true;
        public velocityEpsilon:number = 0.2;
        public distanceEpsilon:number = 0.2;
        public isNavigating():boolean { return (this.m_agentDestination != null); }
        public getAgentType():number { return this.type; }
        public getAgentIndex():number { return this.m_agentIndex; }
        public getAgentRadius():number { return this.radius; }
        public getAgentHeight():number { return this.height; }
        public getAgentClimb():number { return this.climb; }
        public getAgentSlope():number { return this.slope; }
        public getAgentSpeed():number { return this.speed; }
        public getAgentOffset():number { return this.baseOffset; }

        protected m_agentIndex:number = -1;
        protected m_agentTrans:BABYLON.TransformNode = null;
        protected m_agentParams:BABYLON.IAgentParameters = null;
        protected m_agentMovement:BABYLON.Vector3 = new BABYLON.Vector3(0.0, 0.0, 0.0);
        protected m_agentDirection:BABYLON.Vector3 = new BABYLON.Vector3(0.0, 0.0, 1.0);
        protected m_agentDestination:BABYLON.Vector3 = null;
        public constructor(transform: BABYLON.TransformNode, scene: BABYLON.Scene, properties: any = {}) {
            super(transform, scene, properties);
            this.type = this.getProperty("type", this.type);
            this.radius = this.getProperty("radius", this.radius);
            this.height = this.getProperty("height", this.height);
            this.climb = this.getProperty("climb", this.climb);
            this.slope = this.getProperty("slope", this.slope);
            this.speed = this.getProperty("speed", this.speed);
            this.baseOffset = this.getProperty("offset", this.baseOffset);
            this.angularSpeed = this.getProperty("angularspeed", this.angularSpeed);
            this.acceleration = this.getProperty("acceleration", this.acceleration);
            this.stoppingDistance = this.getProperty("stoppingdistance", this.stoppingDistance);
            this.autoBraking = this.getProperty("autobraking", this.autoBraking);
            this.avoidRadius = this.getProperty("avoidradius", this.avoidRadius);
            this.avoidHeight = this.getProperty("avoidheight", this.avoidHeight);
            this.obstacleAvoidanceType = this.getProperty("avoidquality", this.obstacleAvoidanceType);
            this.avoidancePriority = this.getProperty("avoidpriority", this.avoidancePriority);
            this.autoTraverseOffMeshLink = this.getProperty("autotraverse", this.autoTraverseOffMeshLink);
            this.autoRepath = this.getProperty("autopepath", this.autoRepath);
            this.areaMask = this.getProperty("areamask", this.areaMask);
        }
        protected awake(): void { this.awakeNavigationAgent(); }
        protected update(): void { this.updateNavigationAgent(); }
        protected late(): void { this.lateNavigationAgent(); }
        protected destroy(): void { this.destroyNavigationAgent(); }

        /** Register handler that is triggered before the navigation update */
        public onPreUpdateObservable = new BABYLON.Observable<BABYLON.TransformNode>();
        /** Register handler that is triggered after the navigation update */
        public onPostUpdateObservable = new BABYLON.Observable<BABYLON.TransformNode>();
        /** Register handler that is triggered when the navigation is complete */
        public onNavCompleteObservable = new BABYLON.Observable<BABYLON.TransformNode>();

        //////////////////////////////////////////////////////
        // Navigation Private Functions                     //
        //////////////////////////////////////////////////////

        private awakeNavigationAgent():void {
            BABYLON.Utilities.ValidateTransformQuaternion(this.transform);
            //// DEBUG: this.m_agentTrans = BABYLON.Mesh.CreateBox((this.transform.name + "Agent"), 2, this.scene);
            this.m_agentTrans = new BABYLON.TransformNode((this.transform.name + ".Agent"), this.scene);
            this.m_agentTrans.position = new BABYLON.Vector3(0.0, 0.0, 0.0);
            this.m_agentTrans.rotation = new BABYLON.Vector3(0.0, 0.0, 0.0);
            BABYLON.Utilities.ValidateTransformQuaternion(this.m_agentTrans);
        }
        private updateNavigationAgent():void {
            const crowd:BABYLON.ICrowd = BABYLON.SceneManager.GetCrowdInterface(this.scene);
            if (crowd == null) return; // Note: No Detour Navigation Mesh Available Yet
            if (this.m_agentIndex < 0) {
                this.m_agentParams = {
                    radius: this.radius,
                    height: this.height,
                    maxSpeed: this.speed,
                    maxAcceleration: this.acceleration,
                    collisionQueryRange: 0.5,
                    pathOptimizationRange: 0.0,
                    separationWeight: 1.0
                };
                BABYLON.Utilities.GetAbsolutePositionToRef(this.transform, this.resetPosition);
                this.m_agentIndex = crowd.addAgent(this.resetPosition, this.m_agentParams, this.m_agentTrans);
            }
            if (this.m_agentDestination != null) {
                if (this.updatePosition === true || this.updateRotation === true) {
                    if (this.onPreUpdateObservable.hasObservers() === true) {
                        this.onPreUpdateObservable.notifyObservers(this.transform);
                    }
                    const position:BABYLON.Vector3 = this.getAgentPosition();
                    const velocity:BABYLON.Vector3 = this.getAgentVelocity();
                    if (this.updateRotation === true) {
                        if (velocity.length() > this.velocityEpsilon) {
                            velocity.normalize();
                            const factor:number = (this.angularSpeed * BABYLON.NavigationAgent.DEG_UNIT * BABYLON.NavigationAgent.UNITY_SPEED_FACTOR);
                            this.moveDirection.copyFrom(this.m_agentDirection);
                            this.m_agentDirection.set((this.moveDirection.x + (velocity.x - this.moveDirection.x) * factor), (this.moveDirection.y + (velocity.y - this.moveDirection.y) * factor), (this.moveDirection.z + (velocity.z - this.moveDirection.z) * factor));
                            this.m_agentDirection.normalize();
                            const targetAngle:number = (BABYLON.NavigationAgent.ANGLE_FACTOR - Math.atan2(this.m_agentDirection.z, this.m_agentDirection.x));
                            BABYLON.Quaternion.FromEulerAnglesToRef(0.0, targetAngle, 0.0, this.transform.rotationQuaternion);
                        }
                    }
                    if (this.updatePosition === true) {
                        position.y += (this.baseOffset + this.heightOffset);
                        this.transform.position.copyFrom(position);
                    }
                    if (this.onPostUpdateObservable.hasObservers() === true) {
                        this.onPostUpdateObservable.notifyObservers(this.transform);
                    }
                }
            }
        }
        private lateNavigationAgent():void {
            const position:BABYLON.Vector3 = this.getAgentPosition();
            if (this.m_agentDestination != null) {
                const distance:number = BABYLON.Vector3.Distance(position, this.m_agentDestination);
                if (distance < this.distanceEpsilon) {
                    this.cancelNavigation();
                    if (this.onNavCompleteObservable.hasObservers() === true) {
                        this.onNavCompleteObservable.notifyObservers(this.transform);
                    }
                }
            }
        }
        private destroyNavigationAgent():void {
            this.m_agentIndex = -1;
            this.m_agentMovement = null;
            this.m_agentDirection = null;
            this.onPreUpdateObservable.clear();
            this.onPreUpdateObservable = null;
            this.onPostUpdateObservable.clear();
            this.onPostUpdateObservable = null;
            this.onNavCompleteObservable.clear();
            this.onNavCompleteObservable = null;
            if (this.m_agentTrans != null) {
                this.m_agentTrans.dispose();
                this.m_agentTrans = null;
            }
        }

        //////////////////////////////////////////////////////
        // Navigation Public Functions                      //
        //////////////////////////////////////////////////////

        /** Move agent relative to current position. */
        public move(offset: BABYLON.Vector3, closetPoint:boolean = true): void {
            const plugin:BABYLON.RecastJSPlugin = BABYLON.SceneManager.GetNavigationTools();
            const crowd:BABYLON.ICrowd = BABYLON.SceneManager.GetCrowdInterface(this.scene);
            if (plugin != null && crowd != null) {
                crowd.getAgentPosition(this.m_agentIndex).addToRef(offset, this.m_agentMovement);
                if (closetPoint === true) this.m_agentDestination = plugin.getClosestPoint(this.m_agentMovement);
                else this.m_agentDestination = this.m_agentMovement.clone();
                if (this.m_agentIndex >= 0) crowd.agentGoto(this.m_agentIndex, this.m_agentDestination);
            }
        }
        /** Teleport agent to destination point. */
        public teleport(destination: BABYLON.Vector3, closetPoint:boolean = true): void {
            const plugin:BABYLON.RecastJSPlugin = BABYLON.SceneManager.GetNavigationTools();
            const crowd:BABYLON.ICrowd = BABYLON.SceneManager.GetCrowdInterface(this.scene);
            if (closetPoint === true) this.m_agentDestination = plugin.getClosestPoint(destination);
            else this.m_agentDestination = destination.clone();
            if (crowd != null && this.m_agentIndex >= 0) crowd.agentTeleport(this.m_agentIndex, this.m_agentDestination);
        }
        /** Sets agent current destination point. */
        public setDestination(destination: BABYLON.Vector3, closetPoint:boolean = true): void {
            const plugin:BABYLON.RecastJSPlugin = BABYLON.SceneManager.GetNavigationTools();
            const crowd:BABYLON.ICrowd = BABYLON.SceneManager.GetCrowdInterface(this.scene);
            if (closetPoint === true) this.m_agentDestination = plugin.getClosestPoint(destination);
            else this.m_agentDestination = destination.clone();
            if (plugin != null && crowd != null && this.m_agentIndex >= 0) crowd.agentGoto(this.m_agentIndex, this.m_agentDestination);
        }
        /** Gets agent current world space velocity. */
        public getAgentVelocity(): BABYLON.Vector3 {
            const crowd:BABYLON.ICrowd = BABYLON.SceneManager.GetCrowdInterface(this.scene);
            if (crowd != null && this.m_agentIndex >= 0) return crowd.getAgentVelocity(this.m_agentIndex);
            else return null;
        }
        /** Gets agent current world space position. */
        public getAgentPosition(): BABYLON.Vector3 {
            const crowd:BABYLON.ICrowd = BABYLON.SceneManager.GetCrowdInterface(this.scene);
            if (crowd != null && this.m_agentIndex >= 0) return crowd.getAgentPosition(this.m_agentIndex);
            else return null;
        }
        /** Reset the agent to transform world space position. */
        public resetAgentPosition():void {
            this.m_agentDestination = null; // Note: Disable Auto Position Update
            const crowd:BABYLON.ICrowd = BABYLON.SceneManager.GetCrowdInterface(this.scene);
            BABYLON.Utilities.GetAbsolutePositionToRef(this.transform, this.resetPosition);
            if (crowd != null && this.m_agentIndex >= 0) crowd.agentTeleport(this.m_agentIndex, this.resetPosition);
        }
        /** Cancel current waypoint path navigation. */
        public cancelNavigation():void {
            this.m_agentDestination = null; // Note: Disable Auto Position Update
            const crowd:BABYLON.ICrowd = BABYLON.SceneManager.GetCrowdInterface(this.scene);
            const position:BABYLON.Vector3 = this.getAgentPosition();
            if (position != null && crowd != null && this.m_agentIndex >= 0) {
                crowd.agentTeleport(this.m_agentIndex, position);
                position.y += (this.baseOffset + this.heightOffset);
                this.transform.position.copyFrom(position);
            }
        }
    }
}
1 Like

It seems possible to query for next target


I can try to expose that in a couple days.

Please… That would be so awesome … Thank You Very Much For All Your Help :slight_smile:

I took some time to do it : Get next path step point for an agent by CedricGuillemet · Pull Request #9139 · BabylonJS/Babylon.js · GitHub

2 Likes

Yo @Cedric … D you know if something changed in emscripten ???

now i get an error when try to do a python make.py

Does the recastjs requirr a certain version of emscripten … What version of emscripten are you using to build the project with ?

Yes, for some unkown reason, it needs emscripten 1.38
newer version do not compile or do not run. I didn’t take time to figure that out yet but it’s on my list.