The Flight Compendium
by Bruno Grieco · 01/30/2005 (1:07 am) · 85 comments
The Flight Compendium
This is an attempt to gather the most important resources I found regarding flight and flying bots I found hanging around GG site. So please keep in mind that I'm just putting together information that other people created. Also, I'm a programmer and my skills in math and physics aren't really great so I don't really understand much of the calculations that are being done, but I do understand where they start and end so I was able to encapsulate them properly.
Acknowledgements
I would like to thank Badguy for his vehicle steering code, Wendell Brown that had actually achieved the bot 3D flight on this thread, F.W. Hardijzer for his excellent Air Control resource, Erik Madison for the Swimming Resource that pointed the way in how to control simple flight and everyone else I forgot to mention that contributed to the heavier-than-air flight.
Simple vs. Complex Flight
There are two ways of flying. The simple way which is making a Player (and an AIPlayer for inheritance) take of the ground and walk in mid-air and the complex way that involves mounting an AIPlayer onto a vehicle and have it fly around. The main difference about them are the physics involved.
You should choose the simple flight method if : your player is supposed to fly on his own, for instance if he has wings (note that there is a Jet Pack resource that will handle if your player has such item), if you need precision in the spot you send your AIPlayer to (since complex flight involves inertia, the move tolerance is bigger), and you are adverse to C++ coding ( even though you will have to do some )
Meanwhile, the complex flight is for those who require a more realistic flight movement, need vehicles to fly and aren't afraid of a compiler.
Simple Flight
The main problem about flying is that you need a Z coordinate (height) to send your bot to, unfortunately the move algorithm only deals with XY plane coordinates, the Z is ignored, it's only used to check if you are falling or climbing something (a hill) in both cases it will check the datablock to see what should be done (fall, or reduce the speed).
The solution to this problem came from the swimming resource, it uses the SetAimLocation() function to determine the direction to tilt the player and the height it should achive.
Coding :
You should edit the Player.cc file ( in engine/Game ) and replace the Player::updateMove() function for this one (It looks big but copy&Paste will do the trick):
Complex Flight
Complex Flight will be implemented directly on AIPlayer. This will enable a bot to pilot flying, wheeled and hover vehicles. The Z-coord info is passed to the aim location on the SetMoveDestination function ( which would also work in Simple Flight ).
In order to maintain the original Move capability, the getAIMove function should be renamed to getAIOnFootMove and a new getAIMove and a getAIMountedMove will be provided.
Coding :
In AIPlayer.h, insert :
In AIPlayer.c substitute :
( remember to rename the previous getAIMove to getAIOnFootMove )
and insert :
in FlyingVehicle.cc, around line 36
Usage
In order to the bot to pilot it, you must create the bot and the vehicle. Then you mount the bot in the vehicle using the function below :
the bot should now drive the vehicle correctly when using %bot.setMoveDestination().
Datablocks
Before complaining that the resource doesn't work, and your plane or hoverthing doesn't go wherever you want to, keep in mind that the great villain in this matter can and will be your Datablock. They can be pretty nasty and even FREEZE the game so I'm adding below the datablocks for wheeled, flying and hover vehicles that did work for me.
But there is only one way of having the vehicles behaving the way you want : TWEAKING.
Vehicle physics are yet a great mystery to me and I have very little or even no idea of how those little parameters work out together.
Wheled Vehicle Datablock
Flying Vehicle Datablock
Hover Vehicle Datablock
Disclaimer
OK guys, that's about it. That's all I know about vehicles, hope it helps.
This is an attempt to gather the most important resources I found regarding flight and flying bots I found hanging around GG site. So please keep in mind that I'm just putting together information that other people created. Also, I'm a programmer and my skills in math and physics aren't really great so I don't really understand much of the calculations that are being done, but I do understand where they start and end so I was able to encapsulate them properly.
Acknowledgements
I would like to thank Badguy for his vehicle steering code, Wendell Brown that had actually achieved the bot 3D flight on this thread, F.W. Hardijzer for his excellent Air Control resource, Erik Madison for the Swimming Resource that pointed the way in how to control simple flight and everyone else I forgot to mention that contributed to the heavier-than-air flight.
Simple vs. Complex Flight
There are two ways of flying. The simple way which is making a Player (and an AIPlayer for inheritance) take of the ground and walk in mid-air and the complex way that involves mounting an AIPlayer onto a vehicle and have it fly around. The main difference about them are the physics involved.
You should choose the simple flight method if : your player is supposed to fly on his own, for instance if he has wings (note that there is a Jet Pack resource that will handle if your player has such item), if you need precision in the spot you send your AIPlayer to (since complex flight involves inertia, the move tolerance is bigger), and you are adverse to C++ coding ( even though you will have to do some )
Meanwhile, the complex flight is for those who require a more realistic flight movement, need vehicles to fly and aren't afraid of a compiler.
Simple Flight
The main problem about flying is that you need a Z coordinate (height) to send your bot to, unfortunately the move algorithm only deals with XY plane coordinates, the Z is ignored, it's only used to check if you are falling or climbing something (a hill) in both cases it will check the datablock to see what should be done (fall, or reduce the speed).
The solution to this problem came from the swimming resource, it uses the SetAimLocation() function to determine the direction to tilt the player and the height it should achive.
Coding :
You should edit the Player.cc file ( in engine/Game ) and replace the Player::updateMove() function for this one (It looks big but copy&Paste will do the trick):
//----------------------------------------------------------------------------
void Player::updateMove(const Move* move)
{
delta.move = *move;
// Trigger images
if (mDamageState == Enabled) {
setImageTriggerState(0,move->trigger[0]);
setImageTriggerState(1,move->trigger[1]);
}
// Update current orientation
if (mDamageState == Enabled)
{
F32 prevZRot = mRot.z;
delta.headVec = mHead;
F32 p = move->pitch;
if (p > M_PI) p -= M_2PI;
mHead.x = mClampF(mHead.x + p,mDataBlock->minLookAngle,
mDataBlock->maxLookAngle);
F32 y = move->yaw;
if (y > M_PI) y -= M_2PI;
GameConnection* con = getControllingClient();
if (move->freeLook && ((isMounted() && getMountNode() == 0) || (con && !con->isFirstPerson())))
{
mHead.z = mClampF(mHead.z + y,
-mDataBlock->maxFreelookAngle,
mDataBlock->maxFreelookAngle);
}
else
{
mRot.z += y;
// Rotate the head back to the front, center horizontal
// as well if we're controlling another object.
mHead.z *= 0.5;
if (mControlObject)
mHead.x *= 0.5;
}
// constrain the range of mRot.z
while (mRot.z < 0)
mRot.z += M_2PI;
while (mRot.z > M_2PI)
mRot.z -= M_2PI;
delta.rot = mRot;
delta.rotVec.x = delta.rotVec.y = 0;
delta.rotVec.z = prevZRot - mRot.z;
if (delta.rotVec.z > M_PI)
delta.rotVec.z -= M_2PI;
else if (delta.rotVec.z < -M_PI)
delta.rotVec.z += M_2PI;
delta.head = mHead;
delta.headVec -= mHead;
}
MatrixF zRot;
zRot.set(EulerF(0, 0, mRot.z));
// Desired move direction & speed
VectorF moveVec;
F32 moveSpeed;
if (mState == MoveState && mDamageState == Enabled)
{
zRot.getColumn(0,&moveVec);
moveVec *= move->x;
VectorF tv;
zRot.getColumn(1,&tv);
moveVec += tv * move->y;
// Clamp water movement
if (move->y > 0)
{
if( mWaterCoverage >= 0.9 )
moveSpeed = getMax(mDataBlock->maxUnderwaterForwardSpeed * move->y,
mDataBlock->maxUnderwaterSideSpeed * mFabs(move->x));
else
moveSpeed = getMax(mDataBlock->maxForwardSpeed * move->y,
mDataBlock->maxSideSpeed * mFabs(move->x));
}
else
{
if( mWaterCoverage >= 0.9 )
moveSpeed = getMax(mDataBlock->maxUnderwaterBackwardSpeed * mFabs(move->y),
mDataBlock->maxUnderwaterSideSpeed * mFabs(move->x));
else
moveSpeed = getMax(mDataBlock->maxBackwardSpeed * mFabs(move->y),
mDataBlock->maxSideSpeed * mFabs(move->x));
}
// Cancel any script driven animations if we are going to move.
/*if (moveVec.x + moveVec.y + moveVec.z != 0 &&
(mActionAnimation.action >= PlayerData::NumTableActionAnims
|| mActionAnimation.action == PlayerData::LandAnim))
mActionAnimation.action = PlayerData::NullAnimation;
*/
}
else
{
moveVec.set(0,0,0);
moveSpeed = 0;
}
// Acceleration due to gravity
VectorF acc(0,0,mGravity * mGravityMod * TickSec);
//VectorF acc(0,0,0.5);
// Determin ground contact normal. Only look for contacts if
// we can move.
VectorF contactNormal, normalZ(moveVec.x,-moveVec.z,moveVec.y);
bool jumpSurface = false, runSurface = false;
if (!isMounted())
findContact(&runSurface,&jumpSurface,&contactNormal);
if (jumpSurface)
mJumpSurfaceNormal = contactNormal;
// Point3F headRotation = getHeadRotation();
// acc.z = (-headRotation.x) * (mDataBlock->runForce / mMass) * TickSec;
// Acceleration on run surface
if (runSurface)
{
//Con::printf("Estou em uma Run Surface");
mContactTimer = 0;
// Remove acc into contact surface (should only be gravity)
// Clear out floating point acc errors, this will allow
// the player to "rest" on the ground.
F32 vd = -mDot(acc,contactNormal);
if (vd > 0) {
VectorF dv = contactNormal * (vd + 0.002);
acc += dv;
if (acc.len() < 0.0001)
acc.set(0,0,0);
}
// Force a 0 move if there is no energy, and only drain
// move energy if we're moving.
VectorF pv;
if (mEnergy >= mDataBlock->minRunEnergy) {
if (moveSpeed)
mEnergy -= mDataBlock->runEnergyDrain;
pv = moveVec;
}
else
pv.set(0,0,0);
// Adjust the players's requested dir. to be parallel
// to the contact surface.
F32 pvl = pv.len();
if(pvl)
{
VectorF nn;
mCross(pv,VectorF(0,0,1),&nn);
nn *= 1 / pvl;
VectorF cv = contactNormal;
cv -= nn * mDot(nn,cv);
pv -= cv * mDot(pv,cv);
pvl = pv.len();
}
// Convert to acceleration
if (pvl)
pv *= moveSpeed / pvl;
VectorF runAcc = pv - (mVelocity + acc);
F32 runSpeed = runAcc.len();
// Clamp acceleratin, player also accelerates faster when
// in his hard landing recover state.
F32 maxAcc = (mDataBlock->runForce / mMass) * TickSec;
if (mState == RecoverState)
maxAcc *= mDataBlock->recoverRunForceScale;
if (runSpeed > maxAcc)
runAcc *= maxAcc / runSpeed;
acc += runAcc;
// If we are running on the ground, then we're not jumping
if (mDataBlock->isJumpAction(mActionAnimation.action))
mActionAnimation.action = PlayerData::NullAnimation;
}
else
{
F32 airControl = 1.5;
mContactTimer=0;
//--- by DJMystic ---
VectorF pv;
pv = moveVec;
F32 pvl = pv.len();
if (pvl)
pv *= moveSpeed / pvl;
VectorF runAcc = pv - ( mVelocity + acc);
runAcc.z = runAcc.z * airControl;
runAcc.x = runAcc.x * airControl;
runAcc.y = runAcc.y * airControl;
F32 runSpeed = runAcc.len();
F32 maxAcc = (mDataBlock->runForce / mMass) * TickSec * airControl;
if (runSpeed > maxAcc)
runAcc *= maxAcc / runSpeed;
acc += runAcc;
//--- end by DJMystic ---
}
Point3F headRotation = getHeadRotation();
acc.z = (-headRotation.x) * (mDataBlock->runForce / mMass) * TickSec;
// Acceleration from Jumping
if (move->trigger[2] && !isMounted() && canJump())
{
// Scale the jump impulse base on maxJumpSpeed
F32 zSpeedScale = mVelocity.z;
if (zSpeedScale <= mDataBlock->maxJumpSpeed) {
zSpeedScale = (zSpeedScale <= mDataBlock->minJumpSpeed)? 1:
1 - (zSpeedScale - mDataBlock->minJumpSpeed) /
(mDataBlock->maxJumpSpeed - mDataBlock->minJumpSpeed);
// Desired jump direction
VectorF pv = moveVec;
F32 len = pv.len();
if (len > 0)
pv *= 1 / len;
// We want to scale the jump size by the player size, somewhat
// in reduced ratio so a smaller player can jump higher in
// proportion to his size, than a larger player.
F32 scaleZ = (getScale().z * 0.25) + 0.75;
// If we are facing into the surface jump up, otherwise
// jump away from surface.
F32 dot = mDot(pv,mJumpSurfaceNormal);
F32 impulse = mDataBlock->jumpForce / mMass;
if (dot <= 0)
acc.z += mJumpSurfaceNormal.z * scaleZ * impulse * zSpeedScale;
else {
acc.x += pv.x * impulse * dot;
acc.y += pv.y * impulse * dot;
acc.z += mJumpSurfaceNormal.z * scaleZ * impulse * zSpeedScale;
}
mJumpDelay = mDataBlock->jumpDelay;
mEnergy -= mDataBlock->jumpEnergyDrain;
setActionThread((mVelocity.len() < 0.5)?
PlayerData::StandJumpAnim: PlayerData::JumpAnim, true, false, true);
mJumpSurfaceLastContact = JumpSkipContactsMax;
}
}
else
{
if (jumpSurface)
{
if (mJumpDelay > 0)
mJumpDelay--;
mJumpSurfaceLastContact = 0;
}
else
mJumpSurfaceLastContact++;
}
// Add in force from physical zones...
acc += (mAppliedForce / mMass) * TickSec;
// Adjust velocity with all the move & gravity acceleration
// TG: I forgot why doesn't the TickSec multiply happen here...
mVelocity += acc;
// apply horizontal air resistance
F32 hvel = mSqrt(mVelocity.x * mVelocity.x + mVelocity.y * mVelocity.y);
if(hvel > mDataBlock->horizResistSpeed)
{
F32 speedCap = hvel;
if(speedCap > mDataBlock->horizMaxSpeed)
speedCap = mDataBlock->horizMaxSpeed;
speedCap -= mDataBlock->horizResistFactor * TickSec * (speedCap - mDataBlock->horizResistSpeed);
F32 scale = speedCap / hvel;
mVelocity.x *= scale;
mVelocity.y *= scale;
}
if(mVelocity.z > mDataBlock->upResistSpeed)
{
if(mVelocity.z > mDataBlock->upMaxSpeed)
mVelocity.z = mDataBlock->upMaxSpeed;
mVelocity.z -= mDataBlock->upResistFactor * TickSec * (mVelocity.z - mDataBlock->upResistSpeed);
}
// Container buoyancy & drag
if (mBuoyancy != 0)
{ // Applying buoyancy when standing still causing some jitters-
if (mBuoyancy > 1.0 || !runSurface) // || !mVelocity.isZero() )
mVelocity.z -= mBuoyancy * mGravity * mGravityMod * TickSec;
}
mVelocity -= mVelocity * mDrag * TickSec;
// If we are not touching anything and have sufficient -z vel,
// we are falling.
if (runSurface)
mFalling = false;
else
{
VectorF vel;
mWorldToObj.mulV(mVelocity,&vel);
mFalling = vel.z < sFallingThreshold;
}
if (!isGhost())
{
// Vehicle Dismount
if(move->trigger[2] && isMounted())
Con::executef(mDataBlock,2,"doDismount",scriptThis());
if(!inLiquid && mWaterCoverage != 0.0f)
{
Con::executef(mDataBlock,4,"onEnterLiquid",scriptThis(), Con::getFloatArg(mWaterCoverage), Con::getIntArg(mLiquidType));
inLiquid = true;
}
else if(inLiquid && mWaterCoverage == 0.0f)
{
Con::executef(mDataBlock,3,"onLeaveLiquid",scriptThis(), Con::getIntArg(mLiquidType));
inLiquid = false;
}
}
else
{
if(!inLiquid && mWaterCoverage >= 1.0f)
{
inLiquid = true;
}
else
if(inLiquid && mWaterCoverage < 0.8f)
{
if(getVelocity().len() >= mDataBlock->exitSplashSoundVel && !isMounted())
alxPlay(mDataBlock->sound[PlayerData::ExitWater], &getTransform());
inLiquid = false;
}
}
}
//----------------------------------------------------------------------------Complex Flight
Complex Flight will be implemented directly on AIPlayer. This will enable a bot to pilot flying, wheeled and hover vehicles. The Z-coord info is passed to the aim location on the SetMoveDestination function ( which would also work in Simple Flight ).
In order to maintain the original Move capability, the getAIMove function should be renamed to getAIOnFootMove and a new getAIMove and a getAIMountedMove will be provided.
Coding :
In AIPlayer.h, insert :
virtual bool getAIMountedMove(Move *move); virtual bool getAIOnFootMove(Move *move);right after the original getAIMove prototype.
In AIPlayer.c substitute :
void AIPlayer::setMoveDestination( const Point3F &location, bool slowdown )
{
mMoveDestination = location;
mMoveState = ModeMove;
mAimLocationSet = true;
mAimLocation = location;
mMoveSlowdown = slowdown;
}bool AIPlayer::getAIMove(Move *movePtr)
{
if(isMounted())
return(getAIMountedMove(movePtr));
else
return(getAIOnFootMove(movePtr));
}( remember to rename the previous getAIMove to getAIOnFootMove )
and insert :
bool AIPlayer::getAIMountedMove(Move *movePtr)
{
*movePtr = NullMove;
Point3F location;
Point3F rotation;
F32 myTol = mMoveTolerance * 7;
// now we use local space
location.x = location.y = location.z = 0.0;
rotation.x = rotation.z = 0.0;
rotation.y = 1.0;
MatrixF invTrans = getTransform();
invTrans.inverse();
// <- phdana testing
// Orient towards the aim point, aim object, or towards
// our destination.
// NOTE: you must change the protection these data members
// of AIPlayer from private to protected to get this
// to compile...
if (mAimObject || mAimLocationSet || mMoveState == ModeMove)
{
// Update the aim position if we're aiming for an object
if (mAimObject)
mAimLocation = mAimObject->getPosition();
// transform to local space
Point3F aimLocation;
invTrans.mulP(mAimLocation,&aimLocation);
F32 xDiff = aimLocation.x - location.x;
F32 yDiff = aimLocation.y - location.y;
if (!isZero(xDiff) || !isZero(yDiff)) {
// First do Yaw
// use the cur yaw between -Pi and Pi
F32 curYaw = rotation.z;
while (curYaw > M_2PI)
curYaw -= M_2PI;
while (curYaw < -M_2PI)
curYaw += M_2PI;
// find the yaw offset
F32 newYaw = mAtan( xDiff, yDiff );
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;
movePtr->yaw = yawDiff;
// Next do pitch. This should be adjusted to run from the
// eye point to the object's center position. Though this
// works well enough for now.
F32 vertDist = aimLocation.z - location.z;
F32 horzDist = mSqrt(xDiff * xDiff + yDiff * yDiff);
F32 newPitch = mAtan( horzDist, vertDist ) - ( M_PI / 2.0f );
// phdana testing ->
movePtr->pitch = newPitch - rotation.x;
// <- phdan testsing
}
}
else
{
// Level out if we're not doing anything else
// Point3F headRotation = getRotation();
// movePtr->pitch = -headRotation.x;
}
// Move towards the destination
if (mMoveState == ModeMove)
{
// transform to local space
Point3F moveDestination;
invTrans.mulP(mMoveDestination,&moveDestination);
F32 xDiff = moveDestination.x - location.x;
F32 yDiff = moveDestination.y - location.y;
F32 zDiff = mMoveDestination.z - location.z;
// Check if we should mMove, or if we are 'close enough'
if ((mFabs(xDiff) < myTol) && (mFabs(yDiff) < myTol)) // && (mFabs(zDiff) < myTol ))
{
mMoveState = ModeStop;
Con::printf("AI Steering : ------------------------------------- Cheguei no destino pela função default");
throwCallback("onReachDestination");
}
else
{
// Build move direction in world space
if (isZero(xDiff))
{
movePtr->y = 1; // Always go forward
}
else
{
if (isZero(yDiff))
{
movePtr->x = (location.x > mMoveDestination.x)? -1 : 1;
}
else
{
if (mFabs(xDiff) > mFabs(yDiff))
{
F32 value = mFabs(yDiff / xDiff);
movePtr->x = (location.x > mMoveDestination.x)? -1 : 1;
movePtr->y = value;
}
else
{
F32 value = mFabs(xDiff / yDiff);
movePtr->x = (location.x > mMoveDestination.x)? -value : value;
movePtr->y = 1;
}
}
}
}
// Rotate the move into object space (this really only needs
// a 2D matrix)
Point3F newMove;
MatrixF moveMatrix;
moveMatrix.set(EulerF(0, 0, -(rotation.z + movePtr->yaw)));
moveMatrix.mulV( Point3F( movePtr->x, movePtr->y, 0 ), &newMove );
// 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;
movePtr->x *= speed;
movePtr->y *= speed;
}
else
{
movePtr->x *= mMoveSpeed;
movePtr->y *= mMoveSpeed;
}
// We should check to see if we are stuck...
if (location == mLastLocation)
{
throwCallback("onMoveStuck");
mMoveState = ModeStop;
}
}
// 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;
}in FlyingVehicle.cc, around line 36
sFlyingVehicleGravity = 0;
Usage
In order to the bot to pilot it, you must create the bot and the vehicle. Then you mount the bot in the vehicle using the function below :
function MountVehicle(%obj, %targetObject)
{
echo("Found a vehicle: " @ %targetObject);
onMountVehicle(%targetObject.getDataBlock(),%obj,%targetObject);
}the bot should now drive the vehicle correctly when using %bot.setMoveDestination().
Datablocks
Before complaining that the resource doesn't work, and your plane or hoverthing doesn't go wherever you want to, keep in mind that the great villain in this matter can and will be your Datablock. They can be pretty nasty and even FREEZE the game so I'm adding below the datablocks for wheeled, flying and hover vehicles that did work for me.
But there is only one way of having the vehicles behaving the way you want : TWEAKING.
Vehicle physics are yet a great mystery to me and I have very little or even no idea of how those little parameters work out together.
Wheled Vehicle Datablock
datablock WheeledVehicleData(DefaultCar)
{
category = "Vehicles";
shapeFile = "~/data/shapes/buggy/buggy.dts";
emap = true;
maxDamage = 1.0;
destroyedLevel = 0.5;
maxSteeringAngle = 0.785; // Maximum steering angle, should match animation
tireEmitter = TireEmitter; // All the tires use the same dust emitter
// 3rd person camera settings
cameraRoll = true; // Roll the camera with the vehicle
cameraMaxDist = 6; // Far distance from vehicle
cameraOffset = 1.5; // Vertical offset from camera mount point
cameraLag = 0.1; // Velocity lag of camera
cameraDecay = 0.75; // Decay per sec. rate of velocity lag
// Rigid Body
mass = 200;
massCenter = "0 -0.5 0"; // Center of mass for rigid body
massBox = "0 0 0"; // Size of box used for moment of inertia,
// if zero it defaults to object bounding box
drag = 0.6; // Drag coefficient
bodyFriction = 0.6;
bodyRestitution = 0.4;
minImpactSpeed = 5; // Impacts over this invoke the script callback
softImpactSpeed = 5; // Play SoftImpact Sound
hardImpactSpeed = 15; // Play HardImpact Sound
integration = 4; // Physics integration: TickSec/Rate
collisionTol = 0.1; // Collision distance tolerance
contactTol = 0.1; // Contact velocity tolerance
// Engine
engineTorque = 1000; // Engine power era 4000
engineBrake = 600; // Braking when throttle is 0
brakeTorque = 8000; // When brakes are applied
maxWheelSpeed = 30; // Engine scale by current speed / max speed
// Energy
maxEnergy = 100;
jetForce = 3000;
minJetEnergy = 30;
jetEnergyDrain = 2;
// Sounds
// jetSound = ScoutThrustSound;
// engineSound = BuggyEngineSound;
// squealSound = ScoutSquealSound;
// softImpactSound = SoftImpactSound;
// hardImpactSound = HardImpactSound;
// wheelImpactSound = WheelImpactSound;
// explosion = VehicleExplosion;
maxMountSpeed = 0.1;
mountDelay = 2;
dismountDelay = 1;
stationaryThreshold = 0.5;
maxDismountSpeed = 0.1;
numMountPoints = 1;
mountable = true;
mountPose[0] = "Sitting";
mountPointTransform[0] = "0 0 0 0 0 1 0";
isProtectedMountPoint[0] = false;
};Flying Vehicle Datablock
datablock FlyingVehicleData(Drone)
{
spawnOffset = "0 0 2";
emap = true;
category = "Vehicles";
shapeFile = "~/data/shapes/vehicles/Drone.dts";
multipassenger = false;
computeCRC = true;
debrisShapeName = "~/data/shapes/vehicles/Drone.dts";
debris = DroneShapeDebris;
renderWhenDestroyed = false;
mountPose[0] = sitting;
numMountPoints = 1;
isProtectedMountPoint[0] = true;
cameraMaxDist = 0.5;
cameraOffset = 4.5;
cameraLag = 0.0;
cameraRoll = true; // Roll the camera with the vehicle
// explosion = DroneVehicleExplosion;
explosionDamage = 10.5;
explosionRadius = 15.0;
maxDamage = 50.40;
destroyedLevel = 50.40;
isShielded = true;
energyPerDamagePoint = 160;
maxEnergy = 280; // Afterburner and any energy weapon pool
rechargeRate = 0.8;
integration = 4; // Physics integration: TickSec/Rate
collisionTol = 0.2; // Collision distance tolerance
contactTol = 0.1;
drag = 0.6;
density = 6.0;
minDrag = 30; // Linear Drag (eventually slows you down when not thrusting...constant drag)
rotationalDrag = 5; // Anguler Drag (dampens the drift after you stop moving the mouse...also tumble drag)
maxAutoSpeed = 10; // Autostabilizer kicks in when less than this speed. (meters/second)
autoAngularForce = 200; // Angular stabilizer force (this force levels you out when autostabilizer kicks in)
autoLinearForce = 200; // Linear stabilzer force (this slows you down when autostabilizer kicks in)
autoInputDamping = 0.95; // Dampen control input so you don't' whack out at very slow speeds
// Maneuvering
maxSteeringAngle = 0.785; // Max radiens you can rotate the wheel. Smaller number is more maneuverable.
horizontalSurfaceForce = 6; // Horizontal center "wing" (provides "bite" into the wind for climbing/diving and turning)
verticalSurfaceForce = 0.5; // Vertical center "wing" (controls side slip. lower numbers make MORE slide.)
maneuveringForce = 1400; // Horizontal jets (W,S,D,A key thrust)
steeringForce = 200; // Steering jets (force applied when you move the mouse)
steeringRollForce = 10; // Steering jets (how much you heel over when you turn)
rollForce = 20; // Auto-roll (self-correction to right you after you roll/invert)
hoverHeight = 45; // Height off the ground at rest
createHoverHeight = 45; // Height off the ground when created
maxForwardSpeed = 60; // speed in which forward thrust force is no longer applied (meters/second)
// Turbo Jet
jetForce = 3000; // Afterburner thrust (this is in addition to normal thrust)
minJetEnergy = 28; // Afterburner can't be used if below this threshhold.
jetEnergyDrain = 2.8; // Energy use of the afterburners (low number is less drain...can be fractional) // Auto stabilize speed
vertThrustMultiple = 3.0;
// Rigid body
mass = 90; //90 // Mass of the vehicle
bodyFriction = 0; // Don't mess with this.
bodyRestitution = 0.4; // When you hit the ground, how much you rebound. (between 0 and 1)
minRollSpeed = 2000; // Don't mess with this.
softImpactSpeed = 3; // Sound hooks. This is the soft hit.
hardImpactSpeed = 15; // Sound hooks. This is the hard hit.
// Ground Impact Damage (uses DamageType::Ground)
minImpactSpeed = 10; // If hit ground at speed above this then it's an impact. Meters/second
speedDamageScale = 0.06;
// Object Impact Damage (uses DamageType::Impact)
collDamageThresholdVel = 23.0;
collDamageMultiplier = 0.02;
//
minTrailSpeed = 15; // The speed your contrail shows up at.
triggerDustHeight = 4.0;
dustHeight = 1.0;
damageEmitterOffset[0] = "0.0 -3.0 0.0 ";
damageLevelTolerance[0] = 0.3;
damageLevelTolerance[1] = 0.7;
numDmgEmitterAreas = 3;
minMountDist = 2;
maxMountSpeed = 100;
checkRadius = 5.5;
observeParameters = "0 0 0";
shieldEffectScale = "0.937 1.125 0.60";
};Hover Vehicle Datablock
datablock HoverVehicleData(AssaultTank)
{
spawnOffset = "0 0 1";
floatingGravMag = 50.5;
catagory = "Vehicles";
shapeFile = "~/data/shapes/buggy/buggy.dts";
computeCRC = true;
renderWhenDestroyed = false;
drag = 0.6;
density = 0.3;
mountPose[0] = sitting;
numMountPoints = 1;
cameraMaxDist = 15.0;
cameraOffset = 5.7;
cameraLag = 5.5;
isProtectedMountPoint[0] = false;
explosionDamage = 0.5;
explosionRadius = 5.0;
lightOnly = 1;
maxDamage = 120;
destroyedLevel = 120;
isShielded = true;
rechargeRate = 0.7;
energyPerDamagePoint = 75;
maxEnergy = 150;
minJetEnergy = 15;
jetEnergyDrain = 1.3;
// Rigid Body
mass = 5;
bodyFriction = 0;
bodyRestitution = 0.5;
softImpactSpeed = 20; // Play SoftImpact Sound
hardImpactSpeed = 28; // Play HardImpact Sound
// Ground Impact Damage (uses DamageType::Ground)
minImpactSpeed = 29;
speedDamageScale = 0.010;
// Object Impact Damage (uses DamageType::Impact)
collDamageThresholdVel = 23;
collDamageMultiplier = 0.030;
dragForce = 4; // 1.9
// dragForce = 25 / 45.0;
vertFactor = 0.5;
floatingThrustFactor = 0.35;
mainThrustForce = 100;
reverseThrustForce = 70;
strafeThrustForce = 70;
turboFactor = 1.5;
brakingForce = 30;
brakingActivationSpeed = 30;
stabLenMin = 6.50;
stabLenMax = 7.25;
stabSpringConstant = 20;
stabDampingConstant = 20;
gyroDrag = 16; // 16 // 36
gyroForce = 50; //50
normalForce = 10;
restorativeForce = 100;
steeringForce = 90;
rollForce = 7;
pitchForce = 10;
minMountDist = 4;
checkRadius = 1.7785;
observeParameters = "1 10 10";
shieldEffectScale = "0.9375 1.125 0.6";
};Disclaimer
OK guys, that's about it. That's all I know about vehicles, hope it helps.
About the author
#82
12/01/2008 (7:56 am)
I am currently using torque version 1.5.2 if that matters?
#83
//if (isZero(yDiff))
if (yDiff < 10.0)//CHANGED THE CONDITION COZ I NEED IT TO BE A LITTLE FLEXIBLE, PROBABLY 5.0 WILL BE ENOUGH
{
movePtr->x = (location.x > mMoveDestination.x)? -1.0 : 1.0;
//Con::printf("YDIFF < 10");
//This makes the car go in reverse until the next forward drivable point is found
movePtr->y = -1;
}
Adding movePtr ->y =-1, it makes the driver move back if he oversteers until he is able to move forward to the next destination. However, sometimes he also starts moving around in circles in reverse direction. So I don't think this is the best solution, but it's probably better than getting stuck. In fact, Ahab was also talking about making the vehicle go in reverse if the destination is behind the vehicle. Adding this code does it for now.
Hope this helps.
01/08/2009 (11:42 pm)
Hi Guys, I found Ahab's code is really effective, but when the vehicle oversteers, it gets stuck and is unable to move further. I tried adding the following://if (isZero(yDiff))
if (yDiff < 10.0)//CHANGED THE CONDITION COZ I NEED IT TO BE A LITTLE FLEXIBLE, PROBABLY 5.0 WILL BE ENOUGH
{
movePtr->x = (location.x > mMoveDestination.x)? -1.0 : 1.0;
//Con::printf("YDIFF < 10");
//This makes the car go in reverse until the next forward drivable point is found
movePtr->y = -1;
}
Adding movePtr ->y =-1, it makes the driver move back if he oversteers until he is able to move forward to the next destination. However, sometimes he also starts moving around in circles in reverse direction. So I don't think this is the best solution, but it's probably better than getting stuck. In fact, Ahab was also talking about making the vehicle go in reverse if the destination is behind the vehicle. Adding this code does it for now.
Hope this helps.
#84
When i spawn the ai in as a vehicle and set a move destination nothing happens either.
dont suppose there is anyone out there that could help? in the meantime i shall kepp investigating
09/18/2009 (4:36 am)
Hey, I have ported this into T3D recently and it compiles fine but the AI when in a flying vehicle and sert to move somewhere it just spirals downwards into oblivion.When i spawn the ai in as a vehicle and set a move destination nothing happens either.
dont suppose there is anyone out there that could help? in the meantime i shall kepp investigating
#85
I haven't touched TGE since 2007. But as I interpret from your post, you are mounting the AI on to the vehicle.
Is that right ?
As I remember from 2005 when I wrote this article, you also have to AIM to the point you want to go.
Please pardon me for not helping much, but I'm out of the gaming business for over 3 or 4 years now.
09/20/2009 (11:31 pm)
@Sam,I haven't touched TGE since 2007. But as I interpret from your post, you are mounting the AI on to the vehicle.
Is that right ?
As I remember from 2005 when I wrote this article, you also have to AIM to the point you want to go.
Please pardon me for not helping much, but I'm out of the gaming business for over 3 or 4 years now.

Torque Owner Brown College (#0102)
I just added: function VehicleData::onAdd(%data, %obj) & function HoverVehicle::useCreateHeight()
It still crashes I'm not sure why, everything looks correct.
datablock HoverVehicleData(hoverboardA)
{
spawnOffset = "0 0 1";
floatingGravMag = 50.5;
category = "Vehicles";
shapeFile = "~/data/shapes/hoverboard/board.dts";
computeCRC = true;
//renderWhenDestroyed = false;
drag = 0.6;
density = 0.3;
mountPose[0] = "sitting";
numMountPoints = 1;
cameraMaxDist = 15.0;
cameraOffset = 5.7;
cameraLag = 5.5;
isProtectedMountPoint[0] = false;
explosionDamage = 0.5;
explosionRadius = 5.0;
lightOnly = 1;
maxDamage = 120;
destroyedLevel = 120;
//isShielded = true;
rechargeRate = 0.7;
energyPerDamagePoint = 75;
maxEnergy = 150;
minJetEnergy = 15;
jetEnergyDrain = 1.3;
// Rigid Body
mass = 5;
bodyFriction = 0;
bodyRestitution = 0.5;
softImpactSpeed = 20; // Play SoftImpact Sound
hardImpactSpeed = 28; // Play HardImpact Sound
// Ground Impact Damage (uses DamageType::Ground)
minImpactSpeed = 29;
speedDamageScale = 0.010;
// Object Impact Damage (uses DamageType::Impact)
collDamageThresholdVel = 23;
collDamageMultiplier = 0.030;
dragForce = 4; // 1.9
// dragForce = 25 / 45.0;
vertFactor = 0.5;
floatingThrustFactor = 0.35;
mainThrustForce = 100;
reverseThrustForce = 70;
strafeThrustForce = 70;
turboFactor = 1.5;
brakingForce = 30;
brakingActivationSpeed = 30;
stabLenMin = 6.50;
stabLenMax = 7.25;
stabSpringConstant = 20;
stabDampingConstant = 20;
gyroDrag = 16; // 16 // 36
gyroForce = 50; //50
normalForce = 10;
restorativeForce = 100;
steeringForce = 90;
rollForce = 7;
pitchForce = 10;
minMountDist = 4;
checkRadius = 1.7785;
observeParameters = "1 10 10";
shieldEffectScale = "0.9375 1.125 0.6";
};
function VehicleData::onAdd(%data, %obj)
{
Parent::onAdd(%data, %obj);
if((%data.sensorData !$= "") && (%obj.getTarget() != -1))
setTargetSensorData(%obj.getTarget(), %data.sensorData);
%obj.setRechargeRate(%data.rechargeRate);
// set full energy
%obj.setEnergyLevel(%data.MaxEnergy);
if(%obj.disableMove)
%obj.immobilized = true;
if(%obj.deployed)
{
if($countDownStarted)
%data.schedule(($Host::WarmupTime * 1000) / 2, "vehicleDeploy", %obj, 0, 1);
else
{
$VehiclesDeploy[$NumVehiclesDeploy] = %obj;
$NumVehiclesDeploy++;
}
}
if(%obj.mountable || %obj.mountable $= "")
%data.isMountable(%obj, true);
else
%data.isMountable(%obj, false);
%obj.setSelfPowered();
// %data.canObserve = true;
}
function HoverVehicle::useCreateHeight()
{
//this function is declared to prevent console error msg spam...
}
function makehoverboard(%position)
{
$hoverboard = new HoverVehicle()
{
dataBlock = hoverboardA;
position = %position;
};
}