NOTE: Pandai Library is part of Panda 1.7.0
The easiest way to make use of the Pandai library is to use Panda 1.7.0 or later. The Pandai project version 1.0 was released in December, 2009 and integrated into Panda 1.7.0 in January, 2010. One change of note: in demo code found on this web site, rather than "from libpandaai import *" you will instead have "from panda3d.ai import *" based on how the Pandai library was folded into Panda 1.7.0.
Download meshgen tool support for Blender (for use with Panda 1.7.0)
The meshgen tool for 2D pathfinding did not work correctly with Blender. This problem was fixed in July, 2010: you can download the meshgen tool for use with Blender here.
See Panda3d forum post on Pandai for more details.
Download (for use with Panda 1.6.2)
INSTALLATION FOR PANDAI v1.0
Following are the preliminary steps for using the Pandai library:
- Download PandaiV1.0.zip from HERE
- Unzip the file and you should find libpandaai.dll.
- Copy libpandaai.dll and BuildDLL.bat to ${Your Panda3D Installation}\bin\ Eg: C:\Panda3D-1.6.2\bin\
- Run BuildDLL.bat
If there are no errors you should be ready to use Pandai version 1.0 in Python.
Navigation Mesh Generation Tool
Download the Navigation Mesh Generation Tool for Pathfinding from HERE
Note: Since the egg file produced by Maya and Blender uses a different format from 3DSMax, you will need to first convert them into the same format (triangles) by using the following command:
C:\Panda3D-1.6.2\bin\egg-trans -C -o out.egg in.egg
The egg file that is thus generated should be passed to our Navigation Mesh Generation Tool.
C++ Framework Code
Note: If you would like to see our C++ source code, you can download it along with the Doxygen documentation HERE
Note: If you would like to see our mesh generation C++ source code, you can download it HERE
Using Steering Behaviors in Python
(Note: the structure has been changed a bit since the last release)
In your python code,
- You will need to import the library:
from libpandaai import *
- Then you will have to create an object of the AIWorld class:
aiWorld = AIWorld(render)
- Then you will have to create an object of the AICharacter class:
aiChar = AICharacter(string Character_name, NodePath model_nodepath, double mass, double movement_force, double maximum_force)
These parameters will determine how your character moves in the world.
- Then you will have to attach this character to your AIWorld:
aiWorld.addAiChar(aiChar) // Note: aiWorld and aiChar used from above.
- Now you will need to get a handle to the steering behaviors of the AICharacter:
aiBehaviors = aiChar.getAiBehaviors()
- With this handle all the 7 steering behaviors can be called as such:
NOTE:
All the behaviors take a priority value which will be explained in detail after their descriptions.
Also, the movement forces are defined in the character above and there are helper functions to modify them.
SEEK:
aiBehaviors.seek(NodePath target, int priority)
aiBehaviors.seek(Vec3 position, int priority)
'Seek' takes the target or a position to seek. Seek's direction is calculated only during the first instance of the call and so is more efficient than pursuit, if all you want is an object to go to a point.
FLEE:
aiBehaviors.flee(NodePath target, double panic_distance, double relax_distance, int priority)
aiBehaviors.flee(Vec3 position, double panic_distance, double relax_distance, int priority)
'Flee' takes in a target or a position to be fled away from; this position should be static. (For moving objects use Evade).
Panic Distance is the radius of detection.
Relax Distance is the distance away from the panic distance after which the object should stop fleeing once flee has been initiated.
PURSUE:
aiBehaviors.pursue(NodePath target, int priority)
'Pursue' takes the target to be pursued. The direction is continuously calculated here and this behavior would represent a continuous seeker or follower.
EVADE:
aiBehaviors.evade(NodePath target, double panic_distance, double relax_distance, int priority)
'Evade' takes in a target to be fled away from, which can be moving.
Panic Distance is the radius of detection.
Relax Distance is the distance away from the panic distance where the object should stop fleeing once flee has been initiated.
ARRIVAL: (Works with seek and pursuit only for now)
aiBehaviors.arrival(double distance)
'Arrival' is a behavior which is usually used with seek or pursuit so that the object slows down on approach to its target.
Distance is the distance at which to start arrival at.
WANDER:
aiBehaviors.wander(double wander_radius, int flag, double aoe, int priority)
'Wander' is a behavior to set an AI Character to move around an environment realistically with no particular goal direction.
Wander Radius is the radius which controls the degree of wandering (representative of a guiding circle infront of the AI Character)
Flag is a value which can take 4 values, 1 for each plane(0 – XY, 1 – YZ, 2 – XZ, 3 – XYZ wandering) . Default is XY wander
AOE is area of effect and specifies a radius within which the wander target stays to wander.
FLOCK:
// To create the flock
flockObject = Flock(unsigned int flock_id, double vcone_angle, double vcone_radius, unsigned int cohesion_wt, unsigned int separation_wt, unsigned int alignment_wt)
// To add your AI Character to the above created flock
flockObject.addAiChar(aiChar) //aiChar was defined above
aiBehaviors.flock(int priority)
// After all the AI Characters are added to the flock, add the flock to the world
aiWorld.addFlock(flockObject)
'Flock' is a behavior which allows a character to move in a group of AI characters realistically.
Flock id is a value identifying the flock.
VCone Angle is the visibility angle of the character (represented by a cone around it)
VCone Radius is the length of the visibility cone.
Separation Weight, Cohesion Weight and Alignment Weight is the amount of separation force that contributes to the overall flocking behavior.
Some standard values to start you off with:
Type | vcone_angle | vcone_radius | separation_wt | cohesion_wt | alignment_wt | ||||||||||
Normal Pack | 270 | 10 | 2 | 4 | 1 | ||||||||||
Loose Pack | 180 | 10 | 2 | 4 | 5 | ||||||||||
Tight Pack | 45 | 5 | 2 | 4 | 5 |
You could try experimenting with your own values to customize your flock.
OBSTACLE AVOIDANCE:
aiBehaviors.obstacleAvoidance(float feeler_length)
Obstacle Avoidance is a behavior to set an AI Character to move around an environment avoiding the obstacles found in it in a realistic manner.
Feeler length is the range at which the obstacle can be detected by the AI Character(Note : This does not correspond to actual length 1.0 in render. The algorithm computes the feeler’s length based on AI Character’s speed and size and also the Obstacle size and the feeler length which is input to it simply a multiplier)
For the algorithm to work, the obstacles need to be added to the world like this
aiWorld.addObstacles(NodePath obstacle)
The Obstacles can be input as NodePath’s which the algorithm uses to generate the appropriate force to avoid it.
Also you can remove an obstacle at any time needed by using
aiWorld.removeObstacle(NodePath obstacle)
PATH FOLLOW:
aiBehaviors.pathFollow(int priority)
aiBehaviors.addToPath(Vec3 position)
aiBehaviors.startFollow()
position is a point in 3D space which falls on the path which the AI Character needs to traverse. (Note: that you need to add multiple positions to create a path say for example Vec3(-10,10,0) Vec3(0,0,0) Vec3(10,10,0) Vec3(-10,10,0) will generate a triangular path in the XY plane)
Note the addToPath works backwards. So, your last call to addToPath will be your first position your AICharacter will go to.
PATH FINDING:
aiBehaviors.initPathFind(string filename)
This function activates the path finding with A* for the AI Character.
Filename is the name of the file that is generated by the Mesh Generator
(Note: The file is in .csv format)
aiBehaviors.pathFindTo(NodePath np, string type)
aiBehaviors.pathFindTo(Vec3 pos, string type)
This function finds the best path via A* algorithm for the AI Character to reach the target destination and then invokes the path follower to make the object follow the path.
It can accept both NodePath and Vec3 as input position. NodePath is used when the destination is not static. Vec3 is used when the destination is going to be static.
If the AI Character needs to pathfind continuously(say after he has finished traversing to a position, a new destination is acquired ) then input string type as “addPath” else leave as default.
addStaticObstacle(NodePath obstacle);
This function is used if the scripter wants to add Obstacles after the mesh has been generated (Say a stack of boxes fall down and blocks the way) to trigger events. It adds a static obstacle to the navigation mesh and pathfinds around it.
addDynamicObstacle(NodePath obstacle);
This functions work similar to addStaticObstacle except that in this case the obstacles can move and are not static.
Priority
Priority is a new feature which we have added to our behavior system. This feature allows a user to determine the resultant force when there is a conflict between two or more behaviors.
For example: Incase 'A' is pursuing 'T', 'B' is pursuing 'T' and 'A' is also evading 'B'.
This will result in a conflict when A is close to B as it has to decide whether to pursue or evade. This conflict can be solved by determining the priority.
Priority is a value between 0 and 1 which you specify in your behavior functions.
For example: If pursue had a priority of 0.1 and evade had a priority of 1.0, then on conflict the AICharacter will definitely evade more than pursue.
AIWorld Update
Finally, you will need to run the AIWorld update which is a global AI update for every character in the world, in a continuous task.
aiWorld.update()
Note how now there is only one function for our entire AI that you need to use a task for and so it is way more convenient.
Doing the above steps correctly will ensure that Pandai will work properly in your world.
Helper functions
*This is a section we made to ease the job of scripters using our AIs:
For the AICharacter class:
double getMass();
void setMass(double m);
LVecBase3f getVelocity();
double getMaxForce();
void setMaxForce(double max_force);
NodePath getNodePath();
void setNodePath(NodePath np);
For the AIBehaviors class:
aiBehaviors.behaviorStatus(string AIName)
This function returns the status of an AI Behavior whether it is active, paused, done or disabled. Returns -1 if an invalid string is passed.
*Note for pathfinding status, use pathfollow as the string name, since pathfinding is a subset of pathfollow.
To remove any AI after their call has been instantiated.
void removeAi(string "AIName");
*Note for pathfinding removal, use pathfollow as the string name, since pathfinding is a subset of pathfollow.
To pause or resume any AI after their call has been instantiated.
void pauseAi(string "AIName");
void resumeAi(string "AIName");
where AIName refers to:
"all" - removes all the Ai's
"seek" - removes seek
"flee" - removes flee
"pursue" - removes arrival
"evade" - removes pursuit
"arrival" - removes evade
"wander" - removes wander
"flock" - removes flock
"obstacle_avoidance" - removes obstacle_avoidance
"pathfollow" - removes pathfollow
For the AIWorld class:
void addAiChar(AICharacter aiChar);
void removeAiChar(string name);
void addFlock(Flock *flock);
void flockOff(int ID);
void flockOn(int ID);
Flock getFlock(int ID);
addObstacle(NodePath obstacle);
removeObstacle(NodePath obstacle);
printList();
update();
Demos for v1.0:
Fish Demo - Thanks to Rachel Berkowitz for the background music
Dynamic Obstacle Addition Demo
Dynamic Obstacle Avoidance Demo
Roaming Ralph with Pathfinding
Older versions and demos:
PandaiV0.5 and PandaiV0.5_Src_Code
Seek and Flee EGG Demo (for v0.2)