Jump to content
  • Advertisement
Sign in to follow this  
DanielWilkins

2D Engine Main Loop - Need advice

This topic is 2347 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi.
I am developing a simple Bomberman clone using SDL and C++. Collision detection, movement, animation have all been implemented. The problem is that the character often seems to skip across the screen. I have researched this and found this to be temporal anti aliasing due to the frame rendering between updates.

I have found two main solutions and wanted input. This engine code should be as smooth as possible and I want to reuse it so I will put in the effort now to understand it. I have found an article on fixed timestep. I really wish I could implement it but I am very confused about how it applies to a non physics application or maybe just to something simpler like moving a character across the screen.

This is the article I am referring to: http://gafferongames...-your-timestep/

Is this method a little overboard for a non physics intense game?

Can anyone elaborate on what it does and how I would apply it to simple position updates on an x/y plane.

There is also another article I found which covers quite a few different main game loops: http://www.koonsolo....tters-gameloop/

I tried to implement the last option but found that often I got some unexpected warping where it would interpolate that I would be somewhere and then change its mind. It's possible it was a bug on my part (e.g. not factoring it into collision correctly).

Does anyone have a solid 2d main game loop they can break down and explain. I mean, I understand how it works, the components relating to handling input, updating logic and rendering, I am just confused sometimes about how the time is applied to those updates.

Thank you for your time.

Here is my main loop as it stands:



int main(int argc, char *argv[])
{
const int TICKS_PER_SECOND = 60;
const int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
const int MAX_FRAMESKIP = 10;

double next_game_tick = timer.getTime();

int loops;

// Initialize everything
if(!init())
{
errorLog.writeError("Failed to initialize engine components!");
return 1;
}

// While the user hasn't quit
while(quit == false)
{
loops = 0;
while( timer.getTime() > next_game_tick && loops < MAX_FRAMESKIP)
{
update();

next_game_tick += SKIP_TICKS;
loops++;
}
}

render.cleanup();

return 0;
}


Chanz Edited by Chanz

Share this post


Link to post
Share on other sites
Advertisement
The idea of a fixed timestep loop is that you de-couple rendering the scene from doing logic update. In your code, I don't see an explicit render() call of any sort, so I assume you are rendering inside update(), which isn't optimal.

Here is the basic loop that I use for just about every project I do, stripped of fluff:



function CoreKernel:run(app, startingstate)
self.statecontext=startingstate
self.running=true
self.oldtime=app:getTime()/1000
self.logicupdatecounter=0
self.logicstep=1.0/self.statecontext.updaterate
event=SEvent()

local thistime=0
local timesincelastshow=0
local framecount=0
local totaltime=0


while(true) do
-- Process input
while (app:getEvent(event)) do
self:handleEvent(event) end
if self.running==false then return end
end

self.curtime=app:getTime()/1000
self.framedelta=(self.curtime-self.oldtime)
totaltime=totaltime+self.framedelta
thistime=thistime+self.framedelta

self.logicupdatecounter = self.logicupdatecounter+self.framedelta
while(self.logicupdatecounter >= self.logicstep) do
-- Update logic
self:updateLogic()
self.logicupdatecounter = self.logicupdatecounter - self.logicstep
end

-- Render
self.percentwithintick = self.logicupdatecounter / self.logicstep
self:addFrameTime(self.framedelta, self.percentwithintick)
app:beginScene()
self:render()
app:set2DMode()
self:renderUI()
app:endScene()
self.oldtime=self.curtime
framecount=framecount+1
end
end



The above loop has all the applicable parts. First, it empties the input event queue and handles all events. Then it accumulates time and executes logic updates if it is time to do so. Then it renders the scene. Two time values are used in rendering, applied in the addFrameTime call: elapsed (self.framedelta) is the value of how much time has elapsed since we started the loop, and is used to advance timers. percentwithintick is used to interpolate objects' Last and Current position and update scene node objects accordingly for smooth rendering.

Share this post


Link to post
Share on other sites
Thank you. I guess what I am confused about is the whole interpolation and the function addFrameTime. What exactly does it do. I understand that the logic updates until it is larger than the logic step and then when rendering the position values are interpolated.

Are these values actually modified? Or are they just interpolated for the rendering (i.e. does the rendering actually modify the values or does it just modify them temporarily for the rendering and then they are permanently changed when the next update() function is run).

What exactly does addFrameTime do? And do you pass this value to your rendering call? Is it possible that it can interpolate a value past collision and have something interpolated into a wall for instance?

Thank you.

Share this post


Link to post
Share on other sites
You're welcome to take a look at my Old Blog (linked in my sig) as it has the steps and the code to one of my games. I do, however, use a 2d physics engine, and I would HIGHLY encourage you to look into using one yourself at some point as it makes writing 2d games EXTREMELY easy.

Share this post


Link to post
Share on other sites
Every object stores its Last position and its current position. In addFrameTime, these values are interpolated and the result is used to set the position of the object's scene node. In UpdateLogic, the Last position is set to Current, and a new Current position is generated based on movement if any movement occurred. For purposes of logic, the object's position is considered to be Current; Last is only stored for the purpose of interpolating to smooth out movement.

Share this post


Link to post
Share on other sites

Every object stores its Last position and its current position. In addFrameTime, these values are interpolated and the result is used to set the position of the object's scene node. In UpdateLogic, the Last position is set to Current, and a new Current position is generated based on movement if any movement occurred. For purposes of logic, the object's position is considered to be Current; Last is only stored for the purpose of interpolating to smooth out movement.


Brilliant! Thank you.

And yes BeerNuts, I will have a look at the source thank you.

Share this post


Link to post
Share on other sites
Here's my gameloop, as simple as can be.

However, before the loop, I make a call to

App.SetFramerateLimit(60); This limits the framerate to 60 fps, which is crucial for the physics simulation. It makes things easier. Also, cpSpaceStep() is the physics engine simulaiton step, and I tell it 1/60 seconds has passed since the last step, so it will move the objects accordingly in my world.


1 // main game loop
2 while (App.IsOpened())
3 {
4 pOurPlayer->CheckInput();
5
6 cpSpaceStep(pMap->GetSpace(), 1.0f/60.0f);
7
8 /* clear screen and draw map */
9 App.Clear(sf::Color(200, 200, 200, 255));
10
11 pMap->Draw(pOurPlayer->GetBody());
12
13 // Update all the bullets in the world
14 GameData.UpdateBullets(pOurPlayer->GetBody());
15
16 // check for items as well
17 GameData.UpdateItems(pOurPlayer->GetBody());
18
19 // Finaly update all enemies
20 GameData.UpdateEnemies(pOurPlayer->GetBody());
21
22 pOurPlayer->Draw();
23
24 App.Display();
25
26 if (CheckGameEnd(&App))
27 {
28 App.Close();
29 break;
30 }
31 }
Edited by BeerNutts

Share this post


Link to post
Share on other sites
Beernuts, so the physics engine has the timestep built in and controls the aspect of updating the players? I am writing a simple Bomberman clone. Would using a robust physics engine be overkill? Even if so, I will use this method if I have something a bit more complicated.

Thank you!

Share this post


Link to post
Share on other sites

Beernuts, so the physics engine has the timestep built in and controls the aspect of updating the players? I am writing a simple Bomberman clone. Would using a robust physics engine be overkill? Even if so, I will use this method if I have something a bit more complicated.

Thank you!


Yes, basically, you give every physical object in your game some values to define what it's doing. Walls are static objects with infinity mass, and never move. You give the player and enemy some force or velocity, and just step the physics engine. When collisions happen, the engine handles the object responses (and, hitting an immoveable wall, and giving the player 0 elasticity, the player would just stop), but you can register for callbacks for collisions between certain objects.

Well, if you are making a true bomberman clone, then, for your first foray into physics engine, maybe you shouldn't use it. I would probably still use a physics engine since I'm quite familiar with everything, but, yes, in general, it would probably be overkill.

Let me just say that for anything more complex than bomberman, physics engines make everything so much easier. I made a side-scrolling space shoot-em up years ago and didn't use a physics engine. If I had, the game would've been so much easier, and I could've made cooler objects and maps with the extra time.

Share this post


Link to post
Share on other sites
I have made some progress but am still confused a bit:

First of all, I keep track of locations in integers. I do this because I didn't assume you could be between pixels but now I realize when I am using doubles that effect the x and y values, I might have to make the switch. Edit: I definitely have to make the switch. It seems as if it was dropping the precision on the speed. 0.9 became 0 and 1 was way too fast.

Here is my revised main loop:



int main(int argc, char *argv[])
{
// Initialize everything
if(!init())
{
errorLog.writeError("Failed to initialize engine components!");
return 1;
}

// Time variables
double t = 0.0;
const double dt = 0.01;
double currentTime = SDL_GetTicks();
double accumulator = 0.0;

// Run until the user has requested a quit
while ( !quit )
{
double newTime = SDL_GetTicks();
double frameTime = newTime - currentTime;

if ( frameTime > 0.25 )
frameTime = 0.25;
currentTime = newTime;

accumulator += frameTime;

// Loop until we are ready to render, updating as we go
while ( accumulator >= dt )
{
update( dt );
t += dt;
accumulator -= dt;
}

// Get the time we use for interpolation
const double alpha = accumulator / dt;

// Interpolate the values
player.interpolate(alpha);

// Render the scene
renderScene();

}
return 0;
}


When I interpolate the values, I do so like this:



void Player::interpolate(double alpha)
{
renderXPos = curXPos * alpha + lastXPos * (1.0 - alpha);
renderYPos = curYPos * alpha + lastYPos * (1.0 - alpha);
}


My question is how delta time and time play a role in updating the physics. I have my speed as an integer at 3. It moves about how fast I want it. In the main player update function:



void Player::update(double dt)
{
float pSpeed = speed * dt;

...
collision handling, etc..
position updating...
}


Am I using delta time correctly? Should I be applying it to speed or did I miss the point. Thanks to everyone who has helped me understand this so far.

Edit: Apologies for the formatting. Not sure why it undoes the formatting I have in my IDE. Edited by Chanz

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!