About the main loop of the game

Source: Internet
Author: User
Original address http://hi.baidu.com/xiongharry/blog/item/e3f8e987a5c1a63766096e75.html

Http://dewitters.koonsolo.com/gameloop.html

Introduction

The main cycle of the game is the heartbeat of each game, which carries the nutrients required by the entire game. Unfortunately, there is no good article to guide a cainiao game programmer on how to support his program. But don't worry, because you just accidentally saw this article, which is the only article that gives this topic enough attention.

As a game programmer, I have seen many cell phone game codes. These codes show me how to implement a colorful game main loop. You may want to ask, "Is it still amazing to make such a simple gadget ?" In this way, I will discuss the advantages and disadvantages of some mainstream implementations in this article, and introduce you to the best nutrient delivery solution in my opinion.

Game Main Loop

Each game is composed of getting user input, updating game status, processing AI, playing music and sound effects, and displaying these behaviors on the screen. The main loop of the game is used to process this behavior sequence. As I said in the introduction, the main cycle of a game is the heartbeat of every game. In this article, I will not explain in detail any of the above actions, but will only detail the main loop of the game. So I simplified these actions to two functions:

Update_game (); // update the game status (which may be translated as a logical frame later)
Display_game (); // update display (display frame)

The following is the simplest main loop of a game:

Bool game_is_running = true;

While (game_is_running ){
Update_game ();
Display_game ();
}

The main problem with this simple loop is that it ignores the time and the game will fly freely. Running the game on the Overlord machine has a strong sense of frustration. running on a powerful machine requires the player to have a super power of judgment and APM (originally intended to run slowly on a slow machine, fast Running on a machine ......). In ancient times, the speed of hardware was known, but there are so many hardware platforms that we have to deal with the important factor of time. There are many ways to process the time. I will give it one by one.

First, I will explain two terminologies throughout the full text:

Frames per second (FPS)

FPS is the abbreviation of frames per second. In this context, it indicates the number of times display_game () is called per second.

Game speed

The game speed is the speed at which the game status is updated per second. In other words, the number of times update_game () is called per second.

FPS depends on constant game speed

Implementation

A solution that allows the game to run stably at 25 frames per second is as follows:

Const int frames_per_second = 25;
Const int skip_ticks = 1000/frames_per_second;

DWORD next_game_tick = gettickcount ();
// Gettickcount () returns the current number of milliseconds
// That have elapsed since the system was started

Int sleep_time = 0;

Bool game_is_running = true;

While (game_is_running ){
Update_game ();
Display_game ();

Next_game_tick + = skip_ticks;
Sleep_time = next_game_tick-gettickcount ();
If (sleep_time> = 0 ){
Sleep (sleep_time );
}
Else {
// Shit, we are running behind!
}
}

This solution has a great advantage: simple! Because you know that update_game () is called 25 times per second, the logic part of your game code will be very straightforward. For example, implementing a replay function in a game implemented in this main loop is very simple (Note: Because the interval between each frame is known, you only need to record the status of each frame of the game, play back at a constant speed. ). If the game is not affected by a random value, you only need to record the player input for replay.

On the hardware you implement this loop, you can adjust frames_per_second to an ideal value as needed. But how does the main loop implementation of this game perform on various hardware?

Overlord Machine

If the hardware can cope with the specified FPS, nothing will happen. But the overlord is usually unable to cope with it, and the game will be stuck. In extreme cases, the game may get stuck, or even take ten or ten steps (originally, in some cases, the game is slow, and in some cases it is normal ). Such a problem will destroy your game and defeat players.

Awesome Machine

It seems that there will be no problem on the awesome machine, but the main loop of such a game wastes a lot of clock loops! Running the game with awesome machines can easily reach 300 frames, but only 25 or 30 frames per second ~ Therefore, this main loop will make it impossible for players with Awesome hardware to make full use of its hardware effect, resulting in a great sense of frustration (originally, this implementation will affect your visual effect, especially for high-speed moving objects ).

From another perspective, this may be an advantage for mobile devices. The continuous high-speed operation of the game will quickly consume the battery ......

Conclusion

The main loop implementation solution based on FPS with constant game speed is easy to learn. However, there are some problems. For example, if the defined FPS is too high, the old man will be overwhelmed. If the defined FPS is too low, the high-end hardware will lose too much visual effect.

Game speed based on variable FPS

Implementation

Another game implementation allows the game to fly as far as possible, and allows the game speed to be determined based on FPS. The game status is updated based on the time consumed by each display frame.

DWORD prev_frame_tick;
DWORD curr_frame_tick = gettickcount ();

Bool game_is_running = true;
While (game_is_running ){
Prev_frame_tick = curr_frame_tick;
Curr_frame_tick = gettickcount ();

Update_game (curr_frame_tick-prev_frame_tick );
Display_game ();
}

The main loop code of this game is a little more complex than before, because we have to consider the time difference between the two update_game () calls. However, this is not complicated.
The code for this implementation seems to be an ideal implementation solution. I have seen many smart game programmers use this method to write the main loop of a game. But I will show you how this implementation solution has serious problems on overlord and awesome machines! Yes, including the machines of very professional and extremely skilled players.

Overlord

The Overlord will be stuck in complicated operations, especially in complex scenarios in 3D games. The lower frame rate will affect the response of game input and reduce the player's response speed. Game status updates will also be suddenly affected. Such a situation slows down the response of players and AI, and increases the players' frustration. For example, an obstacle that can be easily crossed at a normal frame rate will be insurmountable at a low frame rate. The more serious problem is that on the Overlord, there are often some strange things that violate physical laws, if these operations involve physical simulation.

Awesome Machine

You may wonder why the previous game loop encountered a problem on a fast machine. Unfortunately, this solution is indeed true. First, let me introduce you to some computer mathematics knowledge.

The memory size occupied by floating point numbers is limited, so some values cannot be displayed. For example, 0.1 cannot be expressed in binary format, so it will be stored in a floating point. I will show you how to use python.

>>> 0.1
0.10000000000000001

This issue is not dramatic in itself, but the consequences are the opposite. For example, your car speed is 0.001 units per microsecond. The correct result is that your racing car will move 10 units in 10 seconds, so let's do this:

>>> Def get_distance (FPS ):
... Skip_ticks = 1000/FPS
... Total_ticks = 0
... Distance = 0.0
..... Speed_per_tick = 0.001
... While total_ticks <10000:
... Distance + = speed_per_tick * skip_ticks
... Total_ticks + = skip_ticks
... Return distance

Now we can get the result after 10 seconds of running 40 frames per second.
>>> Get_distance (40)
10.000000000000075

And so on ~ Why not 10? What happened? Well, the error after 400 addition operations is so large. How can this problem be solved after 100 addition operations per second?

>>> Get_distance (100)
9.9999999999998312

The error is getting bigger and bigger! What is the difference between the result of 40 frames per second and the error of 100 frames per second?

>>> Get_distance (40)-get_distance (100)
2.4336088699783431e-13

You may think that this error can be ignored. But the real problem occurs when you use these wrong values for more operations. Small errors will be extended to fatal errors! Then these errors will crash the game while it is flying! The probability of these problems is absolutely high enough to attract your attention. I have seen a game with High Frame Rate Problems for this reason. Then the game programmer found that these problems appeared in the core part of the game. Only a majority of the code can be rewritten to fix them.

Conclusion

The main loop of such a game looks good, but it is not very good. Serious problems may occur regardless of the hardware that runs it. In addition, the Code implemented by the game is more complex than the master loop with fixed game speed. What are the reasons for using the code?

Maximum FPS and constant game speed

Implementation

In our first implementation, FPS depends on constant game speed, and problems may occur on low-end machines. Frames are dropped for both game speed and game display. A feasible solution is to sacrifice the display frame rate to maintain a constant game speed. This solution is implemented as follows:


Const int ticks_per_second = 50;
Const int skip_ticks = 1000/ticks_per_second;
Const int max_frameskip = 10;

DWORD next_game_tick = gettickcount ();
Int loops;

Bool game_is_running = true;
While (game_is_running ){

Loops = 0;
While (gettickcount ()> next_game_tick & loops <max_frameskip ){
Update_game ();

Next_game_tick + = skip_ticks;
Loops ++;
}

Display_game ();
}

The game will update at a stable speed of 50 (logical) frames per second, and the rendering speed is as fast as possible. Note that if the rendering speed exceeds 50 frames per second, some frames will be identical. Therefore, the display frame rate is equivalent to the fastest 50 frames per second. When running on overlord, the frame rate will decrease when the game status is updated and the number of rounds reaches max_frameskip. In the above example, when the rendering frame rate drops below 5 (frames_per_second/max_frameskip), the game speed slows down.

Overlord

If you run such a game loop on overlord, frames are dropped, but the speed of the game is not affected. If the hardware still cannot handle such a loop, the game speed and frame rate will be affected.

Awesome Machine

This game loop won't be a problem on the awesome machine, but like the first solution, too many clock cycles are wasted. It is vital to find a balance between fast updates and running the game on overlord!

Conclusion

Using the above solution can simplify the game implementation code. But there are still some problems: if a high FPS is defined, the Overlord will not be able to afford it. If it is too low, it will make it difficult for the powerful machine to play the performance.

Independent Variable display frame rate and constant game speed

Implementation

Is it possible to optimize the previous solution so that it can be well performed on various platforms? Of course! The game status itself does not need to be updated 60 times per second. Player input and AI information do not require such a high frame rate to be updated. It is enough to be updated about 25 times per second. Therefore, we can try to make update_game () be called 25 times a few times per second. Rendering will not let it go, let it fly. However, it is not allowed that the low rendering Frame Rate of Overlord affects the speed of game status updates. The implementation of this solution is as follows:

Const int ticks_per_second = 25;
Const int skip_ticks = 1000/ticks_per_second;
Const int max_frameskip = 5;

DWORD next_game_tick = gettickcount ();
Int loops;
Float interpolation;

Bool game_is_running = true;
While (game_is_running ){

Loops = 0;
While (gettickcount ()> next_game_tick & loops <max_frameskip ){
Update_game ();

Next_game_tick + = skip_ticks;
Loops ++;
}

Interpolation = float (gettickcount () + skip_ticks-next_game_tick)
/Float (skip_ticks );
Display_game (interpolation );
}

Using update_game () in this solution is relatively simple, and display_game () is relatively complex. You need to implement a prediction function that receives interpolation parameters. This is not difficult, but you need to do some additional work. I will explain how this prediction function works, but first let me tell you why such a function is needed.

The game status is updated 25 times per second. If you do not use interpolation during rendering, the rendering frame rate is limited to 25 frames. It should be noted that 25 frames are not as bad as people think, and the video is still smooth at 24 frames per second. Therefore, 25 frames can be used to display game images. However, a higher frame rate will bring better results for high-speed moving objects. So what we need to do is to smooth objects that move at a high speed between display frames. This is why we need interpolation and prediction functions.

Interpolation and prediction functions

As I said before, game status updates run at a constant frame rate. When you render the screen, it is very likely that there are two logical frames. Suppose you have updated your game status 10th times, and now you need to render your scenario. This rendering will appear between 10th and 11th logical frames. It is very likely to appear at the position of frame 10.3. The interpolation value is 0.3. For example, one of my racing cars calculates positions in the following ways.

Position = Position + speed;

If the position of the car is 10th after 500 logical frames and the speed is 100, the position of the 11th frame will be 600. so where will you render your car at 10.3 frames? Obviously, it should be like the following:

View_position = Position + (speed * interpolation)

Now the car will be correctly rendered at the position 530.

Basically, the interpolation value is the position where the rendering occurs in the previous frame and the next frame. What you need to do is write a prediction function to predict the correct position of your racing car/camera or other objects during rendering. You can calculate the expected position based on the object speed. These are not complex. For some expected frame errors, such as when an object is rendered to an object, it does. Because the speed of the game is constantly updated for 25 times per second, this error remains on the screen for a very short time, difficult to find, and no major problem.

Overlord

In most cases, update_game () takes much less time to execute than display_game. In fact, we can assume that update_game () can still run 25 times per second on the overlord. Therefore, the logic status of the game will not be greatly affected, even if the FPS is very low.

Awesome Machine

On Awesome hardware, the speed of the game will remain 25 times per second, but the screen update can be very fast. The interpolation solution allows the game to have a better picture performance at a high frame rate. However, in essence, the game status is updated only 25 times per second.

Conclusion

The solution that makes game status updates independent of FPS seems to be the best game main loop implementation. However, you must implement an interpolation function.

Overall summary

The main cycle of a game has far more impact on the game than you think. We have discussed four possible implementation methods, one of which must be resolutely avoided, that is, the variable frame rate to determine the game speed.

A constant frame rate may be a good implementation for mobile devices. If you want to show the full strength of your hardware, you 'd better use an implementation scheme with FPS independent of the Game speed.

If you don't want to bother implementing a prediction function, you can use the implementation scheme of the maximum frame rate, just to find a balance point of the frame rate.

Now, you can write the game you dreamed!

Koen witters

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.