Asynchronous screen update to gameplay logic, C++
Asked Answered
L

3

5

I am programming a game using Visual C++ 2008 Express and the Ogre3D sdk.

My core gameplay logic is designed to run at 100 times/second. For simplicity, I'll say it's a method called 'gamelogic()'. It is not time-based, which means if I want to "advance" game time by 1 second, I have to call 'gamelogic()' 100 times. 'gamelogic()' is lightweight in comparison to the game's screen rendering.

Ogre has a "listener" logic that informs your code when it's about to draw a frame and when it has finished drawing a frame. If I just call 'gamelogic()' just before the frame rendering, then the gameplay will be greatly affected by screen rendering speed, which could vary from 5fps to 120 fps.

The easy solution that comes to mind is : calculate the time elapsed since last rendered frame and call 'gamelogic()' this many times before the next frame: 100 * timeElapsedInSeconds

However, I pressume that the "right" way to do it is with multithreading; have a separate thread that runs 'gamelogic()' 100 times/sec.

The question is, how do I achieve this and what can be done when there is a conflict between the 2 separate threads : gamelogic changing screen content (3d object coordinates) while Ogre is rendering the screen at the same time .

Many thanks in advance.

Lothaire answered 28/5, 2009 at 13:24 Comment(0)
K
7

If this is your first game application, using multi-threading to achieve your results might be more work than you should really tackle on your first game. Sychronizing a game loop and render loop in different threads is not an easy problem to solve.

As you correctly point out, rendering time can greatly affect the "speed" of your game. I would suggest that you do not make your game logic dependent on a set time slice (i.e. 1/100 of a second). Make it dependent on the current frametime (well, the last frametime since you don't know how long your current frame will take to render).

Typically I would write something like below (what I wrote is greatly simplified):

float Frametime = 1.0f / 30.0f;
while(1) {
    game_loop(Frametime);      // maniuplate objects, etc.
    render_loop();             // render the frame
    calculate_new_frametime();
}

Where Frametime is the calculcated frametime that the current frame took. When you process your game loop you are using the frametime from the previous frame (so set the initial value to something reasonable, like 1/30th or 1/15th of a second). Running it on the previous frametime is close enough to get you the results that you need. Run your game loop using that time frame, then render your stuff. You might have to change the logic in your game loop to not assume a fixed time interval, but generally those kinds of fixes are pretty easy.

Asynchoronous game/render loops may be something that you ultimately need, but that is a tough problem to solve. It involves taking snapshops of objects and their relevant data, putting those snapshots into a buffer and then passing the buffer to the rendering engine. That memory buffer will have to be correctly partitioned around critical sections to avoid having the game loop write to it while the render loop is reading from it. You'll have to take care to make sure that you copy all relevant data into the buffer before passing to the render loop. Additionally, you'll have to write logic to stall either the game or render loops while waiting for one or the other to complete.

This complexity is why I suggest writing it in a more serial manner first (unless you have experience, which you might). The reason being is that doing it the "easy" way first will force you to learn about how your code works, how the rendering engine works, what kind of data the rendering engine needs, etc. Multithreading knowledge is defintely required in complex game development these days, but knowing how to do it well requires indepth knowledge of how game systems interact with each other.

Knockabout answered 28/5, 2009 at 14:16 Comment(4)
Games switched from fixed render rate assumptions to tracking frame-times a while ago - They used to rely on knowing the processor runs at 8 Hz. When they came out with 16Hz machines, they included a 'turbo button' to switch the processor from 16Hz down to 8Hz so that games would still run at the correct speed. Fun Fact.Futurity
Indeed! There were even small apps that were written to "slow down" your processor so that old games would run at acceptable framerates on newer machines. One was called "MOslo" I think. I remember all of my old Ultima games running way too fast on newer computers that I had.Knockabout
It got even better with the turbo pascal runtime, which would produce an integer overflow when running on a fast machine and thereby crash everything.Zoilazoilla
seconded.. don't do it the way you describe, do it the way this answer says! Is there any actual reason to do it using threads and fixed time?Apophyge
W
1

There's not a whole lot of benefit to your core game logic running faster than the player can respond. About the only time it's really useful is for physics simulations, where running at a fast, fixed time step can make the sim behave more consistently.

Apart from that, just update your game loop once per frame, and pass in a variable time delta instead of relying on the fixed one. The benefit you'll get from doing multithreading is minimal compared to the cost, especially if this is your first game.

Window answered 5/9, 2009 at 9:13 Comment(0)
D
0

Double buffering your render-able objects is an approach you could explore. Meaning, the rendering component is using 1 buffer which is updated when all game actions have updated the relevant object in the 2nd buffer.

But personally I don't like it, I'd (and have, frequently) employ Mark's approach.

Dessiedessma answered 5/9, 2009 at 9:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.