The three virtues of a programmer: Laziness, Impatience, and Hubris. – Larry Wall

Legacy:Vehicles Pre2004/Driver Setup

From Unreal Wiki, The Unreal Engine Documentation Site
< Legacy:Vehicles Pre2004
Revision as of 06:48, 17 November 2007 by Dyn-62-56-58-176.dslaccess.co.uk (Talk) (Moved)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Setting up the driver position/animation.[edit]

For an alternate driver setup position code, please go to Legacy:Vehicles Pre2004/Driver Setup Alternative

First of all, when you have any vehicle, you might want to have the driver visible. To make sure he's visible, use this variable in your vehicle class' DefaultProperties:

bDrawDriverInTP=True

Then, you have to set his position. Use a vector for this. In the same DefaultProperties code, use:

DrivePos=(X=-18.0,Y=-10.0,Z=99.000000)

Of course, replace the position values by the values you want to use. Although positioning in your modeling application of preference and grabbing the coordinates from there might help, nothing beats testing the position in game, so be sure to test. This is the position of the root bone of your player's skeleton.

Third, and most important in some cases: the above code is enough to have your driver/player sitting nice on a certain position, but depending on your vehicle, the default position is not really what you want. You'll notice, perhaps, that your vehicle class' superclass have a variable that defines the animation (position) that's used to set the driving position. This variable is also stored in the DefaultProperties block, and you can easily add it to your own vehicle and change it to whatever you like. This is the default code:

DriveAnim="Vehicle_Driving"

So you can change the value between quotes to the name of the animation (position) you want to use. This can be easily found on UnrealEd, on the animations browser; just select the mesh you'll be using (for example, MercFemaleA) and the list of available animations and poses will be shown on the left side list.

However, on most cases, this won't suffice since there isn't much to work with. If you need a pose that shows your player riding a bike with his legs a bit more apart, you'll have to make the pose yourself. In this case, there are two alternatives you can work with.

The first one is to create a customized animation (pose) for the player bones; you can then paste then on UnrealEd and append them to the mesh you'll be using. This can be applied to your own customized character meshes, but I don't know how you can apply them to the built-in character meshes and still use them as pure packages without replicating the whole stuff. (I'd appreciate if anyone would shed a light on this)

The second one, which is easy and hard at the same time, is to apply rotation to the right bones to get your model on the position you want it to be. Easy, because you just need a bit of easily modifiable code; and hard, because you'll have to enter the rotating values all by yourself, and this may take a bit of testing to get it right. This is the case I'll be talking about here, since I've dealt with it for a while (story here) and I think there's something I can share.

You need to know, then, that all your player bones can be changed - rotated, scaled - by using simple commands. This page on UDN goes into great detail into that and talks about the command you have to use to deal with bone rotation: SetBoneRotation(). You just need them to check the Bone Names list and you're ready to go.

Here's what the code has to do: when the driver enters the vehicle, change his animation (position) to the default "Vehicle_Driving" position (already done by the vehicle classes). Work from that and rotate some bones until you reach the desired position. Then, wait until he leaves the car; when he does that, reset the bones to the correct rotation.

This is accomplished by using the KDriverEnter() and KDriverLeave() functions present in vehicle classes. But first of all, let's working with setting the player position. Let's suppose I made a lot of playtesting, and then I already had the code to reset to my driving position. This is it then:

function SetDriverPositionReset() {
    Driver.SetBoneRotation('Bip01 Neck', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 Neck1', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Dummy07', rot(0, 0, 0), 1.0);
 
    Driver.SetBoneRotation('Bip01 L Clavicle', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Dummy03', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Finger1', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Finger11', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Finger12', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Dummy04', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bone_L_shoulder', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Dummy08', rot(0, 0, 0), 1.0);
 
    Driver.SetBoneRotation('Bip01 R Clavicle', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Dummy01', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Finger1', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Finger11', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Finger12', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Dummy02', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bone_R_shoulder', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Dummy09', rot(0, 0, 0), 1.0);
 
    Driver.SetBoneRotation('Dummy06', rot(0, 0, 0), 1);
    Driver.SetBoneRotation('Dummy05', rot(0, 0, 0), 1);
 
    // Pelvis --------------------------------------------------
    Driver.SetBoneRotation('Bip01', rot(-1000, 13000, 0), 1.0);
    Driver.SetBoneRotation('Bip01 Pelvis', rot(0, 2000, 0), 1.0);
 
    // Head ----------------------------------------------------
    Driver.SetBoneRotation('Bip01 Head', rot(-3000, 0, -8000), 1.0);
 
    // Spine ---------------------------------------------------
    Driver.SetBoneRotation('Bip01 Spine', rot(0, 6000, -1000), 1.0);
    Driver.SetBoneRotation('Bip01 Spine1', rot(0, 0, -1000), 1.0);
    Driver.SetBoneRotation('Bip01 Spine2', rot(0, 0, -1000), 1.0);
 
    // Arms ----------------------------------------------------
    Driver.SetBoneRotation('Bip01 L UpperArm', rot(-6000, 9000, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R UpperArm', rot(6000, 9000, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Forearm', rot(-3000, -4000, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Forearm', rot(3000, -4000, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Hand', rot(0, -3500, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Hand', rot(0, -3500, 0), 1.0);
 
    // Fingers -------------------------------------------------
    Driver.SetBoneRotation('Bip01 L Finger0', rot(0, -10000, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Finger01', rot(0, -10000, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Finger02', rot(0, -10000, 0), 1.0);
 
    Driver.SetBoneRotation('Bip01 R Finger0', rot(0, -10000, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Finger01', rot(0, -10000, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Finger02', rot(0, -10000, 0), 1.0);
 
    // Legs ----------------------------------------------------
    Driver.SetBoneRotation('Bip01 L Thigh', rot(8500, -5200, -3000), 1.0);
    Driver.SetBoneRotation('Bip01 R Thigh', rot(-8500, -5200, -3000), 1.0);
    Driver.SetBoneRotation('Bip01 L Calf', rot(0, -10000, -8000), 1.0);
    Driver.SetBoneRotation('Bip01 R Calf', rot(0, -10000, 8000), 1.0);
    Driver.SetBoneRotation('Bip01 L Foot', rot(-6000, 15000, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Foot', rot(5000, 15000, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Toe0', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Toe0', rot(0, 0, 0), 1.0);
}

YES, the code is big, but that's how it's meant to be - there are a lot of bones.

Well, this is the function that will get the driver from the Vehicle_Driving position and set him on my desired position - on my case, riding side-first on a surfboard with his legs and arms apart. On simple modifications - for example, getting his knees bended or his hands far apart - the code would be really small – you just need to rotate the right bones. For clarification purposes, I've pasted even code that doesn't need to appear (the ones that use rot(0,0,0) as rotator modifiers) just so it's easy to change.

Onto explaining the code: this code gets bone by bone and changes it rotation values by any value that's used on the rotator - the rot() constructor takes Pitch, Yaw and Roll as parameters. These values are related to the bone position, not the world position, so depending on the case, the pitch value will actually be rolling the limbs. So, like I said, be sure to test it a lot.

Next, we need a function that'll get the driver back to the original position - or else he'll be all fuzzy when he leaves the vehicle. This function is easy, since we only need to set the rotation values back to 0. The code:

function SetDriverPositionOriginal(Pawn Driver) {
    // After the player leaves the vehicle, resets its additional bone rotations or else he'll be all twisted
    // Simply resets everything
    Driver.SetBoneRotation('Bip01 Neck', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 Neck1', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Dummy07', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Clavicle', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Dummy03', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Finger1', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Finger11', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Finger12', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Dummy04', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bone_L_shoulder', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Dummy08', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Clavicle', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Dummy01', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Finger1', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Finger11', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Finger12', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Dummy02', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bone_R_shoulder', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Dummy09', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Dummy06', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Dummy05', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 Pelvis', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 Head', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 Spine', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 Spine1', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 Spine2', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L UpperArm', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R UpperArm', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Forearm', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Forearm', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Hand', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Hand', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Finger0', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Finger01', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Finger02', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Finger0', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Finger01', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Finger02', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Thigh', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Thigh', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Calf', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Calf', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Foot', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Foot', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 L Toe0', rot(0, 0, 0), 1.0);
    Driver.SetBoneRotation('Bip01 R Toe0', rot(0, 0, 0), 1.0);
}

Of course, using an in-line created string array and setting all values to rot(0,0,0) on a for loop would be the right thing, but I don't know how to create an in-line array in UnrealScript and the first parameter (between single quotes) doesn't seem to be a string parameter. What gives? If someone could rewrite this in four lines (sans local var declarations) I'd be thankful.

// After the player leaves the vehicle, resets its additional bone rotations or else he'll be all twisted
function SetDriverPositionOriginal(Pawn Driver) {
    /* Dynamic array of names
     *  note: name uses ', string uses "
     *  note: dynamic arrays can't be replicated
     */
    local array<name> BoneNames;
    // Other variables
    local rotator ZeroRot;
    local int i;
 
    /* This is a hack, read below.
     * Also, string is split and concatenated with $
     * to improve readability, for speed you'd probably
     * want to go for one very long line
     */
    SetPropertyText("BoneNames", "("$
        "'Bip01 Neck','Bip01 Neck1','Dummy07','Bip01 L Clavicle',"$
        "'Dummy03','Bip01 L Finger1','Bip01 L Finger11','Bip01 L Finger12','Dummy04',"$
        "'Bone_L_shoulder','Dummy08','Bip01 R Clavicle','Dummy01','Bip01 R Finger1',"$
        "'Bip01 R Finger11','Bip01 R Finger12','Dummy02','Bone_R_shoulder','Dummy09',"$
        "'Dummy06','Dummy05','Bip01','Bip01 Pelvis','Bip01 Head','Bip01 Spine',"$
        "'Bip01 Spine1','Bip01 Spine2','Bip01 L UpperArm','Bip01 R UpperArm','Bip01 L Forearm',"$
        "'Bip01 R Forearm','Bip01 L Hand','Bip01 R Hand','Bip01 L Finger0','Bip01 L Finger01',"$
        "'Bip01 L Finger02','Bip01 R Finger0','Bip01 R Finger01','Bip01 R Finger02',"$
        "'Bip01 L Thigh','Bip01 R Thigh','Bip01 L Calf','Bip01 R Calf','Bip01 L Foot',"$
        "'Bip01 R Foot','Bip01 L Toe0','Bip01 R Toe0'"$
        ")");
    // Set ZeroRot to prevent repeated creation of the same rotator
    ZeroRot = rot(0, 0, 0);
 
    // And finally, run through the loop resetting each bone (specified above)
    for (i=0; i<BoneNames.Length; i++)
         Driver.SetBoneRotation(BoneNames[i], ZeroRot, 1.0);
}

I'm not exactly sure that this is the best way to create an inline array, but its the best way I could think of at the moment. I would recommend not using SetPropertyText to change a name variable (or an array of names). See the "String [...] to Name" section of Typecasting for more information.

This will do the same thing the previous function does, but it'll set the rotation values back to 0, that is, with no offsets - making it look like "Vehicle_Driving" again.

Next, you have to call the functions on your vehicle code - just edit KDriverEnter() and KDriverLeave() and make it call the functions when needed. This is the code (supposing you don't already have these functions on your class, or that they're just general-purpose ones):

function KDriverEnter (Pawn P) {
    Super.KDriverEnter(P); // cals the normal function
    SetDriverPositionReset(); // resets the position to my driving position
}
function bool KDriverLeave (bool bForceLeave) {
    local Pawn tempDriver;
    tempDriver = Driver; // use variable to hold Driver because Driver is destroyed after KDriverLeave
    if (Super.KDriverLeave(bForceLeave)) {
        SetDriverPositionOriginal(tempDriver); // resets the position to the original position only if he could leave
        return true;
    } else {
        return false;
    }
}

That's pretty much it. Using similar code, you can work with your model's Tick() function to set your bone rotation to specific values - the Unreal Tournament 2004 code already does this, to set the rotation of the head bone to a given value to make the driver it look like the driver is looking at the side you're facing. But you can work with all bones, making him rotate his arms or legs too; In my Spaceboard's case, I made the code change lots of the player's bones, so it'd look like he was jugging his body to the sides or twisting it for balance. It's a pretty nice effect if you ask me. For reference, here's the piece of code I'm using inside my Tick() on my Spaceboard Class (it's based on the bone-updating code):

    local float tempYaw, tempRoll; // contador pra calcular a quantidade de rotacao que serah aplicada
    local rotator rott; // rotacionador pra conter a nova rotacao
 
    Super.Tick (DeltaTime);
 
    // (..do other stuff here..)
 
    if ( Level.NetMode != NM_DedicatedServer && !bDropDetail && bAdjustDriversHead && bDrawDriverinTP && (Driver != None) && (Driver.HeadBone != '')) {
        tempYaw = (DriverViewYaw - Driver.Rotation.Yaw) & 65535;
        tempRoll = Driver.Rotation.Roll * -1;
        if (tempYaw > 32767) tempYaw = (65535 - tempYaw)*-1;
 
        rott.Pitch = -3000;
        rott.Roll = (tempYaw/6)-8000; // -8000;
        rott.Yaw = 0;
        Driver.SetBoneRotation('Bip01 Head', rott, 1.0);
        rott.Pitch = 0;
        rott.Roll = (tempYaw/6)-1000;
        rott.Yaw = (tempRoll/2) + 6000;
        Driver.SetBoneRotation('Bip01 Spine', rott, 1.0);
        rott.Pitch = 0;
        rott.Roll = (tempYaw/6)-1000;
        rott.Yaw = tempRoll/2;
        Driver.SetBoneRotation('Bip01 Spine1', rott, 1.0);
        rott.Pitch = 0;
        rott.Roll = (tempYaw/6)-1000;
        rott.Yaw = tempRoll/2;
        Driver.SetBoneRotation('Bip01 Spine2', rott, 1.0);
 
        rott.Pitch = 0;
        rott.Roll = tempYaw/6;
        rott.Yaw = (tempYaw/6)*-1;
        Driver.SetBoneRotation('Bip01 L Clavicle', rott, 1.0);
        rott.Pitch = 0;
        rott.Roll = tempYaw/6;
        rott.Yaw = tempYaw/6;
        Driver.SetBoneRotation('Bip01 R Clavicle', rott, 1.0);
 
        rott.Pitch = -6000;
        rott.Roll = 0;
        rott.Yaw = 9000-tempRoll;
        Driver.SetBoneRotation('Bip01 L UpperArm', rott, 1.0);
        rott.Pitch = 6000;
        rott.Roll = 0;
        rott.Yaw = 9000-tempRoll;
        Driver.SetBoneRotation('Bip01 R UpperArm', rott, 1.0);
 
    }

Easy and powerful. :D