| Home Page | Recent Changes

KHoverTank

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
{
}

Category Custom Class

The Unreal Engine Documentation Site

Wiki Community

Topic Categories

Recent Changes

Offline Wiki

Unreal Engine

Console Commands

Terminology

FAQs

Help Desk

Mapping Topics

Mapping Lessons

UnrealEd Interface

UnrealScript Topics

UnrealScript Lessons

Making Mods

Class Tree

Modeling Topics

Chongqing Page

Log In