Hovering Vehicle Physics Tutorial
Level: Medium
This tutorial mainly focuses on the basics of physics coding. You may notice in most games you will have objects falling around the place, bouncing and everything, even with vehicles. But today we wont be learning this, this tutorial may be at a medium level, but it is mostly aimed at complete newcomers, which means, that you shouldn't start coding like this, this is just to give you a clue as to how it's done.
I have been working on some physics functions of my own recently, and I have learned enough while I was at it, so i thought I should share some of that knowledge.
I'm now going to stop blabbing and get to the get go, first of all, we will set up the program. So here's the first piece of code I will give you.
`------------------
` **Game setup**
`------------------
Sync on : Sync rate 60
Set Display mode 1024,768,16 : Hide Mouse
Autocam off : Set Camera Range 1,0x7fffffff
You may notice the parts marked with a open quote mark (`), these aren't commands, they completely blank out that line.
I made some fancy decoration above th setup, this isn't just so it looks cool, but also to keep my program tidy. I'll tell you one thing, keep your program tidy, because no matter how far you get, if your code isn't tidy, then you will feel the urge to restart.
Next, for some more seting up, we will use this code:
`Setup Types
Type Vector
X as Float
Y as Float
Z as Float
Endtype
These are types. Two things you need to notice are: The name of the type, and what's within it. First of all, what's in it, can be called anything you want, I called them X, Y and Z, so I can use them as coordinates. (Floats mean numbers with decimals). The name of the type doesn't really have to be anything special, but I called it vector anyway. Basically, if I make a type, in this case, this one, I can make a variable as a type. I.e. Pos as vector. That means I can extend that variable to pos.x, pos.y and pos.z, which can be easier than always doing posx, posy, and posz.
Ok, now we have setup the basics. Now we will look at the player. Enter this code just after,
`--------------------
` **Setup Player**
`--------------------
` —Variables
Global MDir as Vector `Direction of movement
Global Vel as Vector `Velocity
Global Acc as Vector `Acceleration
Global Force as Vector `Force
Global Drag as Vector `Drag
Global Traction as Vector `Traction
Global AngForce as Vector `Angular force
Global AngAcc as Vector `Angular Acceleration
Global AngVel as Vector `Angular Velocity
Global Angle as Vector `Angle
Global Pos as Vector `Position
Global Speed as Float `Speed
` —Constants
Global EngForce `Engine Force
Global Mass as Float `Mass
Global CDrag as Float `Constant Drag
Global CTraction as Float `Constant Traction
Global ATraction as Float `Angular Constant Traction
Again, I have made a fancy little thing at the top. Notice I have made most of the variables as vectors, they will have x, y and z attached onto the end of them. I have also made them all Global, which means they can be used in the whole program. This means I can put it in a function, and if I can do that, I can have AI Bots with physics too, but this would mean that I would have to use arrays.
I may explain arrays later. Just ask me to if you want.
Next, I will have to configure the player. While I'm at it, please take note that I have a very big setup, The setup if a very large part of the code, and you can have hundreds of lines just setting up, but it's all very important, so try not to setup stuff all in seperate bits.
`---------------------
` **Configuration**
`---------------------
EngForce=1000
Mass=50
CDrag=0.47
CTraction=0.98
ATraction=0.98
The mass is a very important part here, the higher the mass, the slower you will accelerate.
Now we have set up. Time to create some stuff.
`--------------
` **Create**
`--------------
`Create Player Vehicle
Make Object Box 1,100,50,70
`Create World
Make Matrix 1,10000,10000,50,50
The 3D box will represent the player, though it can be modeled in some very good programs out there, but since this is a tutorial it doesn't matter.
A matrix will make a grid that can be used as a ground, I am only using this so we can see ourselves moving.
Ok, now, this part is the main loop, as you may have guessed, it's a loop, it loops, it makes continuous loops, and it doesn't stop unless you tell it to, or you have a power-cut in your house. This is where the controls and everything that changes goes.
`----------------
` ))Main Loop((
`----------------
Do
Sync
Loop
`-----------------
` ))Main Loop((
`-----------------
'Sync' has to go before the 'loop' command so the screen will refresh, otherwise you wouldn't see anything changes.
Ok, to start off the loop, we have to atleast set out the controls.
`----------------
` **Controls**
`----------------
If Upkey()=1 then up=1 else up=0
If Downkey()=1 then down=1 else down=0
If Leftkey()=1 then left=1 else left=0
If Rightkey()=1 then right=1 else right=0
These variables here that I have made a called boolean variables. For example, if the up button is pressed, the up variale is 1, and if not, then it turns to 0. That's all a boolean variable is, 1, or 0. In some languages they can be called 'true' or 'false'.
Next thing we have to do is to change the forces that are applyed to the player when you press the controls.
`Apply Forces When Controls Are Pressed
if Up=1
Force.X=Traction.X+Drag.X
Force.Y=Traction.Y+Drag.Y
Force.Z=Traction.Z+Drag.Z
else
Force.X=0
Force.Y=0
Force.Z=0
endif
If Left=1 Then AngForce.Y=-(Speed/5)
If Right=1 then AngForce.Y=(Speed/5)
If Left=0 and Right=0 then AngForce.Y=0
Notice now we have put the types into use. Also, if the buttons are not pressed, then we have to change the forces back to 0, it wont do this for you.
Also, notice that when you press the left or right keys, the angular force is the speed devided 5. As you may know, if you are in your car and you're not moving, then you cannot turn, so if your speed (which we will calculate later) is 0, then you will not have any angular force. If 0 is devided by anything, then it will end up as 0, which means you will not turn. Also, the more you devide it by, the less you will turn.
Now, time to recap, here's out code so far.
`------------------
` **Game setup**
`------------------
Sync on : Sync rate 60
Set Display mode 1024,768,16 : Hide Mouse
Autocam off : Set Camera Range 1,0x7fffffff
`Setup Types
Type Vector
X as Float
Y as Float
Z as Float
Endtype
`--------------------
` **Setup Player**
`--------------------
` —Variables
Global MDir as Vector `Direction of movement
Global Vel as Vector `Velocity
Global Acc as Vector `Acceleration
Global Force as Vector `Force
Global Drag as Vector `Drag
Global Traction as Vector `Traction
Global AngForce as Vector `Angular force
Global AngAcc as Vector `Angular Acceleration
Global AngVel as Vector `Angular Velocity
Global Angle as Vector `Angle
Global Pos as Vector `Position
Global Speed as Float `Speed
` —Constants
Global EngForce `Engine Force
Global Mass as Float `Mass
Global CDrag as Float `Constant Drag
Global CTraction as Float `Constant Traction
Global ATraction as Float `Angular Constant Traction
`---------------------
` **Configuration**
`---------------------
EngForce=1000
Mass=50
CDrag=0.47
CTraction=0.98
ATraction=0.98
`--------------
` **Create**
`--------------
`Create Player Vehicle
Make Object Box 1,100,50,70
`Create World
Make Matrix 1,10000,10000,50,50
`----------------
` ))Main Loop((
`----------------
Do
`----------------
` **Controls**
`----------------
If Upkey()=1 then up=1 else up=0
If Downkey()=1 then down=1 else down=0
If Leftkey()=1 then left=1 else left=0
If Rightkey()=1 then right=1 else right=0
`Apply Forces When Controls Are Pressed
if Up=1
Force.X=Traction.X+Drag.X
Force.Y=Traction.Y+Drag.Y
Force.Z=Traction.Z+Drag.Z
else
Force.X=0
Force.Y=0
Force.Z=0
endif
If Left=1 Then AngForce.Y=-(Speed/5)
If Right=1 then AngForce.Y=(Speed/5)
If Left=0 and Right=0 then AngForce.Y=0
Sync
Loop
`-----------------
` ))Main Loop((
`-----------------
Nothing will happen so far, but that doesn't mean we don't have progress.
Now we have to get down to the nitty-gritty of the program. The
equations!!
You may find them complex, depending on you physics knowledge, so I will go through it all bit by bit.
`---------------
` **Physics**
`---------------
`Direction
MDir.X = Sin(Angle.Y) * Cos(Angle.X)
MDir.Y = -Sin(Angle.X)
MDir.Z = Cos(Angle.Y) * Cos(Angle.X)
The MDir variable is a variable which will calculate the direction of your movement. E.G. If you wanted them to react the the direction you are moving on, you could do these calculations, and draw a 3d line for 0,0,0 to mdir.x, mdir.y, mdir.z, the angle of that line would be the same your your angle. We do not have to have such complex equations for moving along the X and Z axis, but it doesn't make any difference doing it this way apart from the fact that you can make it for moving in the air, like an aeroplane, or a spaceship.
Now, we will work out some forces.
`Forces
Traction.X=MDir.X * EngForce
Traction.Y=MDir.Y * EngForce
Traction.Z=MDir.Z * EngForce
Drag.X=-CDrag * Vel.X * Speed
Drag.Y=-CDrag * Vel.Y * Speed
Drag.Z=-CDrag * Vel.Z * Speed
traction = Direction * Engine Force
drag = -Constant drag * Velocity * Speed
These equations are important to our movement, because as you may have noticed at the controls part, the force that is used to push us depends on these two forces.
Next, we will calculate our velocities, the velocity is just like a speed, instead in only one axis, i.e. if you are moving at 100mph along the z axis, then your x velocity will still be 0.
`Movement
Acc.X = Force.X / Mass
Acc.Y = Force.Y / Mass
Acc.Z = Force.Z / Mass
Vel.X = (Vel.X + Acc.X) * CTraction
Vel.Y = (Vel.Y + Acc.Y) * CTraction
Vel.Z = (Vel.Z + Acc.Z) * CTraction
Speed=SQRT( Vel.X^2 + Vel.Y^2 + Vel.Z^2)
Acceleration = Force / Mass
Velocity = (Velocity + Acceleration) * Constant traction
Speed = Square root ((Velocity X*Velocity X) + (Velocity y*Velocity y) + (Velocity z*Velocity z))
Now, first, notice the force and mass affect the acceleration, this is why you speed up gradualy as force is applyed, and the higher the mass, the slower you speed up, it makes perfect sense.
The velocity equation isn't entirely true, but we will skip all the really complex stuff to get it. The constant traction is important to slowing the vehicle down over time, otherwise it'll just keep on going. It has to be less than 1, here's what I mean. let's say the velocity is at 10, and you multiply it by 0.98 all the time, this is what will happen to it:
10
9.8
9.604
9.41192
9.2236816
Notice how it keeps going down, success, we are slowing down.
The speed also has to be worked out, because it has been used. The speed it like velocity, instead affects all axis.
Now, the important parts. We have to affect out angles and coordinates from these.
To affect the coordinates, there are two equations you can use:
Position = Position + Velocity
Position = Position + (Direction * Speed)
We will use the first one. It will not move you forward all the time, your angles will only affects your movement if you press the up arrow key, otherwise you will continue in one direction no matter which way you turn. It's like the difference between the way a skateboard and a snowboard moves. The skateboard will only move forward. Sometimes you can make lateral forces to affect the positions, but we wont.
We are going to do this type of movement because it is a floating type of vehicle, and it gives us better movement. Though if you do want sliding, you can check out this site for car physics, it is much more complex and doesn't actually give you any code, but it gives you alot of equations.
http://home.planet.nl/~monstrous/tutcar.html
Now the positions have been affected, all we have to do now is affect the angles.
Angle.X = Wrapvalue( Angle.X + AngVel.X )
Angle.Y = Wrapvalue( Angle.Y + AngVel.Y )
Angle.Z = Wrapvalue( Angle.Z + AngVel.Z )
We are pretty much finished. Only two things to do, first, we will update the player with the positions.
`Update player
Pos.Y=Get Ground Height(1,Pos.X,Pos.Z)+30
Position Object 1, Pos.X, Pos.Y, Pos.Z
Rotate object 1, Angle.X, Angle.Y, Angle.Z
I changed the Pos.Y variable so you are always above the ground.
Now, to finish with, we will make a good little 3rd person camera to follow us around.
`--------------
` **Camera**
`--------------
ca# = curveangle(Angle.Y,ca#,20.0)
cx#=pos.x-Sin(ca#)*600
cy#=pos.y+300
cz#=pos.z-Cos(ca#)*600
position camera cx#,cy#,cz#
point camera pos.x,pos.y,pos.z
And here's the code in full for you to try out:
`------------------
` **Game setup**
`------------------
Sync on : Sync rate 60
Set Display mode 1024,768,16 : Hide Mouse
Autocam off : Set Camera Range 1,0x7fffffff
`Setup Types
Type Vector
X as Float
Y as Float
Z as Float
Endtype
`--------------------
` **Setup Player**
`--------------------
` —Variables
Global MDir as Vector `Direction of movement
Global Vel as Vector `Velocity
Global Acc as Vector `Acceleration
Global Force as Vector `Force
Global Drag as Vector `Drag
Global Traction as Vector `Traction
Global AngForce as Vector `Angular force
Global AngAcc as Vector `Angular Acceleration
Global AngVel as Vector `Angular Velocity
Global Angle as Vector `Angle
Global Pos as Vector `Position
Global Speed as Float `Speed
` —Constants
Global EngForce `Engine Force
Global Mass as Float `Mass
Global CDrag as Float `Constant Drag
Global CTraction as Float `Constant Traction
Global ATraction as Float `Angular Constant Traction
`---------------------
` **Configuration**
`---------------------
EngForce=1000
Mass=50
CDrag=0.47
CTraction=0.98
ATraction=0.98
`--------------
` **Create**
`--------------
`Create Player Vehicle
Make Object Box 1,100,50,70
`Create World
Make Matrix 1,10000,10000,50,50
`----------------
` ))Main Loop((
`----------------
Do
`----------------
` **Controls**
`----------------
If Upkey()=1 then up=1 else up=0
If Downkey()=1 then down=1 else down=0
If Leftkey()=1 then left=1 else left=0
If Rightkey()=1 then right=1 else right=0
`Apply Forces When Controls Are Pressed
if Up=1
Force.X=Traction.X+Drag.X
Force.Y=Traction.Y+Drag.Y
Force.Z=Traction.Z+Drag.Z
else
Force.X=0
Force.Y=0
Force.Z=0
endif
If Left=1 Then AngForce.Y=-(Speed/5)
If Right=1 then AngForce.Y=(Speed/5)
If Left=0 and Right=0 then AngForce.Y=0
`---------------
` **Physics**
`---------------
`Direction
MDir.X = Sin(Angle.Y) * Cos(Angle.X)
MDir.Y = -Sin(Angle.X)
MDir.Z = Cos(Angle.Y) * Cos(Angle.X)
`Forces
Traction.X=MDir.X * EngForce
Traction.Y=MDir.Y * EngForce
Traction.Z=MDir.Z * EngForce
Drag.X=-CDrag * Vel.X * Speed
Drag.Y=-CDrag * Vel.Y * Speed
Drag.Z=-CDrag * Vel.Z * Speed
`Movement
Acc.X = Force.X / Mass
Acc.Y = Force.Y / Mass
Acc.Z = Force.Z / Mass
Vel.X = (Vel.X + Acc.X) * CTraction
Vel.Y = (Vel.Y + Acc.Y) * CTraction
Vel.Z = (Vel.Z + Acc.Z) * CTraction
Speed=SQRT( Vel.X^2 + Vel.Y^2 + Vel.Z^2)
`Angles
AngAcc.X = AngForce.X / Mass
AngAcc.Y = AngForce.Y / Mass
AngAcc.Z = AngForce.Z / Mass
AngVel.X = (AngVel.X + AngAcc.X) * ATraction
AngVel.Y = (AngVel.Y + AngAcc.Y) * ATraction
AngVel.Z = (AngVel.Z + AngAcc.Z) * ATraction
`Apply Physics To Player
Pos.X=Pos.X+Vel.X
Pos.Y=Pos.Y+Vel.Y
Pos.Z=Pos.Z+Vel.Z
Angle.X = Wrapvalue( Angle.X + AngVel.X )
Angle.Y = Wrapvalue( Angle.Y + AngVel.Y )
Angle.Z = Wrapvalue( Angle.Z + AngVel.Z )
`Update player
Pos.Y=Get Ground Height(1,Pos.X,Pos.Z)+30
Position Object 1, Pos.X, Pos.Y, Pos.Z
Rotate object 1, Angle.X, Angle.Y, Angle.Z
`--------------
` **Camera**
`--------------
ca# = curveangle(Angle.Y,ca#,20.0)
cx#=pos.x-Sin(ca#)*600
cy#=pos.y+300
cz#=pos.z-Cos(ca#)*600
position camera cx#,cy#,cz#
point camera pos.x,pos.y,pos.z
Sync
Loop
`-----------------
` ))Main Loop((
`-----------------
I hope you have learned the basics of physics programming, and agknowledge that no matter how much you hate school, you still have to use real equations to make something convincing.
Have fun, bye