Legacy:KHoverTank

From Unreal Wiki, The Unreal Engine Documentation Site
UT2003 :: Actor >> Pawn >> KVehicle >> KCar >> 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
{
}