The trick is to separate the logic simulation from other game loops (rendering, UI, input, sound, etc). So when a player pauses the game, everything else still more or less works. And the logic simulation should be able to take user "command" while being paused.
Most commands should mutate the game state and reflect in the UI immediately. A few commands that have to wait until the next tick should at least acknowledge the action result.
Pausing a game has a massive impact on the game experience. It lets you break the fourth wall experientially. Not wrong, but it changes the dynamic of the game.
Same as saving at any time does. As losing your loot or your life permanently does. Not wrong, but a hard choice that appeals to some players and not to others.
I used to pause pacman on my Atari 800 so I could run to church and sing in the choir or be an altar boy. Then I ran home and unpaused to continue. Sometimes in summer the computer over-heated and I lost everything while I was at church.
Lessons learnt? None, I think :)
Like torch flames and trees swaying in the wind.
But when you find a broken ancient seal in the forest, the giant creepy eyeball moving around in it keeps moving even when you pause the game, which helps emphasise how other-worldly it is.
Other pause some underlying simulation while still letting you modify the game state, as an expected part of gameplay, like a city builder. As the user might spend a significant amount of time in a paused state building things, it would be pretty visually unappealing to have the entire world completely frozen the whole time.
Others might pause all gameplay entirely, such as for displaying a menu, in which case pausing even environmental animations might make more sense since the user isn’t actively playing.
For the second type, I would much prefer some GUI element to indicate the simulation is paused rather than freezing the whole game world, such as a border around the screen or maybe a change of color theme of the GUI or similar.
A system is only correct relative to the transition system you wrote down. If the real system admits extra transitions that you care about (pause, crash, re-entry, partial commits), and you didn't model them, then you proved correctness of the wrong system.
While the game is paused, if a player were to click on the "level up" buttons for their skills, each click actually advanced the game by 1 frame - so it was possible for people to die etc. during a pause screen.
Unity has introduced the idea of coroutines (which were essentially yield based generators), and people started using them, and immediately encountered problems with pausing/saves.
Internally these coroutines compile down to state machines with opaque internals which are not necessarily consistent across compilers/code changes, and its very difficult to accomodate needs like pausing when using them.
From what I've seen, the usual answer is that people go back to hand-written state machines, and go through the pain of essentially goto programming to fix these issues.
switch(game_state):
case(paused):
<the paused logic goes here>
case(gameplay)
<updating entities and regular gameplay goes here>
You still have to be careful about how you implement "gameplay", though. For example if at any point you read the 'system clock' to do time-based stuff like animations or physics, then when you unpause you suddenly will have a couple minutes of advance in a place where you expect fractions of a second. float accum = 0;
while (running) {
poll_input();
poll_network();
accum += delta_time;
while (accum >= tick_time) {
accum -= tick_time;
update_ui(tick_time);
update_anims(tick_time);
if (!paused) {
update_entities(tick_time);
}
}
render();
}It's a simulation; why should clock time be involved to begin with? The only things that should care about clock time are those that exist outside the sim, e.g audio
Modern games can have the same issue. Even taking a capture of the exact graphics commands and repeating them, you'll sometimes see animated physics effects like smoke and raindrops. They're doing the work on the GPU where it's not necessarily tied to any traditional physics timestep.
It suggests a level of control way below what I would ordinarily consider required for game development.
I have made maybe around 50 games, and I think the level of control of time has only ever gone up. Starting at move one step when I say, to move a non-integer amount when I say, to (when network stuff comes into play) return to time X and then move forward y amount.
Typically any of the common modern engines with a "time scale" variable like that are not at all optimising anything in that way. It's likely that the physics engine won't be stepped with a zero delta time, which will reduce the time spent on physics, but that's more of a byproduct of how physics engines work[0] than an optimisation.
You would have to go out of your way to capture the scene and display it "under the pause menu" in that way. Not saying nobody does that, just that it's not something the engine is giving you for free nor is it related to the time scale variable.
Further, doing that won't necessarily reduce resource usage. For example, if there isn't some sleep time inserted in the main loop when in a menu, or v-sync[1] to limit the framerate, the result of the simplified scene (just the menu and the quad with the captured scene) is an extremely high framerate, which may or may not cook the hardware more than the in-game load.
[0] Typical rigidbody physics engines are only (what I'll call) predictably stable with a constant delta time (same dt every tick). And a common way to manage this is with a time accumulator main loop, that only steps physics for whole units of dt.
[1] And v-sync isn't a silver bullet. consider refresh rates, different hardware, different drivers, driver overrides.
article confirms my early theory I formed when reading the title about why would pause be complicated
You could use a knob to slow down any game to a stop. You could also press a button to go to a console that let you change memory.
It would even figure out which bit of memory kept the number of lives of you deliberately lost a life and it could see what decremented.
"Announcing TORMENT HEXUS, a match-3 roguelike about putting technofascist CEOs on the wrong side of skyscraper windows!
[...]
And remember: they SHOULD be afraid of us. #indiedev #indiegame"
Weird times.
Like, yes, the most likely people to respond to such a call for "stuff to put in an article on Kotaku" are probably developers that want some publicity. But this is hardly surprising.
The best approach would be using something like if(game_is_paused) return; in the game loops.
Slowing down time applies it universally. Otherwise you're going to need that condition to every single object in the game.
E.g. when you open the ingame menu, the inventory (etc) you usually want to pause the gameplay, but still want to interact with the UI. Sometimes that means that at least also some of the gameplay logic needs to remain alive (inventory management, crafting, levelling up, etc...).
There are also a lot of games which need some sort of 'active pause', e.g. the gameplay needs to stop while the user can issue commands to units (traditional example: real-time combat with pause like in Baldurs-Gate-style RPGs).
Sometimes the underlying engine also doesn't properly separate gameplay logic from rendering, e.g. you can't skip one without also skipping the other (which is an engine design bug, but similar situations may also happen up in gameplay code).
Finally: pausing and the save-game-implementation is often an afterthought, but really should be implemented as the very first thing. It's quite easy to run into the trap that a frame also needs to advance time. If the game has the concept of a fixed-duration 'game logic tick' which is independent from the frame rate you're already halfway there though, but many games simply use a variable-length game tick which is identical with the frame duration.
I'll add that the notion of the "time scale" variable as mentioned in the article is something that's only solidified/codified since Unity and the like came about. And at the same time, the way Unity et al. works[0] doesn't really encourage thinking about what I'd call "main loop logic" in the bottom-up way that's required to build a really robust system for managing states. You can do it, of course, (you can write as much code as you want) but by default, everything in the scene is "active" and somewhat independent from everything else, and you don't have direct control over the various major systems.
[0] I guess I should say "worked" -- I mostly used 3.x and a little bit of early version 4 -- I'm sure it's improved but I wouldn't expect anything drastically different.
I haven’t tried this yet, but for a custom engine I would introduce a second delta time that is set to 0 in the paused state. Multiplying with the paused-dt „bakes in“ the pause without having to sprinkle ifs everywhere. Multiplying with the conventional dt makes the thing happen even when paused (debug camera, UI animations).
Also there's a need for different time domains - like imagine, in a paused state the menu animations still need to play, or if the player enters a conversation, the game logic needs to pasue (depending on designer intent etc.)
Do people actually do that? What's the plan for when the user sleeps their machine? All the events just inexplicably happen all at once when they wake it?
In other domains, adding the delta time of your main loop to your timers can cause (logical) clock drifts in the long term because of resolution errors.
Inside the game loop, we would keep the global tick counter that incremented on every tick, and timeouts would be based on that rather than on UTC.
The tick counter was updated only when the game logic was actually running. Our approach to pausing was to not run the functions that handled frame updates or physics updates, and to only run the rendering functions.
Generally we would never care about actual world time other than for some timeouts like for network (as the time passes for everyone), or for easter eggs like changing the tree models for Christmas or so.
I don't think anyone serious would implement event timers based on real time.
Many game systems need to remain active in paused state (the UI needs to remain working for example, and actions in the UI may also manipulate game state (inventory, crafting, levelling up...). There are also plenty of games with 'active-pause' where the user can issue commands to units while in paused state.
Live migration boils down to copy memory over the network, stream the page faults till you converge enough, and resume execution on the other host. It’s not a hard problem but a precise and tedious one.
Pausing a game might involve a lot of GPU contexts to freeze, network resources to pause, storage streams to pause, input handling, sound, etc. Add to that physics engine that may be tied deeply in the system and you end up with a hard problem.
What a VM does is not the role of the hypervisor, thus it can apply its hammer that works in pretty much all cases, and VMs are pretty much all the same. On the other hand, all games are bespoke with custom plugins and custom integrations, which make them the opposite of "generic pause implementation".
It always surprised me how few games had that feature - though a few important ones, like StarCraft, did - and it only became rarer over the years.
There was a twitter thread years ago (which appears to be long gone) about how the SNES Pilot Wings pre-game demo was just a recording of controller inputs. For cartridges manufactured later in the game's life, a plane in the demo crashes rather than landing gracefully, due to a revised version of a chip in the cartridge. The inputs for the demo were never re-recorded, so the behaviour was off.
The example you mention of demo playback de-syncing when the circumstances slightly change, that is exactly what you get when you only record inputs from the player. Doom actually did this too for its networking model and demo playback system. That relies much more on the engine being deterministic and the runtime environment behaving consistently, because each client that replays those inputs has to run the exact same game simulation, in order for the resulting game states to match.
the other negative with deterministic input-based replay is what you've said -- if the engine deviates in any manner, the replay becomes invalidated. You'd have to probably ship with every version of the engine, and the replay just runs on the relevant release. Just replaying and re-recording the inputs on the new version wouldn't do anything, because the outcome behavior would inevitably out of sync with the original.
I'm also not sure how one would support scrubbing, except by also having inverse operations defined for every action or by fully-capturing state at various snapshots and replaying forward at like 10x speed.
Very common that replay/demo uses the network stack of it's present in a game.
https://github.com/ESWAT/john-carmack-plan-archive/blob/mast...
This used to be a promoted feature in CS, with "HLTV/GOTV", but sadly disappeared when they moved to CS2.
Spectating in-client is such as powerful way to learn what people are doing that you can't always see even from a recording from their perspective.
Halo 3's in-engine replay system was the high water mark of gaming for me.
Ah, the good old days of watching live competition of quake through the game itself, chatting with others basically through the game console.
Pretty cool system.
The game engine, Source, is also using client-server architecture
https://github.com/saul/demofile-net
I'm sure the technology still exists in the engine, but it's no longer the key feature it once was. HLTV/GOTV was launched with some fanfare back in the day.
https://news.ycombinator.com/item?id=21920508
A game having random mechanisms has absolutely nothing to do with whether it's deterministic.
Slay the spire is 100% deterministic, gameplay-wise. All the online poker games too.
The main downside which probably caused the diseapearance is that any patch to the game will make the replay file unusable. Also at the time (not sure for quake) there was often fixed framerate, today the upsides of using delta time based frame calculation AND multithreading/multi platform target probably make it harded to stay deterministic (specialy for game where you want to optimize input latency)
Networked games have a "tickrate", just for the networking/state aspect. For example, Counter-Strike 2 has a 64Hz tickrate by default. They also typically have a fixed time interval for physics engines. Both of these should be completely independent of framerate, because that's jittery and unpredictable.
I think if I remember right there were also funny moments where things didn't look right after patches?
The bigger problem is that floating point math isn't deterministic. So replays need to save key frames to avoid drift.
Quake used fixed point math.
I guess floats are still mostly deterministic if you use the exact same machine code on every PC.
Thank you for still prioritizing it.
As well as using special library versions of floating-point functions which don't behave the same across different processors I suppose, if you want to be safe.
Eg cr-libm[1] or more modern alternatives like core-math[2].
[1]: https://github.com/SixTrack/crlibm
[2]: https://core-math.gitlabpages.inria.fr/
There's no scenario in which that's desirable.
And yet even Rockstar gets it wrong. (GTA V has several framerate dependent bugs)
The only place where that doesn't matter is fixed hardware - i.e. old generation consoles, before they started to make "pro" upgrades.
What's totally insane is that the modern engine rewrite Aleph One can also play back such old recordings, for M2 Durandal (1995) and Infinity (1996) at least.
I am one of the authors of Fire Fight game (1996-ish) and we pulled the same stunt. It was actually easy, we just had to build our own "random number generator" and fix all bugs with uninitialized memory :-)
It’s one of my favourites
As a kid, I couldn't wait to see what came next. Sadly, Q1 was rather one of a kind, and it was many years until anything else like it showed up.
But then it'd also be nice if they fixed the "game crashes randomly when joining games" bug too.
(To give them credit, it doesn't now take 5 minutes after waking the Switch 2 before Rocket League reconnects me to the Epic servers like it did a couple of months ago...)
[0] Also the stupidly low limit on how many you can download - it's my storage cost, not yours, wtf.
[0] https://www.youtube.com/watch?v=yDUdGvgmKIw
[1] https://donadigo.com/tmx1
Warcraft 3 replays couldn't jump in time, just forward very fast. HoN could do that. It was amazing.
For a few months they even made ALL replays searchable on a website. Every game of HoN played globally.