Legacy:KHoverTank
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 :]
<uscript> //============================================================================= // 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 { } </uscript>