| Home Page | Recent Changes | Preferences

# Alternative Vehicle Control

This code has arisen from the need to have a vehicle where the player can still be seen and shot whilst in control of a vehicle (for the artillery and horses in the LawDogs mod). Instead of the player's original Pawn being hidden and the vehicle possessed, the original pawn is attached to the vehicle and the vehicle controlled indirectly.

This has the advantage not only of being able to kill the player without damaging the vehicle and vice versa, but also it allows the player to potentially fire their normal weapons whilst on the vehicle.

The code shown here has only the code relevant to this control method present; It is a long way from a complete vehicle. There is no bot support.

First the vehicle class:

    class LawDogsVehicle extends KVehicle
        abstract;

    var Pawn RepDriver; //Replicated 'Driver'.

    replication
    {
        reliable if(Role == ROLE_Authority)
            RepDriver;
    }

    simulated function ClientKDriverEnter(PlayerController pc)
    {
        PlayerController(RepDriver.Controller).bBehindView = bUseBehindView;
    }

    function KDriverEnter(Pawn p)
    {
        Driver = p;
        RepDriver = Driver;

        Driver.bCollideWorld = false;
        Driver.bPhysicsAnimUpdate = false;
        Driver.Velocity = vect(0,0,0);
        Driver.SetPhysics(PHYS_None);

        Driver.SetBase(self);
        Driver.SetRelativeLocation(DrivePos);
        Driver.SetRelativeRotation(rot(0, 0, 0));

        LawDogsPlayer(Driver.Controller).Vehicle = self;
        LawDogsPlayer(Driver.Controller).GotoState('PlayerRiding');
        SetOwner(Driver.Controller);

        ClientKDriverEnter(PlayerController(Driver.Controller));
    }

    simulated function ClientKDriverLeave(PlayerController pc)
    {
        pc.bBehindView = pc.Default.bBehindView;
    }

    function bool KDriverLeave(bool bForceLeave)
    {
        local int i;
        local bool havePlaced;
        local vector HitLocation, HitNormal, tryPlace;

        if(Driver == None)
            return false;

        Driver.bCollideWorld = true;

        for(i = 0; i < ExitPositions.Length && havePlaced == false; i++)
        {
            tryPlace = Location + (ExitPositions[i] >> Rotation);

            if(Trace(HitLocation, HitNormal, tryPlace, Location, false) != None)
                continue;

            if(!Driver.SetLocation(tryPlace))
                continue;

            havePlaced = true;
        }

        if(!havePlaced && !bForceLeave)
        {
            Driver.bCollideWorld = false;
            return false;
        }

        Driver.PlayWaiting();
        Driver.bPhysicsAnimUpdate = Driver.Default.bPhysicsAnimUpdate;
        Driver.SetBase(None);
        Driver.Acceleration = vect(0, 0, 24000);

        if(Driver.TouchingWaterVolume())
            Driver.SetPhysics(PHYS_Swimming);
        else
            Driver.SetPhysics(PHYS_Falling);

        Driver.Controller.Restart();
        LawDogsPlayer(Driver.Controller).Vehicle = None;
        ClientKDriverLeave(PlayerController(Driver.Controller));

        Driver = None;
        RepDriver = None;
        SetOwner(None);

        Throttle=0;
        Steering=0;

        return true;
    }

    function TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<DamageType> damageType)
    {
        Health -= Damage;

        if(Health <= 0)
        {
            //Make sure driver leaves when vehicle is destroyed.
            if(Driver != None)
                KDriverLeave(true);

            Destroy();
        }
    }

    simulated function Tick(float DeltaTime)
    {
        local rotator DriverRot;

        if(RepDriver != None)
        {
            //So taking damage doesn't make the driver start falling.
            if(RepDriver.Physics != PHYS_None)
                RepDriver.SetPhysics(PHYS_None);

            //Make very sure the driver stays still.
            RepDriver.SetRelativeLocation(DrivePos);
            DriverRot.Yaw = RepDriver.RelativeRotation.Yaw;
            RepDriver.SetRelativeRotation(DriverRot);
        }
    }

    //Allow camera to rotate relative to the vehicle, within limits.
    simulated function bool SpecialCalcView(out actor ViewActor, out vector CameraLocation, out rotator CameraRotation)
    {
        if(RepDriver == None || RepDriver.Controller == None)
            return false;

        ViewActor = RepDriver;

        if(CameraRotation.Yaw ClockwiseFrom (Rotation.Yaw + MaxYaw))
            CameraRotation.Yaw = Rotation.Yaw + MaxYaw;
        else if(!(CameraRotation.Yaw ClockwiseFrom (Rotation.Yaw + MinYaw)))
            CameraRotation.Yaw = Rotation.Yaw + MinYaw;

        CameraRotation = normalize(CameraRotation);
        RepDriver.SetTwistLook(normalize(CameraRotation - Rotation).Yaw, CameraRotation.Pitch);

        RepDriver.Controller.SetRotation(CameraRotation);
        CameraLocation = RepDriver.Location + RepDriver.EyePosition();

        if(PlayerController(RepDriver.Controller).bBehindView)
            CameraLocation += BehindViewOffset >> Rotation;

        return true;
    }

And the new stuff for the PlayerController class:

    class LawDogsPlayer extends xPlayer
        config(user);

    var KVehicle Vehicle;

    replication
    {
        reliable if(Role == ROLE_Authority)
            Vehicle;
    }

    //Fix dying whilst controling a vehicle.
    function WasKilledBy(Controller Other)
    {
        if(Vehicle != None)
            Vehicle.KDriverLeave(true);

        Super.WasKilledBy(Other);
    }

    //Allow vehicle to handle camera.
    event PlayerCalcView(out actor ViewActor, out vector CameraLocation, out rotator CameraRotation)
    {
        if(Vehicle != None && Vehicle.bSpecialCalcView && Vehicle.SpecialCalcView(ViewActor, CameraLocation, CameraRotation))
            return;

        Super.PlayerCalcView(ViewActor, CameraLocation, CameraRotation);
    }

    //Using a LawDogsVehicle.
    state PlayerRiding
    {
    ignores SeePlayer, HearNoise, Bump;

        function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot){}

        //Pressing 'use' will exit vehicle (to free up the jump key for other purposes).
        function ServerUse()
        {
            if(Level.Pauser == PlayerReplicationInfo)
            {
                SetPause(false);
                return;
            }

            if(Pawn == None)
                return;

            if(Vehicle != None)
                Vehicle.bGetOut = true;
        }

        exec function Fire(optional float F)
        {
            if(Vehicle != None)
            {
                Vehicle.VehicleFire(false);
                Vehicle.bVehicleIsFiring = true;
            }
        }

        exec function AltFire(optional float F)
        {
            if(Vehicle != None)
            {
                Vehicle.VehicleFire(true);
                Vehicle.bVehicleIsAltFiring = true;
            }
        }

        // Set the throttle, steering etc. for the vehicle based on the input provided
        function ProcessDrive(float InForward, float InStrafe, bool InJump)
        {
            if(Vehicle == None)
            {
                log("PlayerRiding.PlayerMove: No Vehicle");
                return;
            }

            //Horses can jump.
            if(bPressedJump)
            {
                Vehicle.DoJump(bUpdating);
                bPressedJump = false;
            }

            if(InForward > 1)
                Vehicle.Throttle = 1;
            else if(InForward < -1)
                Vehicle.Throttle = -1;
            else
                Vehicle.Throttle = 0;

            if(InStrafe < -1)
                Vehicle.Steering = -1;
            else if(InStrafe > 1)
                Vehicle.Steering = 1;
            else
                Vehicle.Steering = 0;
        }

        function PlayerMove(float DeltaTime)
        {
            //Only servers can actually do the driving logic.
            if(Role < ROLE_Authority)
                ServerDrive(aForward, aStrafe, bPressedJump);
            else
                ProcessDrive(aForward, aStrafe, bPressedJump);

            if(Vehicle != None)
            {
                if(bFire == 0 && Vehicle.bVehicleIsFiring)
                {
                    Vehicle.VehicleCeaseFire(false);
                    Vehicle.bVehicleIsFiring = false;
                }

                if(bAltFire == 0 && Vehicle.bVehicleIsAltFiring)
                {
                    Vehicle.VehicleCeaseFire(true);
                    Vehicle.bVehicleIsAltFiring = false;
                }
            }

            //update 'looking' rotation - no affect on driving
            UpdateRotation(DeltaTime, 2);
        }

        function BeginState()
        {
            CleanOutSavedMoves();
        }

        function EndState()
        {
            CleanOutSavedMoves();
        }
    }

One thing that you may find necessary but which is not shown here is that to stop a player from firing their own weapons, you need to give them a non-functional dummy weapon whilst they are in control of the vehicle.

Another potential problem is that the player's pawn can encroach on other pawns whilst it is attached to the vehicle, telefragging them instantly. This needs to be fixed in the EncroachedBy function in the pawn.

Foxpaw: From what I've heard, this is already part of the vehicle code for UT2004. But I haven't played UT2004 so I can't say that with certainty.

Mr Evil: That's what I've heard too, but I wanted it now, so I did it.

Category Custom Class

The Unreal Engine Documentation Site

Wiki Community

Topic Categories

Image Uploads

Random Page

Recent Changes

Offline Wiki

Unreal Engine

Console Commands

Terminology

Mapping Topics

Mapping Lessons

UnrealEd Interface

Questions&Answers

Scripting Topics

Scripting Lessons

Making Mods

Class Tree

Questions&Answers

Modeling Topics

Questions&Answers

Log In