UDK 载具基础

Cars, planes, tanks, hoverboards. All of these, in Unreal terms are vehicles. 

Like many other systems in the Unreal Engine, you’ll find several flavors of Vehicles (the Engine, UDK, UT, etc.). 

However, unlike the other systems, there are more arguments in favor of using the base classes rather than the most extended ones, depending on what you want to do.

In this article I will talk about the basics common to all types of vehicles, expand a bit on ground vehicles. 

A few things won’t be covered here (I’ll save them for some other day):

  • Multi-seat vehicles
  • How to make hover vehicles look nice (behaviour-wise)
  • Flying vehicles
  • Applying visual damage to the vehicle.

As usual, here’s the UDN documentation you need to read before going further in this article:

Vehicles and PlayerController

One important thing to know about vehicles is that they are a subclass of Pawn. As such, 

they share much of their behaviour and their relationship to the Player Controller. 

For instance, you can set your GameInfo’s default pawn to be a vehicle (particularly interesting if you’re making a racing game).

Also, Pawn.LandMovementState will tell the PlayerController which state it should run when touching the ground.

 For a normal pawn it’s PlayerWalking, for a vehicle it’s PlayerDriving (this is already set in Vehicle.uc’s default properties).

The main difference is in the PlayerMove() function which is implemented differently. On PlayerWalking, 

it will call ProcessMove()/ServerMove(), while on PlayerDriving it will call ProcessDrive()/ServerDrive(). 

These functions don’t handle input the same way, allowing for instance the joystick to adjust throttle analogically,

rather than being treated as a normal button for player walking.

Vehicle class

That’s the base class for all vehicles, which extends from Pawn as we said earlier. It introduces the concept 

of driver (with entry and exit) and has some additional functions and properties for handling player vs vehicle damage. 

You’ll use it as a base if you don’t need complex physics simulation for your vehicle. If you don’t need driver 

management or player/vehicle damage either, you might be better off extending from Pawn even if you control a vehicle-like avatar.

Also, remember that being a Pawn, a Vehicle can have an Inventory Manager as well as a weapon. As such

 everything you already know about giving weapons to characters applies to vehicles as well.

Vehicle.Driver

This variable holds the reference to the Pawn who’s driving the Vehicle (mainly in order to restore it when 

the player leaves the vehicle). If the game’s default Pawn is a Vehicle, this variable’s value will be None. 

You need to keep that in mind if you’re making a game in which you’ll never leave your vehicle.

Entry/Exit

One of the first things to care about when creating your custom vehicle is to set its default behaviour regarding vehicle entry and exit.

 That’s because the default implementation has one problem: if the vehicle is your game’s default pawn, you can still leave the vehicle,

but the game won’t know what pawn to possess once you’ve left the vehicle which leads to odd behaviour.

By default, the entry/exit logic is performed by the “use” game bindable action. The logic is simple: if the controller’s pawn is a vehicle, 

try to leave it (Vehicle.DriverLeave()). If it’s not, try to enter a nearby vehicle(PlayerController.FindVehicleToDrive()).

DriverLeave() calls GameInfo.CanLeaveVehicle() which decides if the driver can leave the vehicle (remember what I said earlier about the driver).

 If you make a racing game for instance, you need to make sure that this function always return false (or simply remove the call to that function). 

At the end of DriverLeave, Pawn.StopDriving() is called (on the driver), which reset the physics, the attachment and some other variables.

FindVehicleToDrive() is poking around in the vicinity of the pawn to see if there is a vehicle, and if so try to enter it by calling Vehicle.TryToDrive().

That function checks if we can actually enter the Vehicle. If that’s the case, the PlayerController will unpossess the pawn, set it as the vehicle’s Driver, 

possess the Vehicle and set the correct state on the PlayerController. It then calls Pawn.StartDriving() (on the driver) which does the opposite of StopDriving().

One thing quite important is to make sure your vehicle’s mesh has its collisions properly set. Otherwise, 

the FindVehicleToDrive() function will fail to find your vehicle and you won’t be able to enter it.

Movement

The Vehicle class has 3 float variables related to movement: Throttle, Steering and Rise. 

These are set by the Vehicle.SetInputs() function which (by default) is called by PlayerController.

ProcessDrive(). In the default implementation, the value are assigned as follows:

  • Throttle gets the value of PlayerInput.RawJoyUp
  • Steering gets the value of PlayerInput.RawJoyRight
  • Rise gets the value of PlayerInput.aUp

Then, how to use these values to actually make your vehicle move is entirely up to you. 

You’ll probably play around with Rotation, Location, Velocity and so on (in the latter case, remember to give the Vehicle the physics mode you need). 

Below is a small example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class SandboxCarDefault extends Vehicle
     placeable ;
 
simulated function SetInputs( float InForward, float InStrafe, float InUp)
{
     local Rotator rot;
 
     super .SetInputs(InForward, InStrafe, InUp); // This sets Throttle, Steering and Rise
     //Simple movement implementation
     rot = Rotation;
     rot.Yaw = rot.Yaw - Steering* 256 ;
     SetRotation(rot);
     Velocity = Throttle* 256 *Vector(rot);
}
 
DefaultProperties
{
     begin object class =SkeletalMeshComponent Name=CarMesh
         SkeletalMesh=SkeletalMesh 'SandboxContent.Meshes.SK_VH_SandboxCar'
         PhysicsAsset=PhysicsAsset 'SandboxContent.Meshes.SK_VH_SandboxCar_Physics'
         BlockNonZeroExtent= true
         BlockZeroExtent= true
         BlockActors= true
         CollideActors= true
     end object
 
     Mesh=CarMesh
 
     Components.Add(CarMesh)
     Components.Remove(CollisionCylinder) //Removing it altogher as we'll be using the mesh's physics asets for collision.
     CollisionComponent=CarMesh
     Physics=PHYS_Flying
}
SVehicle class

This class introduces physics simulation for vehicles. And as usual, when physics simulation is involved, 

it looks good, but even simple things can become ridiculously complicated to achieve. Unless we know what we’re doing (and then again, it only goes from obscure to complex).

This class also implements the ability to get upside down vehicles upright and/or to keep them upright while they move about.

SVehicle and Skeletal Mesh setup

The UDN documentation explains well enough how to create Vehicle skeletal meshes, but I’ll throw in some additional tips.

First and foremost, while the Vehicle class uses the mesh’s orientation to determine the vehicle’s default orientation, 

SVehicle uses the root bone’s orientation. As the default orientation is used to figure out whether the vehicle is upright or not,

 it’s quite important to get it right.

Also, be advised that the SVehicle does need physical wheels at all to function (i.e. wheel meshes).

 As such, setting the AnimTree up is actually optional (as far as the vehicle’s movement behaviour is concerned). 

That said, it’s kind of an obvious and easy way of visualizing what’s happening with the physics simulation.

Note that SVehicle already defines a SkeletalMeshComponent sub-object in its default properties. It’s called SVehicleMesh.

 It sets some collision-related properties. You only have to override it to specify the mesh you’re using, as shown below:

1
2
3
4
5
Begin Object Name=SVehicleMesh
     SkeletalMesh=SkeletalMesh 'SandboxContent.Meshes.SK_VH_SandboxCar'
     PhysicsAsset=PhysicsAsset 'SandboxContent.Meshes.SK_VH_SandboxCar_Physics'
     AnimTreeTemplate( 0 )=AnimTree 'SandboxContent.Meshes.SK_VH_SandboxCar_AnimTree'
end object
Vehicle simulation objects (SVehicleSimBase and children)

That’s the class that will do the physics simulation for your vehicle. There are quite a few, 

in different flavours (Engine and UDK) and these are the kind of UnrealScript classes I hate,

 because everything is done in native code, so there isn’t much left to study. To be honest,

 I gave up trying to figure out how all of them worked and stuck to the ones that worked easily. 

But basically, the SVehicleSim* classes don’t do anything with the Throttle,

 Steering and Rise properties (you’d have to use those values yourself), while UDKVehicleSim* classes do.

Wheels and suspension

Unless you’re making a flying vehicle, your vehicle will have wheels. Because when there’s a wheel, 

there’s a can :D But more importantly, there’s suspension.

Even hovering vehicles have wheels (The Manta has three), as they’re using specifically tuned suspension to simulate the hovering. 

However, If you don’t have visible wheels, you don’t need to set up bones for them. As we can see on the Manta’s code, 

the wheel’s BoneOffset property allows you to nudge the actual position of the wheel object (coordinates expressed in the bone’s frame).

There are two sets of settings for suspension. The first one for is vehicle-wide ( i.e. shared by all wheels), stored in the vehicle simulation object:

  • float WheelSuspensionStiffness
  • float WheelSuspensionDamping

They should be self explanatory if you know how car suspension works. If you don’t, Google is your friend.

The second set of settings is per-wheel:

  • float SuspensionTravel

Now, the position of the wheel you specify (with the position of the bone and/or with the BoneOffset property) 

corresponds to the full extension of the suspension (i.e. when the wheels are not touching the ground). Then,

 depending on the car’s mass and the suspension’s stiffness and damping, the spring will compress until it reaches equilibrium or SuspensionTravel.

You can get a wheel’s current suspension’s position with SVehicleWheel.SuspensionPosition. When it’s equal to 0,

 it means the suspension is fully extended (most likely, the wheel isn’t touching the ground). Obivously, if it’s equal to SuspensionTravel, the suspension is quite useless.

Also, when setting your wheels up, don’t forget set the wheel’s radius (to match the graphical wheel if there is one): SVehicleWheel.WheelRadius.

Movement

There are several ways to make physics-driven vehicle move.

The easiest one is to use the AddForce(), AddImpulse() and AddTorque(). You probably want to do that in the SetInputs() function. 

Basically you’re just pushing the vehicle (it seems the force is applied at the vehicle’s center of mass (which defaults to the root bone’s position)). 

That’s fine for hovering and flying vehicles.

However, this won’t do for wheeled vehicles, as you’d want the wheels to be driven by the engine and then they’ll be tracting/pushing the entire vehicle.

This is done kind of automatically by setting the Throttle and Steering properties (which is already done in the default implementation of SetInputs()).

 How these values are handled depend on which simulation object class you are using.

Vehicle uprighting

Sometimes your vehicle may end up upside down, because of a stunt gone wrong, or upon receiving a physics impulse. 

The default implementation of SVehicle.TryToDrive() checks if the vehicle is in such a state. 

If it is and SVehicle.bCanFlip is true, instead of entering the vehicle, two forces will be applied to the vehicle in an attempt to put it back on its wheels.

A linear force will be applied along the world’s Z axis (upwards) to lift the vehicle, with a strength of SVehicle.UprightLiftStrength.

At the same time, some torque will be applied to rotate the vehicle around its own X axis. The strenght of that torque is defined by SVehicle.UprightTorqueStrength.

Both these forces will be applied during SVehicle.UprightTime.

There’s no magic formula to find the right value, they mostly depend on the vehicle’s mass,

 which is calculated  from the volume of the chassis’ body (in PhAT). Here are the values for my sample vehicle:

1
2
3
UprightLiftStrength = 210.0 ;
UprightTorqueStrength = 30.0 ;
UprightTime = 1.0 ;

with the following results:

Wheeled vehicles

This kind of vehicle uses the UDKVehicleSimCar simulation object. The wheels are instances of SVehicleWheel (or children).

Throttle

Making a UDKVehicleSimCar go forward or backwards works the following way:

Some torque is applied to the wheels that have SVehicleWheel.bPoweredWheel set to true. 

The amount of torque applied depends on SVehicle.Throttle‘s value and the vehicle’s current speed.

 A maximum speed is achieved by reducing the maximum torque than can be applied to the 

wheels when reaching a given speed. This is done by the UDKVehicleSimCar.TorqueVSpeedCurve property (look at the one in UT’s Scorpion).

If you release the accelerator while the vehicle is moving, engine brake slows the vehicle down 

by applying reversed torque to the powered wheels. The amount of engine brake applied is set by UDKVehicleSimCar.EngineBrakeFactor.

If you apply negative throttle while moving forward, you’ll be braking. Barking power is defined by UDKVehicleSimCar.MaxBrakeTorque.

Steering

When you set SVehicle.Steering, UDKVehicleSimCar will rotate all wheels according to their SteerFactor.

 The speed at which the wheels steer is defined by UDKVehicleSimBase.SteerSpeed. The maximum 

steering angle is defined by UDKVehicleSimCar.MaxSteerAngleCurve, which is depending on the vehicle’s Velocity.

On the Scorpion, the faster you go, the lower your max steering angle is. This is in order make the vehicle

 easier to control, since in real life, we tend to turn the steering wheel a lot less at high speeds. On a 

gamepad however, the travel of a joystick is much shorter, so we need to cheat a little.

UDKVehicleSimCar allows for some air control in order to turn the car when none of its wheels touch the ground. 

This is handled by UDKVehicleSimCar.AirControlTurnTorque (set to 0 to disable).

Sample vehicle

Below is the code of a sample wheeled vehicle I’ve made to conduct my tests and a video of how it behaves.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class SandboxPhysicsCar extends SVehicle;
 
DefaultProperties
{
 
     COMOffset=(x= 0.0 ,y=- 5.0 ,z= 0.0 )
 
     Begin Object Name=SVehicleMesh
         SkeletalMesh=SkeletalMesh 'SandboxContent.Meshes.SK_VH_SandboxCar'
         PhysicsAsset=PhysicsAsset 'SandboxContent.Meshes.SK_VH_SandboxCar_Physics'
         AnimTreeTemplate( 0 )=AnimTree 'SandboxContent.Meshes.SK_VH_SandboxCar_AnimTree'
     end object
 
     Begin Object Name=CollisionCylinder //We could also remove it
         BlockNonZeroExtent= false
         BlockZeroExtent= false
         BlockActors= false
         BlockRigidBody= false
         CollideActors= false
     End Object
 
     Begin Object Class=UDKVehicleSimCar Name=SimulationObject
         bClampedFrictionModel= true
         WheelSuspensionStiffness= 15
         WheelSuspensionDamping= 2.0
         WheelSuspensionBias= 0.0
         MaxSteerAngleCurve=(Points=((InVal= 0 ,OutVal= 45 ),(InVal= 600.0 ,OutVal= 45.0 ),
(InVal= 1100.0 ,OutVal= 45.0 ),(InVal= 1300.0 ,OutVal= 45.0 ),(InVal= 1600.0 ,OutVal= 1.0 )))
         SteerSpeed= 1100
         TorqueVSpeedCurve=(Points=((InVal=- 600.0 ,OutVal= 0.0 ),(InVal=- 300.0 ,
OutVal= 80.0 ,InterpMode=CIM_CurveAuto),(InVal= 0.0 ,OutVal= 130.0 ,InterpMode=CIM_CurveAuto),
(InVal= 950.0 ,OutVal= 130.0 ,InterpMode=CIM_CurveAuto),(InVal= 1050.0 ,OutVal= 10.0 ,
InterpMode=CIM_CurveAuto),(InVal= 1150.0 ,OutVal= 0.0 ,InterpMode=CIM_CurveAuto)))
         EngineBrakeFactor= 0.025
     End Object
 
     SimObj=SimulationObject
 
     Components.Add(SimulationObject)
 
     Begin Object Class=SVehicleWheel Name=RRWheel
         BoneName= "Bone_Wheel_RR"
         SkelControlName= "Car_Ctrl_Wheel_RR"
         WheelRadius= 16
         SuspensionTravel= 60
     End Object
     Wheels( 0 )=RRWheel
 
     Begin Object Class=SVehicleWheel Name=LRWheel
         BoneName= "Bone_Wheel_RL"
         SkelControlName= "Car_Ctrl_Wheel_RL"
         WheelRadius= 16 SuspensionTravel= 60
     End Object
     Wheels( 1 )=LRWheel
 
     Begin Object Class=SVehicleWheel Name=RFWheel
         BoneName= "Bone_Wheel_FR"
         SteerFactor= 1.0
         SkelControlName= "Car_Ctrl_Wheel_FR"
         bPoweredWheel= true
         WheelRadius= 16
         SuspensionTravel= 60
     End Object
     Wheels( 2 )=RFWheel
 
     Begin Object Class=SVehicleWheel Name=LFWheel
         BoneName= "Bone_Wheel_FL"
         SteerFactor= 1.0
         WheelRadius= 16
         SuspensionTravel= 60
         bPoweredWheel= true
         SkelControlName= "Car_Ctrl_Wheel_FL"
     End Object
     Wheels( 3 )=LFWheel
 
     UprightLiftStrength = 210.0 ;
     UprightTorqueStrength = 30.0 ;
     UprightTime = 1.0 ;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值