Recast implementation in Neoaxis 




This is my very crude implementation of the Recast navigation system for the NeoAxis framework. What I'm going to use it mostly for is making my NPC
characters walk randomly inside my scenes. It seems I even broke the attacking system, but this was just a Recast demo anyway.

I always like the use the demo classes as much as I can. This way every time I make something new in my game it works seamlessly with the Demo classes.
GameCharacterAI.cs works as AI for the GameCharacter class and I noticed it already has functionality for movement. There are movement tasks and attack tasks.
The NPC will attack if there is an attack task and if there is not it will move. I was pretty sure that the Recast system calculates the path to the target and then supplies you with waypoints.
I could just drop these waypoints one at a time as a single movetask until the NPC is at the target.

First you need to add the RecastGeneralMapObject to your map.

1. Open up MapEditor. I'm going to use Maps/MainDemo
2. Add Types/Special/RecastNavigationSystem to the map
3. Only thing I changed from default settings was that I gave it a Name == Recast to make it easier to find from code.



4. Now Select "Geometries" from the bottom and click "Add all meshes on the map" from the popup.



5. Click "Rebuild"
6. Recast should build a navigation mesh over the floor that you will see green colored in the next image




7. Add a Types/Units/Bug on the map. Change InitialAI to be a GameCharacterAI and make sure the name is "Bug_0"

8.We are all done. Save the map and open up Visual Studio.

I will cover the basics what I did and then I just link the classes. I won't describe every line of code I changed.

1. Find the RecastNavigationSystem from the map.
2. Initiate pathfinding somehow.
3. Find the path as a series of waypoints.
4. Feed GameCharacterAI with a new waypoint every tine it reaches one.

GameEntities assembly does not see RecastNavigationSystem, so right click on GameEntities -> Add Reference -> RecastNavigationSystem

(1. Find the RecastNavigationSystem from the map.)

Add a variable for recast in GameCharacterAI.cs


...
...
public class GameCharacterAI : AI
{
//optimization
List<Weapon> initialWeapons;

float updateTaskTimer;

[FieldSerialize]
TaskMoveValue taskMove = new TaskMoveValue( null );
[FieldSerialize]
Dynamic taskAttack;

[FieldSerialize]
TaskMoveValue forceTaskMove = new TaskMoveValue( null );
[FieldSerialize]
Dynamic forceTaskAttack;

private List<Vec3> waypoints = new List<Vec3>();
RecastNavigationSystem recast; // <-- this here variable
...
...
...


Use the contructor in GameCharacterAI.cs to find Recast. Remember that you named it "Recast" on the map.


...
...
GameCharacterAIType _type = null; public new GameCharacterAIType Type { get { return _type; } }

public GameCharacterAI()
{
updateTaskTimer = World.Instance.Random.NextFloat() * 3;
recast = (RecastNavigationSystem)Entities.Instance.GetByName("Recast"); //<-- this bit here


}

...
...


(2. Initiate pathfinding somehow.)

Since it is going to be just a method call this can be anything you like. I have PlayerCharacter set up as CustomMessageService listener. This means I can send custom messages from ActionGameWindow and PlayerCharacter catches them.
This is not very pretty using PlayerCharacter to make a totally different Unit (GameCharacter) move, but this was just a demo.

I have this in PlayerCharacter.cs OnPostCreate()



protected override void OnPostCreate( bool loaded )
{
base.OnPostCreate( loaded );

if (EntitySystemWorld.Instance.IsServer())
{
GameNetworkServer.Instance.CustomMessagesService.ReceiveMessage +=
Server_CustomMessagesService_ReceiveMessage;
}


and this as the receiving method in PlayerCharacter.cs


void Server_CustomMessagesService_ReceiveMessage(CustomMessagesServerNetworkService sender,
NetworkNode.ConnectedNode source, string message, string data)
{
if (message == "FOLLOW_ME") //server will receive this
{
GameCharacterAI ai = ((GameCharacter)Entities.Instance.GetByName("Bug_0")).Intellect as GameCharacterAI;

if (ai != null) // we have a "Bug_0" on the map and it has GameCharacterAI as Intellect
{
//initiate movement
ai.RecastGoto(this.Position);
}
}
}


This is how I send the message from ActionGamewindow and in OnKeyDown(KeyEvent e)


..
..
//change camera type
else if (e.Key == EKeys.F7)
{
/*
cameraType = (CameraType)((int)cameraType + 1);
if (cameraType == CameraType.Count)
cameraType = (CameraType)0;

if (GetPlayerUnit() == null)
cameraType = CameraType.Free;

FreeCameraEnabled = cameraType == CameraType.Free;
*/
cameraType = CameraType.TPS;

FreeCameraEnabled = false;
return true;
}
else if (e.Key == EKeys.L) //'L' was pressed. Send a "follow me" message
{
//this is sent from the client
GameNetworkClient.Instance.CustomMessagesService.SendToServer("FOLLOW_ME", "test");
}
..
..


(3. Find the path as a series of waypoints.)

Create a method that the server will call when it gets the "FOLLOW_ME" message.

GameCharacterAI.cs


public void RecastGoto(Vec3 location)
{
if (this.recast != null) //if this fails you don't have Recast on your map or it doesn't have the correct name
{
Vec3[] wpts; //Array of Vec3 for the waypoints

//these values are mostly stolen from the MapEditor's Recast windows.
// first value is the start location
// second value is the end location
// Third value is the distance between waypoints
// rest are kinda vague but seem to affect path steering and max waypoints

recast.FindPath(this.ControlledObject.Position, location, 2f, new Vec3(2f, 2f, 2f), 512, 4192, 16, out wpts);

// create a List so it is easy to remove waypoints

waypoints = new List<Vec3>(wpts);

//start by creating a ForceTaskMove
ForceTaskMove = new TaskMoveValue(waypoints[0]);

//after every new move task remove the waypoint from the list
waypoints.RemoveAt(0);
}
}


(4. Feed GameCharacterAI with a new waypoint every tine it reaches one.)

Then I just supply new MoveTasks in the GameCharacterAI.cs TickTasks() when ever we reach a previous waypoint. Small tweak were also needed like stepsize was 1, but TickTasks() stops if we are closer than 1.5. Bug never moved.

I just link the whole GameCharacterAI.cs and GameCharacter.cs here.

GameCharacterAI.cs

GameCharacter.cs

Gimme any feedback or report if there is a mistake in this tutorial somewhere. Wrote it all from memory.

PS. You might wanna set ViewRadius to 10 for the BUG

--wellu