Legacy:KHoverTank
From Unreal Wiki, The Unreal Engine Documentation Site
Adding extra wheels is pretty easy, useful for making tanks or hovercrafts. :] Also, Rear Steering with forward steering allows for some sharp turns and cool looking vehicle :]
//=============================================================================
// KHoverTank.
// Base class for 6-invisible wheeled 'hover' vehicles using Karma
// Assumes negative-X is forward, negative-Y is right
//=============================================================================
class KHoverTank extends KCar
abstract;
var KTire midLeft, midRight;
var (KHoverTank) class<KTire> MidTireClass;
// Wheel positions
var const float WheelMidAlong;
var const float WheelMidAcross;
replication
{
// We replicate the Gear for brake-lights etc.
unreliable if(Role == ROLE_Authority)
CarState, Gear;
reliable if(Role == ROLE_Authority)
FlipTimeLeft;
}
// When new information is received, see if it's new. If so, pass bits off the wheels.
// Each part will then update its rigid body position via the KUpdateState event.
// JTODO: This is where clever unpacking would happen.
simulated event VehicleStateReceived()
{
local vector ChassisY, SteerY, ChassisZ, calcPos, WheelY, lPos;
local vector chassisPos, chassisLinVel, chassisAngVel, WheelLinVel, wPosRel;
local Quat relQ, WheelQ;
// Don't do anything if car isn't started up.
// Shouldn't need to worry about Front or Rear as it's done in Super.
if( midLeft == None || midRight == None )
return;
Super.VehicleStateReceived()
////////////////////////// MID LEFT //////////////////////////
midLeft.KGetRigidBodyState(midLeft.ReceiveState);
// Position
lPos.X = WheelMidAlong;
lPos.Y = WheelMidAcross;
lPos.Z = CarState.WheelHeight[2];
calcPos = chassisPos + QuatRotateVector(CarState.ChassisQuaternion, lPos);
midLeft.ReceiveState.Position = KRBVecFromVector(calcPos);
// Rotation
wheelQ = midLeft.KGetRBQuaternion();
WheelY = QuatRotateVector(wheelQ, vect(0, 1, 0));
relQ = QuatFindBetween(WheelY, ChassisY);
midLeft.ReceiveState.Quaternion = QuatProduct(relQ, wheelQ);
// Velocity
wPosRel = calcPos - chassisPos;
WheelLinVel = chassisLinVel + (chassisAngVel Cross wPosRel);
WheelLinVel += CarState.WheelVertVel[2] * ChassisZ;
midLeft.ReceiveState.LinVel = KRBVecFromVector(WheelLinVel);
midLeft.bReceiveStateNew = true;
////////////////////////// MID RIGHT //////////////////////////
midRight.KGetRigidBodyState(midRight.ReceiveState);
// Position
lPos.X = WheelMidAlong;
lPos.Y = WheelMidAcross;
lPos.Z = CarState.WheelHeight[3];
calcPos = chassisPos + QuatRotateVector(CarState.ChassisQuaternion, lPos);
midRight.ReceiveState.Position = KRBVecFromVector(calcPos);
// Rotation
wheelQ = midRight.KGetRBQuaternion();
WheelY = QuatRotateVector(wheelQ, vect(0, 1, 0));
relQ = QuatFindBetween(WheelY, ChassisY);
midRight.ReceiveState.Quaternion = QuatProduct(relQ, wheelQ);
// Velocity
wPosRel = calcPos - chassisPos;
WheelLinVel = chassisLinVel + (chassisAngVel Cross wPosRel);
WheelLinVel += CarState.WheelVertVel[3] * ChassisZ;
midRight.ReceiveState.LinVel = KRBVecFromVector(WheelLinVel);
midRight.bReceiveStateNew = true;
////// OTHER //////
// Update control inputs
Steering = CarState.ServerSteering;
OutputTorque = CarState.ServerTorque;
OutputBrake = CarState.ServerBrake;
OutputHandbrakeOn = CarState.ServerHandbrakeOn;
// Update flags
CarState.bNewState = false;
bNewCarState = true;
}
// Pack current state of whole car into the state struct, to be sent to the client.
// Should only get called on the server.
function PackState()
{
local vector lPos, wPos, chassisPos, chassisLinVel, chassisAngVel, wPosRel, WheelLinVel;
local vector ChassisX, ChassisZ, WheelY, oldPos, oldLinVel;
local KRigidBodyState ChassisState, WheelState, TurrState;
Super.PackState();
////////////////////////// MID LEFT //////////////////////////
midLeft.KGetRigidBodyState(WheelState);
wPos = KRBVecToVector(WheelState.Position);
lPos = QuatRotateVector(QuatInvert(CarState.ChassisQuaternion), wPos - chassisPos);
CarState.WheelHeight[2] = lPos.Z;
wPosRel = KRBVecToVector(WheelState.Position) - chassisPos;
WheelLinVel = chassisLinVel + (chassisAngVel Cross wPosRel);
CarState.WheelVertVel[2] =
((WheelState.LinVel.X - WheelLinVel.X)* ChassisZ.X) +
((WheelState.LinVel.Y - WheelLinVel.Y)* ChassisZ.Y) +
((WheelState.LinVel.Z - WheelLinVel.Z)* ChassisZ.Z);
WheelY = QuatRotateVector(WheelState.Quaternion, vect(0, 1, 0));
////////////////////////// MID RIGHT //////////////////////////
midRight.KGetRigidBodyState(WheelState);
wPos = KRBVecToVector(WheelState.Position);
lPos = QuatRotateVector(QuatInvert(CarState.ChassisQuaternion), wPos - chassisPos);
CarState.WheelHeight[3] = lPos.Z;
wPosRel = KRBVecToVector(WheelState.Position) - chassisPos;
WheelLinVel = chassisLinVel + (chassisAngVel Cross wPosRel);
CarState.WheelVertVel[3] =
((WheelState.LinVel.X - WheelLinVel.X)* ChassisZ.X) +
((WheelState.LinVel.Y - WheelLinVel.Y)* ChassisZ.Y) +
((WheelState.LinVel.Z - WheelLinVel.Z)* ChassisZ.Z);
WheelY = QuatRotateVector(WheelState.Quaternion, vect(0, 1, 0));
// OTHER
CarState.ServerSteering = Steering;
CarState.ServerTorque = OutputTorque;
CarState.ServerBrake = OutputBrake;
CarState.ServerHandbrakeOn = OutputHandbrakeOn;
// This flag lets the client know this data is new.
CarState.bNewState = true;
}
simulated function PostNetBeginPlay()
{
local vector RotX, RotY, RotZ, lPos;
Super.PostNetBeginPlay();
// Set up suspension graphics
GetAxes(Rotation,RotX,RotY,RotZ);
frontLeft.bHidden = True; // Invisible wheels for hovercrafts
frontRight.bHidden = True; // Invisible wheels for hovercrafts
rearLeft.bHidden = True; // Invisible wheels for hovercrafts
rearRight.bHidden = True; // Invisible wheels for hovercrafts
midLeft = spawn(MidTireClass, self,, Location + WheelMidAlong*RotX - WheelMidAcross*RotY + (256 /*WheelVert*/)*RotZ, Rotation);
midLeft.SetDrawScale3D(vect(1, 1, 1));
midLeft.bHidden = True; // Invisible wheels for hovercrafts
midRight = spawn(MidTireClass, self,, Location + WheelMidAlong*RotX - WheelMidAcross*RotY + (256 /*WheelVert*/)*RotZ, Rotation);
midRight.SetDrawScale3D(vect(1, -1, 1));
midRight.bHidden = True; // Invisible wheels for hovercrafts
lPos.X = WheelMidAlong;
lPos.Y = -WheelMidAcross;
midRight.WheelJoint = spawn(class'KCarWheelJoint', self);
midRight.WheelJoint.KPos1 = lPos/50;
midRight.WheelJoint.KPriAxis1 = vect(0, 0, 1);
midRight.WheelJoint.KSecAxis1 = vect(0, 1, 0);
midRight.WheelJoint.KConstraintActor1 = self;
midRight.WheelJoint.KPos2 = vect(0, 0, 0);
midRight.WheelJoint.KPriAxis2 = vect(0, 0, 1);
midRight.WheelJoint.KSecAxis2 = vect(0, 1, 0);
midRight.WheelJoint.KConstraintActor2 = midRight;
midRight.WheelJoint.SetPhysics(PHYS_Karma);
lPos.Y = WheelMidAcross;
midLeft.WheelJoint = spawn(class'KCarWheelJoint', self);
midLeft.WheelJoint.KPos1 = lPos/50;
midLeft.WheelJoint.KPriAxis1 = vect(0, 0, 1);
midLeft.WheelJoint.KSecAxis1 = vect(0, 1, 0);
midLeft.WheelJoint.KConstraintActor1 = self;
midLeft.WheelJoint.KPos2 = vect(0, 0, 0);
midLeft.WheelJoint.KPriAxis2 = vect(0, 0, 1);
midLeft.WheelJoint.KSecAxis2 = vect(0, 1, 0);
midLeft.WheelJoint.KConstraintActor2 = midLeft;
midLeft.WheelJoint.SetPhysics(PHYS_Karma);
// Initially make sure parameters are synced with Karma
KVehicleUpdateParams();
}
// Clean up wheels etc.
simulated event Destroyed()
{
// Destroy joints holding wheels to car
midLeft.WheelJoint.Destroy();
midRight.WheelJoint.Destroy();
// Destroy wheels themselves.
midLeft.Destroy();
midRight.Destroy();
Super.Destroyed();
}
// Call this if you change any parameters (tire, suspension etc.) and they
// will be passed down to each wheel/joint.
simulated event KVehicleUpdateParams()
{
Super.KVehicleUpdateParams();
midLeft.WheelJoint.bKSteeringLocked = true;
midRight.WheelJoint.bKSteeringLocked = true;
// unlock rear wheels for steering
rearLeft.WheelJoint.bKSteeringLocked = false;
rearLeft.WheelJoint.KProportionalGap = SteerPropGap;
rearLeft.WheelJoint.KMaxSteerTorque = SteerTorque;
rearLeft.WheelJoint.KMaxSteerSpeed = SteerSpeed;
// unlock rear wheels for steering
rearRight.WheelJoint.bKSteeringLocked = false;
rearRight.WheelJoint.KProportionalGap = SteerPropGap;
rearRight.WheelJoint.KMaxSteerTorque = SteerTorque;
rearRight.WheelJoint.KMaxSteerSpeed = SteerSpeed;
midLeft.WheelJoint.KSuspHighLimit = SuspHighLimit;
midLeft.WheelJoint.KSuspLowLimit = SuspLowLimit;
midLeft.WheelJoint.KSuspStiffness = SuspStiffness;
midLeft.WheelJoint.KSuspDamping = SuspDamping;
midRight.WheelJoint.KSuspHighLimit = SuspHighLimit;
midRight.WheelJoint.KSuspLowLimit = SuspLowLimit;
midRight.WheelJoint.KSuspStiffness = SuspStiffness;
midRight.WheelJoint.KSuspDamping = SuspDamping;
// Sync parameters with Karma - even front and rear wheels… Just incase. :P
frontLeft.WheelJoint.KUpdateConstraintParams();
frontRight.WheelJoint.KUpdateConstraintParams();
midLeft.WheelJoint.KUpdateConstraintParams();
midRight.WheelJoint.KUpdateConstraintParams();
rearLeft.WheelJoint.KUpdateConstraintParams();
rearRight.WheelJoint.KUpdateConstraintParams();
// Mass
frontLeft.KSetMass(TireMass+2); // maybe increase front wheels mass to simulte engine weight??
frontRight.KSetMass(TireMass+2); // maybe increase front wheels mass to simulte engine weight??
midLeft.KSetMass(TireMass);
midRight.KSetMass(TireMass);
midLeft.RollFriction = TireRollFriction;
midLeft.LateralFriction = TireLateralFriction;
midLeft.RollSlip = TireRollSlip;
midLeft.LateralSlip = TireLateralSlip;
midLeft.MinSlip = TireMinSlip;
midLeft.SlipRate = TireSlipRate;
midLeft.Softness = TireSoftness;
midLeft.Adhesion = TireAdhesion;
midLeft.Restitution = TireRestitution;
midRight.RollFriction = TireRollFriction;
midRight.LateralFriction = TireLateralFriction;
midRight.RollSlip = TireRollSlip;
midRight.LateralSlip = TireLateralSlip;
midRight.MinSlip = TireMinSlip;
midRight.SlipRate = TireSlipRate;
midRight.Softness = TireSoftness;
midRight.Adhesion = TireAdhesion;
midRight.Restitution = TireRestitution;
}
// Car Simulation
simulated function Tick(float Delta)
{
local float tana, sFactor;
Super.Tick(Delta);
WheelSpinSpeed = (frontLeft.SpinSpeed
+ frontRight.SpinSpeed
+ rearLeft.SpinSpeed
+ midLeft.SpinSpeed
+ midRight.SpinSpeed
+ rearRight.SpinSpeed)/6;
// Motor
// MID .. make them wheel spin :]
midLeft.WheelJoint.KMotorTorque = 0.5 * OutputTorque * TorqueSplit;
midRight.WheelJoint.KMotorTorque = 0.5 * OutputTorque * TorqueSplit;
// Braking
if(OutputBrake)
{
midLeft.WheelJoint.KBraking = MaxBrakeTorque;
midRight.WheelJoint.KBraking = MaxBrakeTorque;
}
else
{
midLeft.WheelJoint.KBraking = 0.0;
midRight.WheelJoint.KBraking = 0.0;
}
// Steering
tana = Tan(6.283/65536 * Steering * MaxSteerAngle);
sFactor = 0.5 * tana * (2 * WheelFrontAcross) / Abs(WheelFrontAlong - WheelRearAlong);
// Make rear wheel turn in opposite direction to turn properly: -65536/6.283
RearLeft.WheelJoint.KSteerAngle = -65536/6.283 * Atan(tana, (1-sFactor));
RearRight.WheelJoint.KSteerAngle = -65536/6.283 * Atan(tana, (1+sFactor));
// Handbrake
if(OutputHandbrakeOn == true)
{
midLeft.LateralFriction = TireLateralFriction + TireHandbrakeFriction;
midLeft.LateralSlip = TireLateralSlip + TireHandbrakeSlip;
midRight.LateralFriction = TireLateralFriction + TireHandbrakeFriction;
midRight.LateralSlip = TireLateralSlip + TireHandbrakeSlip;
}
else
{
midLeft.LateralFriction = TireLateralFriction;
midLeft.LateralSlip = TireLateralSlip;
midRight.LateralFriction = TireLateralFriction;
midRight.LateralSlip = TireLateralSlip;
}
}
defaultproperties
{
}