All sane suggestions will be apreciated.

Code: Select all
#ifndef _AIStrategy_H_
#define _AIStrategy_H_
#ifndef _GAMEBASE_H_
#include "console/simBase.h"
#endif
#include "console/console.h"
#include "console/consoleTypes.h"
#include "core/bitStream.h"
class SAI : public SimObject
{
typedef SimObject Parent;
public:
F32 mAgression;
F32 mPrecision;
SAI();
~SAI();
bool onAdd();
void onRemove();
DECLARE_CONOBJECT(SAI);
static void initPersistFields();
};
DECLARE_CONSOLETYPE(SAI)
#endif
Code: Select all
#include "AIStrategy.h"
IMPLEMENT_CONOBJECT(SAI);
SAI::SAI()
{
mAgression = 0.1;
mPrecision = 0.1;
}
SAI::~SAI()
{
}
void SAI::initPersistFields()
{
addField("Agression", TypeF32, Offset(mAgression, SAI));
addField("Precision", TypeF32, Offset(mPrecision, SAI));
}
bool SAI::onAdd()
{
if(!Parent::onAdd())
return false;
return true;
}
void SAI::onRemove()
{
Parent::onRemove();
}
Code: Select all
// AIWheeledVehicleBrain.h
// Defines a wheeled vehicle that is driven by AI
#ifndef _AIWheeledVehicleBrain_h
#define _AIWheeledVehicleBrain_h
#ifndef _AIStrategy_H_
#include "game/AI/AIStrategy.h"
#endif
#ifndef _WHEELEDVEHICLE_H_
#include "game/vehicles/wheeledVehicle.h"
#endif
#include "stdio.h"
class PathPoint
{
public:
Point3F m_Point;
float m_desiredSpeed;
};
class AIWheeledVehicleBrain : public WheeledVehicle
{
typedef WheeledVehicle Parent;
public:
enum MoveState {
ModeStop,
ModeMove,
ModeStuck,
ModeReverse,
};
enum DrivingState {
SteerNull,
Left,
Right,
Straight,
TurnAround
};
enum FeelerState {
FrontFeeler,
LeftFeeler,
RightFeeler,
Open,
};
protected:
//vehicle info
Point3F mVehiclePos;
F32 mCurrentSpeed;
MoveState mMoveState;
F32 mMoveSpeed;
F32 mMoveTolerance; // Distance from destination before we stop
Point3F mMoveDestination; // Destination for movement
Point3F mLastLocation; // For stuck check
bool mbBetweenLines;
F32 mTurnAngle;
S32 mCurrentNode;
S32 mLastNode;
bool mbInit;
int mTargetNode;
F32 mMovecount;
Point3F mstartPos;
F32 mDistance;
Point3F mIntersection;
VectorF mRacelinedir;
VectorF mVehicledir;
Vector<PathPoint> mPath;
Vector<F32> mvecStop;
F32 followRacingLine();
int CalcPrevNode(int node);
int CalcNextNode(int node);
F32 mStuckTolerance; //minimal motion before were considered stuck
S32 mMaxStuckCount; //max cycles before stuck turns into totally stuck
S32 mStuckCycles; //cycles we've been stuck
S32 mStuckCount; //times we've exceeded max stuck cycles allowable
F32 mLostDistance; //maximum distance before were considered lost
F32 mPrecision;
bool mstart;
void SetLastNode();
// Utility Methods
void throwCallback( const char *name );
virtual bool getAIMove(Move* move);
public:
AIWheeledVehicleBrain();
void setMoveTolerance( const F32 tolerance );
F32 getMoveTolerance() const { return mMoveTolerance; }
void setMoveDestination( const Point3F &location );
Point3F getMoveDestination() const { return mMoveDestination; }
F32 avoidCollisions(void);
void addPathPoint( const Point3F &location, const float &speed);
void setStartNode(int startnode);
void BuildStopVector();
void InitAI();
static void initPersistFields();
F32 getDistanceToNode(U32 node);
void updateVehicleData();
F32 getMaxTurnAngle(F32 currentSpeed);
F32 getSlowDownDistance(F32 currentspeed,F32 desiredspeed);
F32 TurnToRaceLine(Point3F vehicledir,Point3F racelinedir,F32 maxTurnAngle);
// Steering
DrivingState steerState;
F32 mLastSteered;
F32 getSteeringAngle();
virtual bool onNewSAI(SAI* dptr);
SAI* mSAI;
bool setSAI(SAI* dptr);
SAI* getSAI() { return mSAI; }
F32 mAvoidanceTick;
bool mAvoiding;
DECLARE_CONOBJECT(AIWheeledVehicleBrain);
};
bool DistancePointToLine( Point3F Point, Point3F LineStart, Point3F LineEnd, F32 &distance, Point3F &Intersection );
#endif
Code: Select all
#include "AIWheeledVehicleBrain.h"
#include "math/mMatrix.h"
#include "math/mPoint.h"
#include "core/realComp.h"
//TWEAKABLE constants
#define STOPSAFETY 15 //How we pad the stop distance value to make sure car can stop in time
#define TURNANGLE 0.5
#define LOSTDISTANCE 30 //When vehicle is this far treat as lost
#define MAXLOSTSPEED 20 //Max speed when lost
IMPLEMENT_CO_NETOBJECT_V1(AIWheeledVehicleBrain);
AIWheeledVehicleBrain::AIWheeledVehicleBrain() :
WheeledVehicle()
{
mMoveDestination.set( 0.0f, 0.0f, 0.0f );
mMoveSpeed = 1.0f;
mMoveTolerance = 0.25f;
mbInit = false;
mDistance = 1e20f;
mMovecount = 0;
mCurrentNode = 0;
mStuckCount = 0;
mMaxStuckCount = 50;
mStuckCycles = 0;
mLostDistance = LOSTDISTANCE;
mPrecision = 20;
mStuckTolerance = 0.01f;
mstart = false;
mAvoidanceTick = 2;
mAvoiding = false;
}
void AIWheeledVehicleBrain::initPersistFields()
{
Parent::initPersistFields();
addField("StuckTolerance",TypeF32,Offset(mStuckTolerance,AIWheeledVehicleBrain));
addField("MaxStuckCount",TypeS32,Offset(mMaxStuckCount,AIWheeledVehicleBrain));
addField("LostDistance",TypeF32,Offset(mLostDistance,AIWheeledVehicleBrain));
addField("Precision",TypeF32,Offset(mPrecision,AIWheeledVehicleBrain));
addField("AvoidanceTick",TypeF32,Offset(mAvoidanceTick,AIWheeledVehicleBrain));
}
/**
* Sets how far away from the move location is considered
* "on target"
*
* @param tolerance Movement tolerance for error
*/
void AIWheeledVehicleBrain::setMoveTolerance( const F32 tolerance )
{
mMoveTolerance = getMax( 0.1f, tolerance );
}
void AIWheeledVehicleBrain::addPathPoint( const Point3F &location, const float &speed)
{
PathPoint v;
v.m_Point = location;
v.m_desiredSpeed = speed;
mPath.push_back(v);
}
void AIWheeledVehicleBrain::InitAI()
{
BuildStopVector();
mbInit = true;
return;
}
/* Adjust this function for the performance of your vehicle
mvecStop is a vector that store the stop distance need by the vehicle
at a certain speed
*/
void AIWheeledVehicleBrain::BuildStopVector()
{
int maxSpeed = 250;
F32 factor = 0.05f;
F32 value = 0;
for (int i=0;i<maxSpeed;i++)
{
value = (i*i)*factor;
mvecStop.push_back(value);
}
}
/**
* Sets the location for the bot to run to
*
* @param location Point to run to
*/
void AIWheeledVehicleBrain::setMoveDestination( const Point3F &location)
{
mMoveDestination = location;
mMoveState = ModeMove;
}
//Try to have vehicle follow racing line / Path
F32 AIWheeledVehicleBrain::followRacingLine()
{
Point3F v1 = mIntersection-mVehiclePos;
v1.z = 0;
Point3F v2 = mIntersection-mPath[mCurrentNode].m_Point;
v2.z = 0;
v1.normalize();
v2.normalize();
Point3F lv = mCross(v1,v2);
//Use cross product to figure out if we are left or right of racing line
if (lv.z<0)
{
//vehicle is left of racing line
return TurnToRaceLine(mVehicledir,mRacelinedir,TURNANGLE);
}
else
{
//vehicle is right of racing line
return TurnToRaceLine(mVehicledir,mRacelinedir,-TURNANGLE);
}
return 0;
}
F32 AIWheeledVehicleBrain::TurnToRaceLine(Point3F vehicledir,Point3F racelinedir,F32 maxTurnAngle)
{
Point3F desiredDir;
F32 ratio = mDistance/10;
if (ratio>1)
ratio = 1;
else if (ratio<(1/10))
ratio = 0;
//Square this value to have a smaller turn angle as we get closer to the racing line
ratio =ratio *ratio;
F32 turnAngle = ratio*maxTurnAngle;
F32 targetDistance = getDistanceToNode(mTargetNode);
if (targetDistance<mPrecision)
{
//Just aim right for the node instead of following the racing line
desiredDir = mPath[mTargetNode].m_Point - mVehiclePos;
desiredDir.normalize();
//skip turning
//return 0.0;
}
else
{
//rotate about the z axis so that we will turn twords racing line
desiredDir.x = racelinedir.x*cos(turnAngle)+racelinedir.y*sin(turnAngle);
desiredDir.y = racelinedir.x*-sin(turnAngle)+racelinedir.y*cos(turnAngle);
desiredDir.z = 0;
desiredDir.normalize();
}
F32 dot = mDot(desiredDir,vehicledir);
F32 turn;
//This section of code could use some improvement
//Try to have vehicledir match desiredDir
//Don't turn
if (dot>0.999)
turn = 0;
else if (dot>0.995f)
turn = 0.2f;
else if (dot>0.98f)
turn = 0.4f;
else if (dot>0.97f)
turn = 0.6f;
else if (dot>0.96f)
turn = 0.8f;
else
turn = 1;
F32 cz = desiredDir.x*vehicledir.y - desiredDir.y*vehicledir.x;
if (cz<0)
turn *=-1;
return turn;
}
/**
Calculates the max turn angle based on current speed
that won't flip the vehicle
currently does nothing
*/
F32 AIWheeledVehicleBrain::getMaxTurnAngle(F32 currentSpeed)
{
F32 maxTurnAngle = 50;
return maxTurnAngle;
}
/**
* Calculates the distance needed to slow down to a desired speed
*
*/
F32 AIWheeledVehicleBrain::getSlowDownDistance(F32 currentspeed,F32 desiredspeed)
{
if (currentspeed<=desiredspeed)
return 0.0;
F32 distance = mvecStop[(int)currentspeed] - mvecStop[(int)desiredspeed];
return distance;
}
void AIWheeledVehicleBrain::setStartNode(int startnode)
{
mCurrentNode = startnode;
SetLastNode();
setMoveDestination(mPath[mCurrentNode].m_Point);
}
int AIWheeledVehicleBrain::CalcNextNode(int node)
{
int nextnode = node+1;
if (nextnode>mPath.size()-1)
nextnode = 0;
return nextnode;
}
int AIWheeledVehicleBrain::CalcPrevNode(int node)
{
int prevnode = node-1;
if (prevnode<0)
prevnode = mPath.size()-1;
return prevnode;
}
void AIWheeledVehicleBrain::SetLastNode()
{
mLastNode = CalcPrevNode(mCurrentNode);
}
F32 AIWheeledVehicleBrain::avoidCollisions()
{
disableCollision();
F32 thetaScale = 200.0f;
F32 safeDistance = (getVelocity() * thetaScale).magnitudeSafe();
F32 steering = 0.0;
Point3F start = getPosition();
Point3F feeler,temp;
F32 velocity = getVelocity().len() * thetaScale;
Point3F ForwardRot,LeftRot,RightRot;
getTransform().getColumn(0,&LeftRot);
getTransform().getColumn(1,&ForwardRot);
RightRot = -LeftRot;
Point3F frontFeeler = (ForwardRot * velocity) + start;
frontFeeler.z = start.z;
Point3F leftFeeler = (LeftRot * velocity) + start;
leftFeeler.z = start.z;
Point3F rightFeeler = (RightRot * velocity) + start;
rightFeeler.z = start.z;
// Find closest intersection with wall (static shape) line if any
bool intersectionFound = false;
F32 closestDis;
RayInfo closestRay;
FeelerState closestFeelerState = Open;
U32 avoidanceMask = STATIC_COLLISION_MASK|DAMAGEABLE_MASK;
RayInfo rayInfo_1;
closestRay.distance = rayInfo_1.distance = (frontFeeler-start).len();
if (getContainer()->castRay( start, frontFeeler, avoidanceMask , &rayInfo_1 ))
{
if (rayInfo_1.distance < closestRay.distance)
{
closestRay = rayInfo_1;
intersectionFound = true;
closestFeelerState = FrontFeeler;
}
}
RayInfo rayInfo_2;
rayInfo_2.distance = (leftFeeler-start).len();
if (getContainer()->castRay( start, leftFeeler, avoidanceMask , &rayInfo_2 ))
{
intersectionFound = true;
if (rayInfo_2.distance < closestRay.distance)
{
closestRay = rayInfo_2;
closestFeelerState = LeftFeeler;
}
}
RayInfo rayInfo_3;
rayInfo_3.distance = (rightFeeler-start).len();
if (getContainer()->castRay( start, rightFeeler, avoidanceMask , &rayInfo_3 ))
{
intersectionFound = true;
if (rayInfo_3.distance < closestRay.distance)
{
closestRay = rayInfo_3;
closestFeelerState = RightFeeler;
}
}
if (!intersectionFound)
{
mMoveSpeed = 1.0;
steering = 0.0;
}
else
{
switch (closestFeelerState){
case FrontFeeler:
mMoveSpeed -= 0.05f;
if (mMoveSpeed<0.01f) mMoveSpeed = 0.01f;
case LeftFeeler:
if (closestRay.distance >0)
{
steering = mDataBlock->maxSteeringAngle / (closestRay.distance / safeDistance);
mMoveSpeed -= 0.01f;
if (mMoveSpeed<0.01) mMoveSpeed = 0.01f;
break;
}
case RightFeeler:
if (closestRay.distance >0)
{
steering = -mDataBlock->maxSteeringAngle / (closestRay.distance / safeDistance);
mMoveSpeed -= 0.01f;
if (mMoveSpeed<0.01f) mMoveSpeed = 0.01f;
break;
}
}
}
enableCollision();
return steering;
}
void AIWheeledVehicleBrain::updateVehicleData()
{
//Update vehicle data
mVehiclePos = getPosition();
mCurrentSpeed = fabs(getVelocity().len());
MatrixF mat = getTransform();
VectorF vehicledir;
vehicledir.set(0,1,0);
mat.mulV(vehicledir);
//ignore the z part because we can't fix that
vehicledir.z = 0;
vehicledir.normalize();
mVehicledir = vehicledir;
Point3F pt1 = mPath[mCurrentNode].m_Point;
Point3F pt2 = mPath[mLastNode].m_Point;
mbBetweenLines = false;
//Figure distance and intersection to racing line
mTargetNode = mCurrentNode;
bool b = true;
if (!DistancePointToLine(mVehiclePos,pt2,pt1,mDistance,mIntersection))
{
//Try the next node
mTargetNode = CalcNextNode(mCurrentNode);
pt1 = mPath[mTargetNode].m_Point;
pt2 = mPath[mCurrentNode].m_Point;
if (DistancePointToLine(mVehiclePos,pt2,pt1,mDistance,mIntersection))
{
mLastNode = mCurrentNode;
mCurrentNode = mTargetNode;
mbBetweenLines = true;
}
else
{
//In between line segments
Point3F inter = mPath[mTargetNode].m_Point;
mDistance = getDistanceToNode(mTargetNode);
mbBetweenLines = true;
}
}
F32 distance = getDistanceToNode(mTargetNode);
//pretty basic right now. need to add better conditions to being 'stuck'
if (mCurrentSpeed < mStuckTolerance)
{
mStuckCount++;
if (mStuckCount>mMaxStuckCount)
{
mStuckCycles++;
if (mStuckCycles>mMaxStuckCount)
{
mStuckCount = 0;
mStuckCycles = 0;
Con::executef(this, 1, "onStuck");
}
}
else mMoveState = ModeMove;
if (distance>mLostDistance)
{
mStuckCycles = 0;
mStuckCount = 0;
Con::executef(this, 1, "onLost");
}
}
else mStuckCycles = mStuckCount = 0;
if (distance<mPrecision)
{
//Use the next node
mTargetNode = CalcNextNode(mCurrentNode);
pt1 = mPath[mTargetNode].m_Point;
pt2 = mPath[mCurrentNode].m_Point;
}
VectorF racelinedir;
racelinedir = pt1-pt2;
racelinedir.normalize();
mRacelinedir = racelinedir;
}
// Think - figure out speed(accelerate or apply brakes) and steer the vehicle
bool AIWheeledVehicleBrain::getAIMove(Move *movePtr)
{
if (!mbInit)
return false;
if (mDisableMove)
{
mRigid.setAtRest();
return true;
}
*movePtr = NullMove;
updateVehicleData();
Point3F pt1 = mPath[mCurrentNode].m_Point;
Point3F pt2 = mPath[mLastNode].m_Point;
// Orient towards our destination.
mMovecount++;
if (mMovecount > mAvoidanceTick)
{
mMovecount = 1;
movePtr->yaw = avoidCollisions();
movePtr->y = mMoveSpeed;
}
else
{
if (mMoveState == ModeMove || mMoveState == ModeReverse)
{
mTurnAngle = followRacingLine();
movePtr->yaw = mTurnAngle;
}
// Move towards the destination
if (mMoveState == ModeMove)
{
movePtr->y = mMoveSpeed;
setMoveDestination(mPath[mCurrentNode].m_Point);
//Do we need to slow down or speed up?
F32 targetSpeed = mPath[mCurrentNode].m_desiredSpeed;
if (mCurrentSpeed>targetSpeed * mSAI->mAgression)
{
F32 distance = getDistanceToNode(mCurrentNode);
F32 slowdistance = getSlowDownDistance(mCurrentSpeed,mPath[mCurrentNode].m_desiredSpeed);
if (distance<(slowdistance+STOPSAFETY))
{
movePtr->y = 0.0;
movePtr->trigger[2] = true;
}
else
if ((mPath[mCurrentNode].m_desiredSpeed>mCurrentSpeed)&&(mPath[mLastNode].m_desiredSpeed>mCurrentSpeed))
{
movePtr->y = 0.0;
movePtr->trigger[2] = true;
}
else
{
if ((mPath[mCurrentNode].m_desiredSpeed<999)&&(mPath[mLastNode].m_desiredSpeed<999))
{
movePtr->y = mMoveSpeed;
}
else
{
movePtr->y = mMoveSpeed;
}
}
}
else
{
movePtr->y = mMoveSpeed;
}
}
else if(mMoveState == ModeReverse)
{
movePtr->y = -1 * mMoveSpeed;
}
else if(mMoveState == ModeStop)
{
movePtr->y = 0;
}
}
//Don't apply gas if vehicle is badly sliding
Point3F vel = getVelocity();
vel.z = 0;
vel.normalize();
F32 d = mDot(vel,mVehicledir);
bool bSlide = false;
if (mCurrentSpeed>400)
{
if (d<0.95)
{
movePtr->y = 0;
movePtr->trigger[2] = true;
}
}
// Replicate the trigger state into the move so that
// triggers can be controlled from scripts.
for( int i = 0; i < MaxTriggerKeys; i++ )
movePtr->trigger[i] = getImageTriggerState(i);
return true;
}
F32 AIWheeledVehicleBrain::getDistanceToNode(U32 node)
{
Point3F v = mVehiclePos-mPath[node].m_Point;
return v.len();
}
/**
* Utility function to throw callbacks. Callbacks always occure
* on the datablock class.
*
* @param name Name of script function to call
*/
void AIWheeledVehicleBrain::throwCallback( const char *name )
{
Con::executef(getDataBlock(), 2, name, scriptThis());
}
//Perpendicular distance from point to line
//returns false if the point is not perpendicular to line
//Ignores the z value
bool DistancePointToLine( Point3F Point, Point3F LineStart, Point3F LineEnd, F32 &distance, Point3F &Intersection )
{
F32 LineMag;
F32 U;
//Ignore z value
Intersection.set(0,0,0);
Point.z = 0;
LineEnd.z = 0;
LineStart.z = 0;
Point3F l = LineEnd-LineStart;
LineMag = l.len();
if (LineMag!=0.0f)
{
U = ( ( ( Point.x - LineStart.x ) * ( LineEnd.x - LineStart.x ) ) +
( ( Point.y - LineStart.y ) * ( LineEnd.y - LineStart.y ) ))/
( LineMag * LineMag );
}
if( U < 0.0f || U > 1.0f )
return false; // closest point does not fall within the line segment
Intersection.x = LineStart.x + U * ( LineEnd.x - LineStart.x );
Intersection.y = LineStart.y + U * ( LineEnd.y - LineStart.y );
Point3F l2 = Point - Intersection;
distance = l2.len();
return true;
}
// --------------------------------------------------------------------------------------------
// Console Functions
// --------------------------------------------------------------------------------------------
ConsoleMethod( AIWheeledVehicleBrain, init, void, 2, 2, "()"
"Initialize AI for vehicle")
{
object->InitAI();
}
ConsoleMethod( AIWheeledVehicleBrain, setStartNode, void, 3, 3, "( int startnode )"
"Sets the move speed for an AI object.")
{
object->setStartNode( dAtoi( argv[2] ) );
}
ConsoleMethod( AIWheeledVehicleBrain, setMoveTolerance, void, 3, 3, "(float speed)" "Sets the movetolerance")
{
object->setMoveTolerance(dAtof(argv[2]));
}
ConsoleMethod( AIWheeledVehicleBrain, addPathPoint, void, 4, 4, "(Point3F goal)(float speed)"
"Add a point to the vehicle travel path")
{
float speed = 0;
Point3F v( 0.0f, 0.0f, 0.0f );
dSscanf( argv[2], "%f %f %f", &v.x, &v.y, &v.z );
dSscanf( argv[3], "%f", &speed );
object->addPathPoint( v, speed );
}
ConsoleMethod( AIWheeledVehicleBrain, getMoveDestination, const char *, 2, 2, "()"
"Returns the point the AI is set to move to.")
{
Point3F movePoint = object->getMoveDestination();
char *returnBuffer = Con::getReturnBuffer( 256 );
dSprintf( returnBuffer, 256, "%f %f %f", movePoint.x, movePoint.y, movePoint.z );
return returnBuffer;
}
//==============================================================================================
bool AIWheeledVehicleBrain::setSAI(SAI* dptr)
{
if (isGhost() || isProperlyAdded()) {
if (mSAI != dptr)
return onNewSAI(dptr);
}
else
mSAI = dptr;
return true;
}
bool AIWheeledVehicleBrain::onNewSAI(SAI* dptr)
{
mSAI = dptr;
if (!mSAI)
return false;
setMaskBits(DataBlockMask);
return true;
}
//----------------------------------------------------------------------------
ConsoleMethod( AIWheeledVehicleBrain, getSAI, S32, 2, 2, "()"
"Return the SAI this AIWheeledVehicleBrain is using.")
{
return object->getSAI()? object->getSAI()->getId(): 0;
}
//----------------------------------------------------------------------------
ConsoleMethod(AIWheeledVehicleBrain, setSAI, bool, 3, 3, "(SAI db)"
"Assign this AIWheeledVehicleBrain to use the specified SAI.")
{
SAI* data;
if (Sim::findObject(argv[2],data)) {
return object->setSAI(data);
}
Con::errorf("Could not find SAI Template \"%s\"!",argv[2]);
return false;
}