Discord
Login
Community
DARK THEME

Game loop with while / sleep

Officially one codes a microStudio game by implementing init, update and draw inside of a main module.

However, this also seems to work:

// Init code here ...

// Typical game loop ...
while true

  // Process events
  // ...

  // Update state
  // ...

  // Draw screen
  // ...

  sleep 100 // or whatever
end

What's your take on this?

Are there advantages / disadvantages on using this approach over the official one?

Thanks in advance.

I just saw a post mentioning a custom drawing approach using every.

So I guess an alternative to what I initially posted would be:

every 200 milliseconds do
  // Process
  // Update
  // Draw
end

Simple animation demo:

x = -100
d = 5

while true

  // Update state
  if x >= 100 then 
    d = -5
  elsif x <= -100 then
    d = 5
  end
  x = x + d

  // Draw screen
  screen.clear("black")
  screen.drawSprite("icon",x,0,20,20)

  // Sleep (yield)
  sleep 10
end

Update and draw are not called one after another - they are called in separate coroutines that run in parallel to each other.

Update is guaranteed to be called exactly 60 times per seconds (unless you drop the ball and put very expensive logic inside the update function). This is crucial for game state updates, as most calculations take into consideration how much time passed since the previous update. This is important, because you want things like movement of characters to be constant, instead dependent on frames per second. With update being guaranteed to be called every 0.016(6) you can design the mechanics of your game with that in mind. Otherwise, you need to have some way of knowing exactly how much time passed since the previous update call (often called delta time in engines without this "60 times per second" rule). And no - sleep 100 at the end is not equal to delta time. Every single instruction inside your loop takes time to be executed and calculating exactly how long each iteration take is not feasible, even in such trivial example, like yours.

Draw, on the other hand, is called whatever number of times per second the renderer is able to process it. Drawing is a very complex and computationally expansive thing to do, so by adding more and more sprites on the screen, you'll increase its processing time exponentially. This in mind, drawing is most likely the most difficult thing your game is going to do, since usually the game's logic don't come close to the underlying complexity of the rendering. This means that most of the CPU power that your player has will be used for this. That's why it's so important to split game state updates and rendering. Your update coroutine ensures that your game's logic runs with the same speed, no matter how fast or how slow a player's CPU is. Your draw coroutine will work with whatever CPU power is left, which means one player will have 20 fps and others will have 60 fps, but for both of them the player character will move the same distance in 1 second.

So there are plenty of disadvantages to the approach you're proposing. And if there are any advantages, I don't know it.

Very detailed explanation, thanks @Sebastian 👍

Thanks also from my side @Sebastian (btw, my name is also Sebastian ;-)) for your detailed explanation.

I see and understand the advantages you are enumerating.

I guess for really computational expensive games it does make sense to take advantage of the update / draw split for the reasons you mentioned.

From your explanation (and unless somebody says it's forbidden, or strongly shunned upon) I take that the approach I'm citing is also not completely out of question, but is not suitable for computational and drawing-intensive cases.

But then I guess that for simple, say puzzle games, which are neither computational or drawing intensive, my cited approach would be at least acceptable.

As to why one would do this (that is, what "advantage" does it bring) I think it's purely subjective and cognitive. I come from other environments where this split in update / draw does not exist (and I'm not alone in this) and I wanted to at least consider the possibility to structure game-logic-rendering code in such a way.

As for update being guaranteed to be called 60 times per second ... is this really so? Can this really be the case? (that it's guaranteed) You said that yourself that it depends on how computational-heavy the code inside of that is. I also guess that, being this JavaScript, is some other co-routine takes a lot of time then a deadline call might be missed ...

Another advantage (maybe?) of the approach I am proposing could (?) be less CPU, and therefor power consumption (critical especially in mobile phones) ...?

I guess with a while / sleep loop one has more control how often things are called and if I don't need compute-intensive-updates or smooth animations I can get away running my loop less often than 60 times per second and save energy ...

Or I am mistaken?

Definitely nothing wrong with it @sebnozzi and as you say, has its uses. As you see on the discord, there are others that prefer the more linear flow.

In the end, personal taste and it depends on the application.

Live Long and Tinker

Keep in mind that when you're inside the sleep call, your game also don't register player's input. This will feel as the game is not responsive.

Generally, I think it's a bad idea. I would even go as far as to say is that you're doing yourself a disservice by trying to use the approach you're proposing. All it does is teaching you bad habits. It's objectively worse from update-draw separation, and I would even go as far as to call it an anti-pattern.

So to me this is as much a matter of a personal teste as much as making a choice between an easier, but broken solution and putting in the work to understand the right way of doing this.

I was feeling tempted to agree with Sebastian on this (and I still kinda do), but I tested it and I don't see many concerns about it. Although I do see a few.

My initial worry was that this would be stalling the engine's loop, but it isn't. Everything seems to run fine. mouse and keyboard are being updated, you can count delta time as normal, you can print out system.fps just fine, etc. It seems like the engine is running on separate threads, so everything seems to be working as normal.

I actually feel curious about what @gilles would say about this, as he knows everything that's going on under the hood.

The concerns I still have are just these:

1 - the load order of the game files is unpredictable, so if you spread your project to more files, your loop may start running too soon, before everything is initialized, and you'll get errors and bugs. You could work around that by placing your loop in init (it still works the same), because init only gets called after all files are loaded:

init = function()
  while true

    // loop stuff
 
    sleep 10
  end
end

2- your loop is going to be slower than the engine's loop. Because it's a microscript loop, not a JS loop. The embedded language is always inherently slower than the host language, because it's being interpreted by it.

I mean, here's the thing, if it works, it works, and if you can't find faults with it in a particular use case, then I don't see any reason for me to try and stop you from doing it. But I'm guessing you'll run into some issues with it, eventually.

Post a reply

Progress

Status

Preview
Cancel
Post
Validate your e-mail address to participate in the community