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