|
Alrighty! This page will be a work in progress. One of the first
things that I will be writing a tutorial on will be a waypoint system.
I used this waypoint system in an open city game that I helped develop.
I remember when I started writing my first waypoint system I looked
everywhere for some information on how to write it and couldn't
find squat! So I want to help some people out and offer at least
a little something to start off on. :) One suggestion to anyone
programming a project is to use something like CVS to keep everyone
up to date. Using that system allowed us to move ahead faster, stay
more focused, and saved us a LOT of integration time. Please let
me know if there is anything that is not explained thoroughly enough,
or have any questions about this system.
The waypoint system that I designed up has two parts to is: the
first part is the actual waypoint itself and then the manager that
manages all of the waypoints. The waypoint itself contains all of
the information that each of the cars would possibly need to access.
I did not necessarily program in all of the features yet, but the
information is there for future implementation. Take note: this
system is not fully optimized. I optimized the best I could for
the amount of time that I was allowed to work on this but as with
all programming, there is about 200 ways to do it and this one might
not be the best one! :) But it should give you a good start. Also
take note that the traffic lights and manager are integrated in
with the waypoint system somewhat and that I am using one row of
waypoints down the middle of the road and having them "project"
a waypoint for a car to chase. We also made a math library and other
classes that were used extensively throughout the game... so for
this reason a copy and paste wont work too well :( Sorry!
CAR AI
The car consists of its physics (dynamics), AI, bounding sphere
for collision, and other information that we needed for our game.
These are the variables that I have in my CarAI class.
// The cars around us. Use this to detect a possible collision
CCarAI *m_surroundingCars[MAX_CAR_POINTERS];
// Our destination waypoints. Where are we going next?
CWayPoint *m_nextWaypoints[MAX_NEXT_WAYPOINTS];
// How many waypoints we are using. # of ones we are connected to.
Most would usually be 4 and that would be at // a 4-way intersection:
3 waypoints for other lanes and 1 for the waypoint before us.
GLint m_iNumDestinationWaypoints;
// Current position
ngVector3D m_currentPosition;
// Last valid point
ngVector3D m_lastValidPosition;
// Which way are we going? I used two axis, like X and Z. Used
so waypoint will be able to correctly project the //destination
waypoint.
GLuint m_uiDrivingDirection;
// Length of car. How many cars can fit in one road length?
GLfloat m_fLengthOfCar;
// Target Speed. I add a fudge-factor to make it look more realistic.
Based on speed limit.
GLfloat m_fTargetSpeed;
// Can we move? Use this for multiple things: too many cars in
intersection, crash ahead, collision about to happen, // etc.
bool m_bAllowedToMove;
// Are we the player?
bool m_bIsPlayer;
// Our last valid waypoint. Can use for debugging
CWayPoint *m_pLastValidWaypoint;
// What mode we are in. Driving mode and turning mode are ones
mainly used.
GLint m_iCurrentMode;
GLint m_iLastMode;
// Our current turning mode
GLint m_iCurrentTurnMode;
// What lane are we in?
GLint m_iCurrentLane;
// Our dynamics
ngVehicleDynamics *m_pThisCarsDynamics;
// The manager
CWaypointManager *m_waypointManager;
// Our turning vector
ngVector3D m_turnVector;
// The matrix used to set up turn points
ngMatrix4X4 m_posRotateBy, m_negRotateBy;
The logic for the car is as follows:
1. The vehicle queries the next waypoint to see if it is an intersection.
2. If the waypoint is an intersection (this is any turn in the
road from one road to another) the vehicle sees if it is allowed
to go by querying the light to see if it is green. If the waypoint
isn't an intersection then the car continues to move towards the
waypoint.
3. If the light is green then the car continues to the waypoint.
Once it hits the waypoint the vehicle calculates if it will be going
straight by dotting the AT (forward vector) of the
car and the vector of the current waypoint and destination waypoint.
If its within tolerance then we are going straight, if its not,
then we cross the AT and that same vector to determine
right or left. If we are turning then we calculate the waypoints
that it needs to in order to achieve a proper curve by finding a
corner of the road (which corner is determined by which way we are
turning) and rotating that point around that the number of times
we wish to turn. The higher the number of points, the more precise
the turning will look and the more computationally expensive the
calculation will be. Essentially, we are getting the center of a
circle and creating the points along the outside of the circle using
the circles radius.
When the vehicle is adding the new waypoints to the car's known
waypoints I am pushing them to the front of that list. This insures
that the waypoint we want to go to next is the waypoint that will
start us turning.
If the light is yellow or red then the car checks to see if its
distance from the waypoint is greater or less than the WAYPOINT_TOLERENCE.
(This is the enum that we use to say, "If I am plus or minus
this amount, I am at the waypoint.") We want to stop just short
of triggering the switching of waypoints. This way our vehicle will
keep querying the light. If you hit the waypoint, then it would
be deleted from the list and we would never stop.
4. Once we reach a waypoint, we delete that one off of the queue,
and add one more random one to the end of the list. We also check
to see what the speed limit for our next waypoint is and determine
our targetSpeed. To make the vehicles look a little more realistic
I come up with a random number between 1 and 6 and then randomly
add or subtract it from the speed limit of the next waypoint.
WAYPOINTS
As said before, the waypoints contain all of the information that
the vehicle could possibly need. Listed below is the information
that I had my waypoint class contained.
// Type of waypoints
enum WayPointTypes { WP_NONE = 0, WP_ROAD, WP_ONE_WAY, WP_INTERSECTION,
WP_STOP_SIGN,
WP_TRAFFIC_LIGHT, WP_NUM_POINT_TYPES,};
// Types of roads
enum RoadTypes { WP_DIRT = 1, WP_STREET, WP_HIGHWAY, };
//////////////////////////////-- Class --/////////////////////////////
//
//-- NAME: CWayPoint
//
//-- PURPOSE: Create a waypoint
//
//////////////////////////////////////////////////////////////////////
class CWayPoint
{
private:
// Our node number
GLint m_iNodeNumber;
// Number of lanes on the road
GLint m_iNumberOfLanes;
// Width of lane
GLint m_iWidthOfLane;
// Width of median
GLfloat m_fMedianWidth;
// TEMP VARIABLE: X or Z axis alignment
// 1 if oriented an the X axis
GLint m_iXOrientation;
// Length of road segment
GLfloat m_fRoadSegmentLength;
// Speed limit
GLuint m_uiSpeedLimit;
// Type of road
GLuint m_uiTypeOfRoad;
// What type of waypoint are we?
GLuint m_uiTypeOfWayPoint;
// Are we an intersection?
bool m_bIntersection;
// If we are an intersection, whats the traffic light thats there?
CTrafficLight *m_pTrafficLight;
// Waypoints we point to
CWayPoint *m_rgConnectedWaypoints[MAX_WAYPOINT_POINTERS];
// Number of waypoints used (connected)
GLint m_iNumWaypointsUsed;
public:
CWayPoint();
virtual ~CWayPoint();
// Use this for setting up a cars during initialization of the
game
GLint m_iCar;
bool m_bCarCanStartHere;
// Our world coordinates
ngVector3D m_worldCoordinates;
Now, these waypoints are placed in the world and connected to each
other according to which one you lead to. I used a waypoint manager
that kept track of the loading, unloading, and connecting the pointers
of each of them to one another. The node number is a unique ID that
each waypoint has and is part of what I used to store them in the
hash table. One good thing about using this method is that upon
saving of the waypoints, you have a database of the waypoints. This
allowed for easy integration with the game since it can be a different
file for each level, thus freeing up memory. It also made it easy
to update each levels waypoints since all you had to do is update
the CVS server with one file and the other members of the group
didn't have to worry about a recompile if it was updated. The former
CarAI would access these waypoints with the accesser functions to
receive the information it needed.
When a car requested its destination from the waypoint, the waypoint
would first see what axis it was on and then take the lane width,
divide it in half, and add that to its position. In our world, I
kept all of the waypoints in a two dimensional grid (X and Z). This
way, if the vehicle was traveling on a road that was running in
the X direction, it would add the lane width divided by two to the
Z axis. I determined which lane to add it to by checking the next
waypoint and doing a simple and fast subtract to determine which
lane (for any road over 1 lane). Look below to see how I laid out
the waypoints on the map. The yellow points are the waypoints and
the red one is the traffic light. The traffic light will be explained
in depth later.

The downside to using this method is that each waypoint had to
be hand placed and connected by hand. This was accomplished in a
world editor that one of the other people in our group, Mark Robbins,
had built. This was time consuming and ended up requiring him to
build an undo into it! :) heh I learned too save often to make sure
that if I did make a mistake connecting two of the wrong waypoints
together I could quickly go back to my last state.
|