Newton's Law of Motion
Let's take a quick look at the important laws of kinematics. Figure 13-2 shows the physical rules:
Law 1: an object always remains static or at a constant speed in a straight line until it is forced to change this state. This is easy because all objects have a motion vector or velocity vector. Do not change this value unless you apply a force to slow down, accelerate, or change the direction.
Law 2-when an external force is applied, this means that you can accelerate or slow an object. If you exceed one dimension (3d in 3D games), you can change the direction of the object's motion. In this case, you use a vector instead of a scalar. All vector parts (x, y, and z) will change. Because the object quality remains unchanged (at least in most games), you can use the simplified F = ma formula, here "a" is defined as the amount of speed changes divided by the amount of time changes:A=DV/DT.
Law 3-each force has an inverse force in the same direction as its size. This means that if you have a force, such as the gravity that the earth applies to an object, the object will also apply a reverse reaction to the Earth. Because the earth is much larger than the object mass, the gravity of the object on the Earth has no effect on the Earth. However, if you have a larger object, such as the sun, the gravity of the earth is much less than that of the sun. Fortunately, our Earth always rotates around the sun in orbit, the moon rotates around the Earth in the same way.
Figure 13-2
In his book "mathematical principles", Newton proposed these laws, the first of which is the law of gravitation, which explains planetary movements and why the moon turns around the Earth and around the sun. At that time it was very exciting, but today, every school's children can calculate this in the physics class, the computer counts these things more powerful. In the rocket Commander, you have seen the interaction between thousands of minor planets. There is no gravity between them, but it is a great challenge to improve the authenticity by adding the gravity effect.
Keep simple
Physics is a big topic, and you need to do your best to achieve physical effects in the game, so you should keep it as simple as possible. Generally, you don't even know which formula is correct-but you only need to work. For example, you do not have to writeCodeTo follow Newton's first law, the third law can be ignored in many cases, because you usually only care about changing the force of an object's motion.
Before you gain a deeper understanding of the physical world, let's take a look at the existing physical engine. You should ensure that you know exactly what racing games need. If you only need to drive a car and make it stay on the ground, there is no need to implement a very complex physical engine. I really hope to have more time to achieve physical effects in racing games, but the game must be completed on time, and I had quite a few other projects at that time. For this reason, racing games only use a simple method to achieve physical effects, and collision processing is also done in a very simple way. Even if it still requires a lot of fine-tuning, you can see it in the next chapter, but it runs well and you can let the car go on the road.
Figure 13-3 shows the basic physical needs of a racing game. Because you do not interact with other objects in the scene, and the scene objects do not use physical effects, you can focus your attention on the car without caring about other things.
Figure 13-3
These rules are implemented through several lines of code, but it is difficult to adjust all the values to make the vehicle interact correctly with the road:
// [from carphysics. CS] // handle car engine forcefloat newaccelerationforce = 0.0f; If (input. keyboarduppressed | input. keyboard. iskeydown (keys. w) | input. mouseleftbuttonpressed | input. gamepadapressed) newaccelerationforce + = maxaccelerationpersec; // [etc.] // Add acceleration force to total car force, but use the current // car direction! Carforce + = cardir * newaccelerationforce * (movefactor * 85); // 70); // 75); // change speed with standard formula, use acceleration as our // force, gravity is handled below in the applygravity method. float oldspeed = speed; vector3 speedchangevector = carforce/carmass; // only use the amount important for our current direction // (slower rotation) if (speedchangevector. length ()> 0) {float speedapplyfactor = vector3.dot (vector3.normalize (speedchangevector), cardir); If (speedapplyfactor> 1) speedapplyfactor = 1; speed + = speedchangevector. length () * speedapplyfactor;} // If (speedchangevector. length)
This code is more complex than implementing physical effects with the help of the first unit test of the carphysics class, but it still achieves the effect of driving acceleration through the keyboard. In order to let the car drift more than the actual direction, I break down the car direction and movement direction. Speed is used to update the length of the motion vector, but I have encountered many problems. To simplify the problem, I only use one velocity value. With the help of the reverse speed vector, it is easy to make the driving direction lag a little bit, while the car is still pointing to the front.
Because your track is not just straight, you need to rotate the car through the keyboard or the handle. Figure 13-4 shows the basic rules of a revolving car, which is very simple and can be implemented with just a few lines of code. To make the rotation smoother, the previous rotation volume is still used, but a little less.
Figure 13-4
// First handle rotations (reduce last value) rotationchange * = 0.825f; // 75f; // left/right changes rotationif (input. keyboardleftpressed | input. keyboard. iskeydown (keys. a) rotationchange + = maxrotationpersec * movefactor/2.5f; else if (input. keyboardrightpressed | input. keyboard. iskeydown (keys. d) | input. keyboard. iskeydown (keys. e) rotationchange-= maxrotationpersec * movefactor/2.5f; else R Otationchange = 0; If (input. mousexmovement! = 0) rotationchange-= (input. mousexmovement/15.0f) * maxrotationpersec * movefactor; // rotate dir around up vector // interpolate rotatation amount. virtualrotationamount + = rotationchange; // smooth over 200 msfloat interpolatedrotationchange = (rotationchange + virtualrotationamount) * movefactor/0.225f; // 0.200f; virtualrotationamount-= idle; // We can't rotate our car ourself if it is currently not on the groundif (iscaronground) cardir = vector3.transformnormal (cardir, matrix. createfromaxisangle (carup, interpolatedrotationchange ));
If you have several weeks to develop a game, you can have a more complex way to handle the physical effects of your car, if you want more real computing, you can use more constants and forces to make the car's behavior more realistic. But my solution also works well.
To view the effect, use testcarphysicsonplanewithguardrails unit test in the carphysics class. The unit test starts very easily, with only cars and ground rendered through the new planerenderer class. Next, add the guardrail and collision detection, which will be discussed later in this chapter. This is an early version of this unit test, which has been renamed testcarphysicsonplanewithguardrails. This unit test only shows the environment for processing the basic physical effects of a car. Figure 13-5 shows the result. Use the keyboard and input device to control the racing car (top, bottom, left, right, and so on ).
Figure 13-5
Static public void testcarphysicsonplane () {planerenderer plane = NULL; testgame. start ("testcarphysicsonplane", delegate {plane = new planerenderer (vector3.zero, new plane (New vector3 (0, 0, 1), 0), new material ("cityground ", "citygroundnormal"), 500366f); // put car on the ground and use standard ction and up vectors racinggame. player. setcarposition (New vector3 (0, 0, 0), new vector3 (0, 1, 0), new vector3 (0, 0, 1 ));}, delegate {// test slow computers by slowing down the framerate with CTRL if (input. keyboard. iskeydown (keys. leftcontrol) thread. sleep (75); matrix carmatrix = racinggame. player. updatecarmatrixandcamera (); // generate shadows, just the car does shadows shadereffect. shadowmapping. generateshadows (delegate {racinggame. carmodel. generateshadow (carmatrix) ;}); // render shadows (on both the plane and the car) shadereffect. shadowmapping. rendershadows (delegate {racinggame. carmodel. useshadow (carmatrix); plane. useshadow () ;}); basegame. UI. rendergamebackground (); // show car and ground plane racinggame. carmodel. rendercar (0, color. white, carmatrix); plane. render (); // just add brake tracks (we don't render the landscape here) racinggame. landscape. renderbraketracks (); // render all 3D objects basegame. meshrendermanager. render (); // Add shadows shadereffect. shadowmapping. showshadows (); racinggame. player. displayphysicsvaluesandhelp () ;}// testcarphysicsonplane ()
Gravity
It is easy to drive on a plane, but it is not very interesting. In particular, this racing game also allows you to drive on loop tracks, tunnels, curves, and so on. Collision Detection will be discussed later in this chapter, but you can add gravity and adjust it in the testcarphysicsonplane unit test.
You only need to make a slight change in the testcarphysicsonplane unit test, that is, in the initialization code, the car is promoted to a height 10 meters away from the ground, it should fall down, but because you have not achieved real physical effects (only car control, acceleration, and turns), the car just stays in the air (see Figure 13-6 ).
Figure 13-6
// Put Car 10 m above the ground to test gravity and ground plane! Racinggame. Player. setcarposition (New vector3 (0, 0, 10), new vector3 (0, 1, 0), new vector3 (0, 0, 1 ));
The player class inherits from the chasecamera class, while the chasecamera class inherits from the carphysics class. In this way, all vehicle control, physics, and player logic can be processed and accessed from the same player class. The chasecamera class has two modes, similar to the spacecamera class mode of the rocket Commander:
The Free camera mode is used for all unit tests. Sometimes you can change it in unit testing.
The default Chase car game is used for games. It is used for menus or replay the game process, and of course also for displaying and controlling cars in the game.
Use the following code during initialization to ensure that the camera is used. If you do not use zoom in the game, you can see the pull process of the car at the beginning:
// Make sure we are not in free Camera mode and can control the carracinggame. player. freecamera = false; racinggame. player. zoomintime = 0; racinggame. player. setcameraposition (New vector3 (0,-5, 8 ));
The rest of the unit test remains unchanged. In the carphysics update method, gravity computation calls the applygravity method:
/// <Summary> /// apply gravity /// </Summary> private void applygravity () {// fix car on ground float distfromground = vector3helper. signeddistancetoplane (carpos, groundplanepos, groundplanenormal); iscaronground = distfromground>-0.1f; // use very hard and instant gravity to fix if car is below ground! Float movefactor = basegame. latency; float maxgravity = gravity * movefactor; // use more smooth gravity for jumping float mingravity =-gravity * movefactor; If (latency> maxgravity) {distfromground = maxgravity; gravityspeed = 0;} // If (distfromground) if (distfromground <mingravity) {round = mingravity; gravityspeed-= distfromground;} // If (distfromground) carpos. Z + = distfromground;} // applygravity ()
This Code only lowers the Z value to know that the car is on the ground. When you drive up the hill, the car may be lower than the road, or because of some precision errors, the Z value will also be corrected to make the car re-return to the road.
Many constants are used for adjustment, which are defined at the beginning of the carphysics class. Below are the first three constants. Change carmass. For example, if a car is heavy, it is almost stuck on the ground, or very light, and can fly in the air for a long time:
# Region constants // <summary> // car is 1000 /// </Summary> const float carmass = 1000; // 1000.0f; /// <summary> /// gravity on Earth is 9.81 m/s ^ 2 // </Summary> const float gravity = 9.81f; /// <summary> // max speed of our car is 275 mph. /// while we use mph for the display, we calculate internally with // meters per sec since meter is the unit we use for everthing in the // game. and it is a much nicer unit than miles or feet. /// </Summary> Public const float maxspeed = 275.0f * mphtometerpersec; // <summary> // convert our meter per sec to mph for display. /// 1 mile = 1.609344 kilometers // each hour has 3600 seconds (60 min * 60 sec ). /// 1 kilometer = 1000 meter. /// </Summary> Public const float meterpersectomph = 1.609344f * (601_f * 601_f)/1000.0f), mphtometerpersec = 1.0f/meterpersectomph; // [etc.]
Several times you may see that I use meters instead of feet, inches, yards, miles, or formats used in the United States. Sorry, I use meters elsewhere in the world, this makes it easier to measure and set constants. I don't even know how big a gravity constant is in other unit systems. You can see that whenever there are other formats such as mph (miles per hour) I use an auxiliary constant to convert the format. You can also use Google to convert the format. Just enter "1500 meters as Miles" and you will get a number of miles.
The new testcarphysicsonplane unit test allows you to control racing cars and properly handle gravity (see Figure 13-7 ). When you brake, it also handles displays the braking track on the ground.
Figure 13-7