6
\$\begingroup\$

I've done some research regarding how to simulate a game world, and it seems that I need to decide whether I want to write a continuous or a discrete simulation.

From my understanding, in a continuous simulation, I simulate each entity based on the time passed from the last calculation. For example, if I have a moving entity which has the speed of N units per second and one second has passed since the last calculation, the entity will move N units. In a discrete simulation, I calculate "turns", and my entity will have a speed measured in units per turn.

From what I understand, discrete simulation is not as granular as a continuous simulation, but is easier to calculate. I'm not sure that I have this right. I'm in the planning stage of a Dwarf Fortress-like roguelike game and I'm not sure which method will be fine for my purposes.

What are the practical differences between continuous and discrete simulations, in terms of game mechanics and playability? In relation, what are the benefits and disadvantages of each simulation, and do I have any holes in my understanding of the concepts?

\$\endgroup\$
  • 1
    \$\begingroup\$ Possible duplicate of When should I use a fixed or variable time step? \$\endgroup\$ – wondra Feb 4 '17 at 16:47
  • \$\begingroup\$ Both versions you describe are discrete simulations. In both cases, you're advancing to a new state in discrete update steps (as opposed to something like solving a differential equation to get an expression for the system's behaviour over every continuous moment in a time interval). The difference is that one uses a fixed time step (which could be measured in turns as you say, or in units of time just as easily), while the other uses a variable time step scaled to elapsed time. On that subject, you may find this earlier answer useful. \$\endgroup\$ – DMGregory Feb 4 '17 at 16:47
  • \$\begingroup\$ I did not say that I'll calculate once every second but I will calculate a new state as soon as the previous state calculation is ready. Or is that a discrete simulation as well? What is a continuous simulation then? \$\endgroup\$ – Adam Arold Feb 4 '17 at 17:08
  • \$\begingroup\$ That's still updating in discrete hops to a new discrete state. The jumps between your states are variable & may be quite small, but that does not make it continuous. A continuous sim would, as I said, be something like a differential equation modelling the dynamics of the system. In practice that's usually not feasible for game sims, so we almost always simulate in discrete steps. We might use continuous models within a step though — eg in continuous collision detection where we use math to sweep an object through its whole movement rather than looking for collisions at discrete substeps. \$\endgroup\$ – DMGregory Feb 4 '17 at 18:11
  • \$\begingroup\$ Oh I see. And which is better? Use turns or use time? \$\endgroup\$ – Adam Arold Feb 4 '17 at 18:24
3
\$\begingroup\$

(I apologise for the length hereof, but I'd like to clarify a few things to give you solid footing.)

All digital computer simulations are actually discrete in nature. This includes everything from a non-interactive fluid simulation to an OS with its reactive UI, to a game. Just as the refresh rate of your monitor is a discrete 60 fps, so are the operations run on a processor, also discrete. We don't use analog computers (anymore?). So the very idea of truly "continuous" simulations is debatable at this time. Nevertheless I know that you are trying to draw a distinction between turn-based and real-time.

Let's focus on real-time first. I must pose the question, real-time at what level in the code? It matters what the core language/platform provides, and what 3rd party engine libraries you are using, because those things totally alter the development landscape. But let's start at the most basic level.

Loop-based/real-time: Discrete stepping via for/while

At the foundation of any real-time code is the idea that the program moves screen elements in discrete steps, enough times per second that in the average case it appears like something moving smoothly as in real life. This can be as simple as moving a sprite from left to right, across the screen. If you were to look at the source code of any high level engine like Unity or UE or whatever, you'd find, in native code (C, C++) this fundamental for or while loop upon which the basic simulation occurs. Unity, for example, wraps this up under the hood, then you write your C# classes/methods which are called by that underlying native loop (UnityEngine). Most modern engines thus hide the loop from you, but you still know your logic is called every frame, even if you never get to see the loop code that does so. If you're using raw C and no libraries, you will on the other hand have to write that loop yourself, usually in main(), i.e. the root of your C application, and everything else follows from there.

Event-based, Automated interpolations etc.

Here we enter the territory of other sorts of software than games, such as web apps, GUIs and operating systems. Event systems are built for GUIs, and GUIs are highly reactive. Any guesses as to how they are so reactive? That's right - they too are fundamentally loop-based. A GUI system is an extension of the real-time, loop based system described above. That means that operating systems and games share some common heritage! But now there's more than just a loop. Events systems typically consist of some publisher-subscriber mechanism that broadcasts notifications, and a way to provide callbacks on notification receipt. This often (but not always!) ties into the underlying loop. Browser JavaScript and Unity are two platforms exemplifying this association.

Anyway, here's an example: Consider a JavaScript GUI web app that acts on events. Let's say you hit a button to load something. A spinner appears. While the loading proceeds, that spinner keeps moving smoothly onscreen. When the loading completes, the spinner stops. You didn't have to code any of the real meat that makes that spinner spin, or handles the loading and informs the underlying systems of the completion of that load. You just called placed an load spinner in your html, then called something like json.Load() in button.OnClick(). In the meantime, native code (your browser engine) was running a loop that kept that image animating. But you knew nothing about a loop - you just had a couple of simple functions tying into fabulous event system lying under the hood, whose own functioning relies on a loop-based, real-time system. In regards to the association between loop and event system that I described above, you will find with JavaScript that a load complete event will at the very beginning of the very next frame after the load actually completes in native code. This is a textbook case of an event system and loop working in conjunction to provide well-ordered operations.


Now that those representations have been made...


Real-time simulations

Flight Simulator, Wolfenstein, and classic Pong are purely real-time simulations. There is no turn-based element here. They are typically as fine-grained as you can get, often using every one of 60 frames of 16.6ms period each in a second, to perform new logic. Of course, this doesn't mean they always do; they can also wait to do certain tasks only periodically, such as AI updates.

Turn-based simluations

Angband, Nethack are a good example of a very simple turn-based game. These require no loop whatsoever in your code. User input comes from the keyboard which is handled by the OS, so you just let the OS notify your application and when a keyboard event occurs, and your functions otherwise know nothing about timing loops, and need not do so.

Battle Chess is a turn based game with real time animation, movement etc. Clearly, a loop is once again the basis for responding to user interaction. In this case it is also used for animation, gradual moves between tiles, special effects, etc. (possibly even for sound). But the difference here is that while such a game is fundamentally loop-based, it is preventing you from supplying user input for those periods when a move has just been made and your knight is fighting the enemy bishop: it just controls when & how you are allowed to supply input. Otherwise, it operates much like Pong, CoD etc.

World of Warcraft is another interesting example, being (I believe) (semi) turn-based on the server, and real-time on the client. One might also call this semi turn-based, although here we are getting into the more complex realm of distributed computing. It's certainly open to debate.

I want to say something more about such a system. We find constructs in many platforms / languages that do interpolations / animations over time, then fire an event on complete. What is crucial to understand here is that this is still loop-based under the hood, but from the end-developer's perspective, all you are doing is calling something like pawn.InterpolateTo(x, y, 5) to cause the real-time stuff to start happening under the hood, and once it has smoothly moved your pawn to the target location, it fires off a callback that notifies your code that the interpolation has completed 5 seconds later and this then ties back into your own higher level logic. This is crucial to understand: It is event based - but there is still a loop in action at some lower level! It may even be that if this is your own code, that loop is exposed both for step-wise use and for specialised interpolation subsystems.

Conclusion

You can't get away from using a loop if you want an interactive application, although if you use the right library or platform (Unity, Javascript, Flash or C with SDL frame callbacks all come to mind), then you will not need to write the loop yourself, and that will be a major boon as timing can be a complex issue. Again, most modern engines will call this for you, and you will just supply callback functions to be handled at defined times during each loop step/iteration/tick/frame.

Loop-based / real-time systems are required for any reactive/interactive system. Event-based systems, while often appearing very different to real-time systems, are often fundamentally associate with, if not driven by loops, and co-exist alongside loops in various different forms.

Your Game

For a Dwarf Fortress style game, I'd start with the following timing mechanism, see Fix your Timestep. Some here may be your code, some like the loop or vsync event mechanism may be 3rd party:

bool isRunning = true;
//assume milliseconds for all time* variables
int timeAccumulated = 0;
int timeStep = 1000; //period = 1000ms : frequency = 1fps
while (isRunning) //cpu
{
    tick();
}
//...or don't use such a loop, and instead call tick() on vsync event.

void tick()
{
    timeDelta = SomeMethod(); //may be yours, system's, or library's
    timeAccumulated += timeDelta; //see "Fix your Timestep"

    if (timeAccumulated <= timeStep)
    {
        timeAccumulated -= timeStep; 

        turn(); //call all your custom turn logic here
    }
    //inputs must have immediate effect, so are not called in turn()...
    isRunning = CheckKeyPressed("ESCAPE"); //for example
}

...So the outer block (while) controls actions which must have an immediate response, like queuing up input for use in the next turn, handling loads etc. OTOH the inner block (if) only allows this loop iteration to make game logic happen once a second has passed since the last turn was enacted.

\$\endgroup\$
  • \$\begingroup\$ This is exactly where I am right now after reading Game Loop. Thanks for the clear explanation nevertheless! \$\endgroup\$ – Adam Arold Feb 5 '17 at 10:28
  • 1
    \$\begingroup\$ @AdamArold You're welcome - shout if you need any further clarifications. \$\endgroup\$ – Engineer Feb 5 '17 at 10:30
  • \$\begingroup\$ Thanks for the help! I'm programming for 10 years but game development is fundamentally different from the Enterprise world. :) \$\endgroup\$ – Adam Arold Feb 5 '17 at 12:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.