AI for your tank - AITankShape
by Jerry Shaw · in Torque Game Engine · 01/20/2007 (3:36 pm) · 21 replies
I'll post the code here and submit a resource.
About the author
Recent Threads
#2
01/20/2007 (3:41 pm)
AITankShape.cc (1/3)//-----------------------------------------------------------------------------
// AI Tank Shape for use with Bravetree's Tank Pack
// Written by Jerry Shaw
// Based off of AIWheeledVehicle and AITurret
//-----------------------------------------------------------------------------
#include "AITankShape.h"
#include "math/mMatrix.h"
#include "math/mPoint.h"
#include "math/mathUtils.h"
#include "core/realComp.h"
IMPLEMENT_CO_NETOBJECT_V1(AITankShape);
// NOTE: js - being lazy...created a new extact euler, remove later or add this to a utilities class
EulerF myExtractEuler(const MatrixF & matrix)
{
const F32 * mat = (const F32*)matrix;
EulerF r;
r.x = mAsin(mat[MatrixF::idx(2,1)]);
if(mCos(r.x) != 0.f)
{
r.y = mAtan(-mat[MatrixF::idx(2,0)], mat[MatrixF::idx(2,2)]);
r.z = mAtan(-mat[MatrixF::idx(0,1)], mat[MatrixF::idx(1,1)]);
}
else
{
r.y = 0.f;
r.z = mAtan(mat[MatrixF::idx(1,0)], mat[MatrixF::idx(0,0)]);
}
return(r);
}
AITankShape::AITankShape() :
TankShape()
{
mMoveDestination.set( 0.0f, 0.0f, 0.0f );
mMoveSpeed = 1.0f;
mMoveTolerance = 0.25f;
mMoveSlowdown = true;
mSteering.set(0.0f, 0.0f);
mAimObject = 0;
mAimLocationSet = false;
mTargetInLOS = false;
}
AITankShape::~AITankShape()
{
}
/**
* Sets the speed at which this AI moves
*
* @param speed - Set speed
*/
void AITankShape::setMoveSpeed( F32 speed )
{
mMoveSpeed = getMax(0.0f, getMin( 1.0f, speed ));
}
// Stop movement
void AITankShape::stopMove()
{
mMoveState = ModeStop;
}
// Forward
void AITankShape::forwardMove()
{
mMoveState = ModeMove;
}
// Reverse
void AITankShape::reverseMove()
{
mMoveState = ModeReverse;
}
// Turn left
void AITankShape::leftTurn()
{
mMoveState = ModeTurn;
mIsLeftTurn = true;
}
// Turn right
void AITankShape::rightTurn()
{
mMoveState = ModeTurn;
mIsLeftTurn = false;
}
/**
* Sets how far away from the move location is considered
* "on target"
*
* @param tolerance Movement tolerance for error
*/
void AITankShape::setMoveTolerance( const F32 tolerance )
{
mMoveTolerance = getMax( 0.1f, tolerance );
}
/**
* Sets the location for the tank to move to
*
* @param location Point to move to
*/
void AITankShape::setMoveDestination( const Point3F &location, bool slowdown )
{
mMoveDestination = location;
mMoveState = ModeMove;
mMoveSlowdown = slowdown;
}
// Build a Triangle .. calculate angle of rotation required to meet target..
// man there has to be a better way! >:)
// this little block builds the triangle using the destination point
// the front of the vehicle and the left side of the vehicle.
// ftoc == front to center
// ltoc == left to center
// ftol == front to left
// NOTE: js - going into reverse now works, added to original code
F32 AITankShape::getSteeringAngle()
{
// What is our target
Point3F desired;
desired=mMoveDestination;
MatrixF mat = getTransform();
Point3F center, front;
Point3F wFront;
Box3F box = getObjBox();
box.getCenter(¢er);
front=center;
front.y = box.max.y; // should be true for all these objects
getWorldBox().getCenter(¢er);
front=center + front;
Point3F objFront=front;
Point3F offset = front - center;
EulerF rot;
rot=myExtractEuler(mat);
MatrixF transform(rot);
transform.mulV(offset, &wFront);
front = wFront + center;
Point3F ftoc;
ftoc.x=mFabs(front.x-center.x);
ftoc.y=mFabs(front.y-center.y);
ftoc.z=mFabs(front.z-center.z);
F32 fToc=mSqrt((ftoc.x*ftoc.x)+(ftoc.y*ftoc.y));
Point3F ltoc;
ltoc.x=mFabs(desired.x-center.x);
ltoc.y=mFabs(desired.y-center.y);
ltoc.z=mFabs(desired.z-center.z);
F32 lToc=mSqrt((ltoc.x*ltoc.x)+(ltoc.y*ltoc.y));
Point3F ftol;
ftol.x=mFabs(front.x-desired.x);
ftol.y=mFabs(front.y-desired.y);
ftol.z=mFabs(front.z-desired.z);
F32 fTol=mSqrt((ftol.x*ftol.x)+(ftol.y*ftol.y));
F32 myAngle = mAcos(((lToc*lToc) + (fToc * fToc) - (fTol*fTol))/(2*lToc*fToc));
Point3F location = getPosition();
F32 xDiff = desired.x - location.x;
F32 yDiff = desired.y - location.y;
F32 finalYaw=mRadToDeg(myAngle);
// NOTE: js - need to tweak maxSteeringAngle and turnRate
F32 maxSteeringAngle=0;
TankShapeData *tsd= (TankShapeData*) getDataBlock();
maxSteeringAngle= mDegToRad(tsd->turnRate * 1.25);
finalYaw = (mMoveState == ModeReverse) ? 180-finalYaw : 0+finalYaw;
if(finalYaw > 150)
mSteerState = TurnAround;
else if(finalYaw < 5 && mLastSteered != 0)
mSteerState = Straight;
else if(finalYaw < 5)
mSteerState = SteerNull;
else
{// Quickly Hack out left or right turn info
Point3F rotData=objFront-desired;
MatrixF leftM(-rot);
Point3F leftP;
leftM.mulV(rotData, &leftP);
leftP = leftP + desired;
if(leftP.x<desired.x)
mSteerState=(mMoveState == ModeMove) ? Right : Left;
else
mSteerState=(mMoveState == ModeMove) ? Left : Right;
}
Point2F steering = mSteering;
F32 steer=0;
switch(mSteerState)
{
case SteerNull:
//Con::printf("AI Steering : null");
break;
case Left:
steer=myAngle < maxSteeringAngle ? -myAngle-steering.x: -maxSteeringAngle-steering.x;
mLastSteered=steer;
//Con::printf("AI Steering : left");
break;
case Right:
steer=myAngle < maxSteeringAngle ? myAngle-steering.x: maxSteeringAngle-steering.x;
mLastSteered=steer;
//Con::printf("AI Steering : right");
break;
case Straight:
steer=-steering.x;
mLastSteered=0;
//Con::printf("AI Steering : straight");
break;
case TurnAround:
steer=maxSteeringAngle-steering.x;
mLastSteered=steer;
//Con::printf("AI Steering : turnaround");
break;
default:
Con::printf("AI Steering : bogus state");
};
return steer;
}
#3
01/20/2007 (3:42 pm)
AITankShape.cc (2/3)// NOTE: js - rewrite of AIWheeledVehicle::getAIMove
bool AITankShape::getAIMove(Move *movePtr)
{
*movePtr = NullMove;
// y = -1..0..1 tank = reverse..stop.. forward
// x = -1..0..1 turret = left..center..right
// QUESTION: js - test for if disabled?
// getPos gives the tanks position
Point3F location = deltaHelper.getPos();
// Orient tank towards destination if in a moving state
if (mMoveState == ModeMove || mMoveState == ModeReverse) {
movePtr->x = getSteeringAngle();
}
// QUESTION: js - separate test for turning?
else if (mMoveState == ModeTurn) {
movePtr->x = (mIsLeftTurn) ? -1 : 1;
}
// If moving, move towards the destination
// NOTE: js - reverse back
if (mMoveState == ModeMove || mMoveState == ModeReverse) {
F32 xDiff = mMoveDestination.x - location.x;
F32 yDiff = mMoveDestination.y - location.y;
// Check if we should move, or if we are 'close enough'
if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) {
// close enough
mMoveState = ModeStop;
throwCallback("onReachDestination");
movePtr->y = 0;
} else {
// get moving
// NOTE: js - here's where it starts to differ from AIWheeledVehicle
movePtr->y = (mMoveState == ModeMove) ? 1 : -1;
// Set movement speed. We'll slow down once we get close
// to try and stop on the spot...
if (mMoveSlowdown) {
F32 speed = mMoveSpeed;
F32 dist = mSqrt(xDiff*xDiff + yDiff*yDiff);
F32 maxDist = 5;
if (dist < maxDist)
speed *= dist / maxDist;
// ease up on the throttle
movePtr->y *= speed;
} else {
// keep moving
movePtr->y *= mMoveSpeed;
}
// NOTE: js - need to fix stuck test
// Check to see if tank is stuck
if (location == mLastLocation) {
throwCallback("onMoveStuck");
mMoveState = ModeMove;
Con::printf("AI Steering : stuck");
}
// keep track of last location
mLastLocation = location;
}
}
// Aim the turret towards the aim point (object or location)
// NOTE: js - based off of AITurret
// NOTE: js - might not want ModeMove here, was in AIPlayer
if (mAimObject || mAimLocationSet) { // || mMoveState == ModeMove) {
Point3F targetVelocity = Point3F(0.0f, 0.0f, 0.0f);
// if aiming at an object, get it's location
if (mAimObject) {
mAimLocation = mAimObject->getPosition() + mAimOffset; // was getBoxCenter()
targetVelocity = mAimObject->getVelocity();
}
// get current horizontal and verticle positions of the turret
F32 tHor = deltaHelper.getTurretHorTarget();
F32 tVer = deltaHelper.getTurretVerTarget();
// rotation values are always in local space
F32 curYaw = tHor * mDegToRad(getDataBlock()->turretHorRange);
F32 curPitch = tVer * mDegToRad(getDataBlock()->turretVerRange) + mDegToRad(getDataBlock()->turretVerCenter);
// get new local space pitch and yaw that point at target
F32 newYaw, newPitch;
getAnglesFromAimLocation(mAimLocation,targetVelocity,newYaw,newPitch);
F32 yawDiff = newYaw - curYaw;
// make it between 0 and 2PI
// if( yawDiff < 0.0f )
// yawDiff += M_2PI;
// else if( yawDiff >= M_2PI )
// yawDiff -= M_2PI;
//
// now make sure we take the short way around the circle
if( yawDiff > M_PI )
yawDiff -= M_2PI;
else if( yawDiff < -M_PI )
yawDiff += M_2PI;
// create a move that is the difference
movePtr->yaw = yawDiff;
movePtr->pitch = newPitch - curPitch;
} // if (mAimObject ..
else {
// move the turret back to the starting position
F32 tHor = deltaHelper.getTurretHorTarget();
F32 tVer = deltaHelper.getTurretVerTarget();
F32 curYaw = tHor * mDegToRad(getDataBlock()->turretHorRange);
F32 curPitch = tVer * mDegToRad(getDataBlock()->turretVerRange) + mDegToRad(getDataBlock()->turretVerCenter);
movePtr->yaw = -curYaw;
movePtr->pitch = -curPitch;
}
// Test for target location in sight if it's an object. The LOS is
// run from the eye position to the center of the object's bounding,
// which is not very accurate.
if (mAimObject) {
if (isInLOS(mAimObject)) {
if (!mTargetInLOS) {
Con::printf("AITank : target in LOS");
throwCallback( "onTargetEnterLOS" );
mTargetInLOS = true;
}
} else {
if (mTargetInLOS) {
Con::printf("AITank : target NOT in LOS");
throwCallback( "onTargetExitLOS" );
mTargetInLOS = false;
}
}
}
// 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;
}
/**
* NOTE: js - taken from AITurret, major hackage commences
* This method calculates the pitch and yaw values that would
* cause the turret to point at the given location. This method
* will take into account the turrets current orientation and does
* not assume that the turrets local Z axis points up.
*
* @param loc the world location we want to "point" to
* @param newYaw receives the calculated yaw
* @param newPitch receives the calculated pitch
*/
void AITankShape::getAnglesFromAimLocation(VectorF &loc, VectorF &velocity, F32 &newYaw, F32 &newPitch)
{
// NOTE: this code assumes a weapon is mounted in slot 0!!!!
// get muzzle point location and target location in
// the turret's local space
MatrixF invTrans = getWorldTransform();
Point3F location;
getMuzzlePoint(0,&location);
invTrans.mulP(location);
// transform target location to local space of turret
Point3F aimLocation;
invTrans.mulP(loc,&aimLocation);
// dir to target
Point3F targetDir = aimLocation - location;
// calc output values
F32 yawAng,pitchAng;
MathUtils::getAnglesFromVector(targetDir,yawAng,pitchAng);
newYaw = yawAng;
newPitch = -pitchAng;
}
/**
* This method returns true if the given object is in LOS.
*
* @param obj the object to test
*/
bool AITankShape::isInLOS(GameBase *obj, S32 weaponSlot)
{
// NOTE: this code assumes a weapon is mounted in slot 0!!!!
// NOTE: js - need to fix this
// Test for object in sight. The LOS is run from the muzzle point
// to the center of the target bounding box.
Point3F location;
getMuzzlePoint(weaponSlot,&location);
Point3F targetLoc = obj->getBoxCenter();
// This ray ignores non-static shapes. Cast Ray returns true
// if it hit something.
RayInfo dummy;
if (getContainer()->castRay( location, targetLoc,
InteriorObjectType | StaticShapeObjectType | StaticObjectType |
TerrainObjectType, &dummy))
{
return false;
}
return true;
}
#4
01/20/2007 (3:43 pm)
AITankShape.cc (3/3)/**
* Utility function to throw callbacks. Callbacks always occure
* on the datablock class.
*
* @param name Name of script function to call
*/
void AITankShape::throwCallback( const char *name )
{
Con::executef(getDataBlock(), 2, name, scriptThis());
}
/**
* Sets the target object
*
* @param targetObject The object to target
*/
void AITankShape::setAimObject( GameBase *targetObject )
{
mAimObject = targetObject;
mTargetInLOS = false;
mAimOffset = Point3F(0.0f, 0.0f, 0.0f);
}
/**
* Sets the target object and an offset to add to target location
*
* @param targetObject The object to target
* @param offset The offest from the target location to aim at
*/
void AITankShape::setAimObject( GameBase *targetObject, Point3F offset )
{
mAimObject = targetObject;
mTargetInLOS = false;
mAimOffset = offset;
}
/**
* Sets the location for the tank to aim at
*
* @param location Point to aim at
*/
void AITankShape::setAimLocation( const Point3F &location )
{
mAimObject = 0;
mAimLocationSet = true;
mAimLocation = location;
mAimOffset = Point3F(0.0f, 0.0f, 0.0f);
}
/**
* Clears the aim location and sets it to the bot's
* current destination so he looks where he's going
*/
void AITankShape::clearAim()
{
mAimObject = 0;
mAimLocationSet = false;
mAimOffset = Point3F(0.0f, 0.0f, 0.0f);
}
// --------------------------------------------------------------------------------------------
// Console Functions
// --------------------------------------------------------------------------------------------
ConsoleMethod( AITankShape, stop, void, 2, 2, "()"
"Stop moving.")
{
object->stopMove();
}
ConsoleMethod( AITankShape, forward, void, 2, 2, "()"
"Forward direction.")
{
object->forwardMove();
}
ConsoleMethod( AITankShape, reverse, void, 2, 2, "()"
"Reverse direction.")
{
object->reverseMove();
}
ConsoleMethod( AITankShape, left, void, 2, 2, "()"
"Left turn.")
{
object->leftTurn();
}
ConsoleMethod( AITankShape, right, void, 2, 2, "()"
"Right turn.")
{
object->rightTurn();
}
ConsoleMethod( AITankShape, setMoveSpeed, void, 3, 3, "( float speed )"
"Sets the move speed for an AI object.")
{
object->setMoveSpeed( dAtof( argv[2] ) );
}
ConsoleMethod( AITankShape, setMoveTolerance, void, 3, 3, "(float speed)" "Sets the movetolerance")
{
object->setMoveTolerance(dAtof(argv[2]));
}
ConsoleMethod( AITankShape, setMoveDestination, void, 3, 4, "(Point3F goal, bool slowDown=true)"
"Tells the AI to move to the location provided.")
{
Point3F v( 0.0f, 0.0f, 0.0f );
dSscanf( argv[2], "%f %f %f", &v.x, &v.y, &v.z );
bool slowdown = (argc > 3)? dAtob(argv[3]): true;
object->setMoveDestination( v, slowdown);
}
ConsoleMethod( AITankShape, 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;
}
ConsoleMethod( AITankShape, clearAim, void, 2, 2, "()"
"Stop aiming at anything.")
{
object->clearAim();
}
ConsoleMethod( AITankShape, setAimLocation, void, 3, 3, "( Point3F target )"
"Tells the AI to aim at the location provided.")
{
Point3F v( 0.0f,0.0f,0.0f );
dSscanf( argv[2], "%f %f %f", &v.x, &v.y, &v.z );
object->setAimLocation( v );
}
ConsoleMethod( AITankShape, getAimLocation, const char *, 2, 2, "()"
"Returns the point the AI is aiming at.")
{
Point3F aimPoint = object->getAimLocation();
char *returnBuffer = Con::getReturnBuffer( 256 );
dSprintf( returnBuffer, 256, "%f %f %f", aimPoint.x, aimPoint.y, aimPoint.z );
return returnBuffer;
}
ConsoleMethod( AITankShape, setAimObject, void, 3, 3, "( GameBase obj )"
"Sets the bot's target object.")
{
// Find the target
GameBase *targetObject;
if( Sim::findObject( argv[2], targetObject ) )
object->setAimObject( targetObject );
else
object->setAimObject( 0 );
}
ConsoleMethod( AITankShape, getAimObject, S32, 2, 2, "()"
"Gets the object the AI is targeting.")
{
GameBase* obj = object->getAimObject();
return obj? obj->getId(): -1;
}
#5
01/20/2007 (3:52 pm)
That should get most of you going. I'll continue down the path of creating a resource and a patch to add in all of this plus some other important edits.
#6
01/20/2007 (4:11 pm)
Jerry you rock!
#7
How do I get this into a game? Guess I'll check out the AIwheeledvehicle resource.
01/20/2007 (4:47 pm)
Ok, i got it to compile but i had to make deltahelper public in tankshape.h. How do I get this into a game? Guess I'll check out the AIwheeledvehicle resource.
#8
01/20/2007 (5:04 pm)
My aiTank.cs:function CreateAITankShape(%name, %botPath)
{
// Create the AI driven tank
%player = new AITankShape()
{
dataBlock = ShermanTank;
path = %botPath;
};
MissionCleanup.add( %player );
%player.setShapeName( %name );
%node = %botPath.getObject(0);
%player.setTransform( %node.getTransform());
%player.currentNode = 0; // current node
%player.setMoveSpeed(1.5);
%player.setMoveTolerance(10.0);
%player.score = 0;
return %player;
}
// create a marker at the next waypoint, used for debug
function createMarker(%pos) {
%shape= new TSStatic() {
shapeName = "~/data/shapes/markers/octahedron.dts";
scale = "4 4 4";
};
MissionCleanup.add(%shape);
%shape.setTransform(%pos);
return %shape;
}
function AITankShape::followPath( %this, %path, %node )
{
// Start the bot following a path
%this.stopThread(0);
// Make sure our path is valid
if( !isObject( %path ) )
{
echo( "AITankShape::followPath failed - Bad Path!" );
%this.path = "";
return;
}
// How far do we want to travel on the path?
if(( %node > %path.getCount() - 1 ) || (%node == -1))
%this.targetNode = %path.getCount() - 1;
else
%this.targetNode = %node;
if( %this.path $= %path ) {
%this.moveToNode(%this.currentNode + 1);
} else {
%this.path = %path;
%this.moveToNode(0);
%this.currentNode = 0;
}
}
function AITankShape::moveToNextNode( %this )
{
if( %this.targetNode < 0 || %this.currentNode < %this.targetNode )
{
if( %this.currentNode < %this.path.getCount() - 1 )
%this.moveToNode( %this.currentNode + 1 );
else
%this.moveToNode( 0 );
}
else
{
if( %this.currentNode == 0 )
%this.moveToNode( %this.path.getCount() - 1 );
else
%this.moveToNode( %this.currentNode - 1 );
}
}
function AITankShape::moveToNode( %this, %index )
{
// Move to the given path node index
%this.currentNode = %index;
%node = %this.path.getObject(%index);
%this.setMoveDestination( %node.getTransform(), %index == %this.targetNode );
error("TankBot - moving to node " SPC %index SPC " pos: " SPC %node.getPosition());
error("TankBot node ID " SPC %node);
$Marker = createMarker(%node.getPosition());
}
// Default callbacks
function DefaultTank::onReachDestination( %this, %obj )
{
$Marker.delete();
error("AITank - reached destination");
// Moves to the next node on the path.
if( %obj.path !$= "" )
{
if( %obj.currentNode == %obj.targetNode )
%this.onEndOfPath( %obj, %obj.path );
else
%obj.moveToNextNode();
}
else
error( "AITank::onReachDestination warning - Path is blank!" );
%this.path.getObject(%index);
}
function DefaultTank::onEndOfPath( %this, %obj, %path )
{
echo("AITank" SPC %obj SPC "done with path");
%obj.moveToNode(0);
//%obj.nextTask();
}
function DefaultTank::onMoveStuck(%this, %obj)
{
echo( "AITank" SPC %obj SPC "stuck");
}
function DefaultTank::onTargetEnterLOS(%this,%tank)
{
error("AITank" SPC %tank SPC "target in LOS");
%tank.fire();
}
function DefaultTank::onTargetExitLOS(%this,%tank)
{
error("AITank" SPC %tank SPC "target NOT in LOS");
%tank.ceasefire();
}
function AITankShape::fire(%this, %slot)
{
if (%slot == "")
%slot = 0;
echo( "AITank " SPC %this SPC " fire weapon " SPC %slot);
%this.setImageTrigger(%slot, 1);
}
function AITankShape::ceasefire(%this, %slot)
{
if (%slot == "")
%slot = 0;
echo( "AITank " SPC %this SPC " cease fire weapon " SPC %slot);
%this.setImageTrigger(%slot, 0);
}
#9
01/21/2007 (1:33 am)
Great, dude, great job Jerry! :-)
#10
I packaged up my aiTankShape.h/cpp, the corresponding aiTank.cs script plus the mission file and the yellow testbox that was used to test this (TGE 1.5). Get the file here. Usually after starting simply open the console and type in "ai" + return and the tank spawns and starts moving randomly between the four yellow boxes around. Here's a screenshot of the ai Tank in action:
(see this in motion)

The interesting part of the aiTankShape class:
Somewhere in the bold marked area we need to find the right angles to aim the turret at it's target. I already tried to incorporate Paul Dana's turret aiming, but failed to do it right. If someone is interested... jump in! :-)
Martin
01/23/2007 (1:57 am)
I tried Jerry's version of the aiTankShape, but it doesn't work for me. The tank simply doesn't move at all and I was too lazy to debug it. So I tried my (more or less) own version and it works nice so far, but I have one drawback - I'm bad at math and I don't get the turret to point at the target location. Is somebody interested to jump in and complete the class?I packaged up my aiTankShape.h/cpp, the corresponding aiTank.cs script plus the mission file and the yellow testbox that was used to test this (TGE 1.5). Get the file here. Usually after starting simply open the console and type in "ai" + return and the tank spawns and starts moving randomly between the four yellow boxes around. Here's a screenshot of the ai Tank in action:
(see this in motion)

The interesting part of the aiTankShape class:
bool AITankShape::getAIMove(Move *movePtr)
{
*movePtr = NullMove;
// Move towards the destination and orient the tank towards it
if (mMoveState == ModeMove)
{
// get the tank position
Point3F location = getPosition();
//Point3F location = deltaHelper.getPos();
// Distance to move destination
F32 xDiffPos = mMoveDestination.x - location.x;
F32 yDiffPos = mMoveDestination.y - location.y;
if (mFabs(xDiffPos) < mMoveTolerance && mFabs(yDiffPos) < mMoveTolerance)
{
mMoveState = ModeStop;
movePtr->x = 0; // no turning
movePtr->y = 0; // no forward/backward
throwCallback("onReachDestination");
//Con::printf("AI Tank: Reached Destination!");
}
else
{
//Con::printf("AI Tank: Move Mode");
// left/right turning
F32 turnDirection = getSteeringAngle();
if (turnDirection > 0)
{
movePtr->x = 1.0f; // turn right
if (turnDirection > 0.4)
movePtr->x = 3.0f; // turn right fast
}
else if (turnDirection < 0)
{
movePtr->x = -1.0f; // turn left
if (turnDirection < -0.4)
movePtr->x = -3.0f; // turn left fast
}
else
{
movePtr->x = 0; // no turning
}
// Movement. But only, if we don't need to turn to much. If so, then turn
// first and then move (to get sharp corners moveable too).
if (turnDirection > -0.4f && turnDirection < 0.4f)
{
movePtr->y = 1.0f;
//Con::printf("Moving Fast!");
}
else
{
movePtr->y = 0;
//Con::printf("Moving Slow!");
}
// This does not work currently!
// Check to see if tank is stuck
//if (location == mLastLocation) {
// throwCallback("onMoveStuck");
// //mMoveState = ModeMove;
// mMoveState = ModeStop;
// Con::printf("AI Steering : stuck");
//}
// keep track of last location
mLastLocation = location;
}
}
else if (mMoveState == ModeStop)
{
movePtr->x = 0; // No turning
movePtr->y = 0; // Stop moving
movePtr->z = 0; // No rolling at all :)
//Con::errorf("Move Mode Stop!");
}
[b]
if (mAimObject || mAimLocationSet)
{
if (mAimObject)
mAimLocation = mAimObject->getPosition(); // Update the aim objects position if set
//// our own rotation values are always in local space
//F32 curYaw = deltaHelper.getTurretHorTarget();
//F32 curPitch = deltaHelper.getTurretVerTarget();
//// get new local space pitch and yaw that point at target
//F32 newYaw, newPitch;
//getAnglesFromAimLocation(mAimLocation,newYaw,newPitch);
//
//// create a move that is the difference
//movePtr->yaw = newYaw - curYaw;
//movePtr->pitch = newPitch - curPitch;
}
[/b]
// 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;
}Somewhere in the bold marked area we need to find the right angles to aim the turret at it's target. I already tried to incorporate Paul Dana's turret aiming, but failed to do it right. If someone is interested... jump in! :-)
Martin
#11
01/23/2007 (6:23 am)
Hi, I've built Jerry's changes but haven't tested it because I don't know much about Torque's AI. So I'm reading the Advanced 3D gaming book which has several chapters on AI. Once I get that under my belt I'll take a crack at Jerry's AI tank(not before this weekend). Then I'll have a go at yours Martin. Thanks
#12
01/23/2007 (10:31 am)
@ Martin: turret aiming works, see the code above in AITankShape.cc (2/3)
#13
01/23/2007 (11:26 am)
@Jerry: Ok, I'll check out why it didn't work for me. I'll be back with an update when I got it working. Thanks for the feedback!
#14
02/27/2007 (7:51 am)
Hi, i got this to work, is there a way to get this to work like aIgaurd, i tried to copy some of the code and makes some alterations, but cant get the ai to go from the spawn, the default code works fine in the 1.5 afx code btw. any more progress on your end Martin?
#15
No new progress on the above aiTankShape class. Was bound to the upcoming Flying Starter Kit I have developed and also working with 2 other guys on a full tank game.
02/27/2007 (10:22 am)
Edward: Moving should work with the tanks. Got them moving easily. Have you tried putting in some debug statements (or set breakpoints) in the ai tank shape class if the ai movement state is set to aiMove?No new progress on the above aiTankShape class. Was bound to the upcoming Flying Starter Kit I have developed and also working with 2 other guys on a full tank game.
#16
02/27/2007 (3:48 pm)
Hmm it doesnt seem to want to go to the testboxes, and i looked at the AImove, you made a few changes, i am trying to getit alotlike the aIpatrol and AIguard so that they can be put in the editor, and placed, currently thetank shows up, using the player spawnsphere, inflicting damage on the player. then rolls forward and stops when it hits a obstacle, it doesnt attack me, it doesnt head towards the testboxes (i have 4 in the opposite direction). did you make any current changes to your AITank, i dont deny that it moves, just seems to be buggered a bit.
#17
heres a pic, the tankmakes not effortto turn either.
02/27/2007 (4:02 pm)
Http://i144.photobucket.com/albums/r183/racs333/tankerror-2.jpgheres a pic, the tankmakes not effortto turn either.
#18
02/27/2007 (10:20 pm)
Have you tried a clean copy of torque with the changes above of the aiTankShape?
#19
02/28/2007 (5:52 am)
Well, its not a clean copy because im integrating it into my current project, however i did use your tankshape thats packaged up, i assume the zipped version already included the changes you mentioned . I think it needs abit of work regardless, as i cant have the testboxes spread over the demo area.and i need a better way to bring the AI Tank into the game at loading. I tried Jerry's and i got recompile errors. when i get a better .cs working ill post it to see if its a better solution. rightnow im looking at a mix between AITANk and AI patrol. I wouldnt mind doing a AI gaurd style option, but thats a hair above my skill currently, especially with the Turret involved.
#20
04/19/2009 (5:42 pm)
good resource, i will try to modify it for work in a RTS game...
Torque Owner Jerry Shaw
Roaming Gamer LLC
// AITankShape.h // Defines a tank shape that is driven by AI // Written by Jerry Shaw // Based on AIWheeledVehicle and AITurret #ifndef _AITANKVEHICLE_H_ #define _AITANKVEHICLE_H_ #ifndef _TANKVEHICLE_H_ #include "game/tankShape.h" #endif class AITankShape : public TankShape { private: typedef TankShape Parent; enum MoveState { ModeStop, ModeMove, ModeStuck, ModeTurn, ModeReverse }; enum DrivingState { SteerNull, Left, Right, Straight, TurnAround }; protected: MoveState mMoveState; F32 mMoveSpeed; F32 mMoveTolerance; // Distance from destination before we stop Point3F mMoveDestination; // Destination for movement Point3F mLastLocation; // For stuck check S32 mLastLocationCount; // For stuck check bool mMoveSlowdown; // Slowdown as we near the destination // Aiming SimObjectPtr<GameBase> mAimObject; // Object to point at, overrides location bool mAimLocationSet; // Has an aim location been set? Point3F mAimLocation; // Point to look at Point3F mAimOffset; // Offset to apply to aim location bool mTargetInLOS; // Is target object visible? // Steering DrivingState mSteerState; F32 mLastSteered; Point2F mSteering; bool mIsLeftTurn; F32 getSteeringAngle(); // Utility Methods void throwCallback( const char *name ); void getAnglesFromAimLocation(Point3F &loc, VectorF &velocity, F32 &newYaw, F32 &newPitch); bool isInLOS(GameBase *obj, S32 weaponSlot = 0); virtual bool getAIMove(Move* move); public: AITankShape(); ~AITankShape(); // Targeting and aiming sets/gets void setAimObject( GameBase *targetObject ); void setAimObject( GameBase *targetObject, Point3F offset); GameBase* getAimObject() const { return mAimObject; } void setAimLocation( const Point3F &location ); Point3F getAimLocation() const { return mAimLocation; } void clearAim(); // Movement sets/gets void setMoveSpeed( const F32 speed ); F32 getMoveSpeed() const { return mMoveSpeed; } void setMoveTolerance( const F32 tolerance ); F32 getMoveTolerance() const { return mMoveTolerance; } void setMoveDestination( const Point3F &location, bool slowdown ); Point3F getMoveDestination() const { return mMoveDestination; } void stopMove(); void forwardMove(); void reverseMove(); void leftTurn(); void rightTurn(); DECLARE_CONOBJECT(AITankShape); }; #endif