5 ms·
It's probably not the author trying to say, but at a higher level a game is just: new_game_state = simulate_a_frame(old_game_state, user_input, time_delta) So
by raincole 3y ago
It's probably not the author trying to say, but at a higher level a game is just:
new_game_state = simulate_a_frame(old_game_state, user_input, time_delta)
So of course it's possible. Whether it's practical is another question.
- sph 3y agoMore generally, `new_state, framebuffer = update(old_state, input, time_delta)` How far away are we from being able to represent the GPU state and rendering instructions with simple datastructures ready to be submitted wholesale to the driver?
- tubs 3y agoThat’s pretty much what a command buffer is in any of the modern gpu apis. Recording it is still command driven but trying to give an entire app scene in one go is really messy. We tried this internally on our gpu before Vulkan.
- nottorp 3y agoI know it's harder, but can we please decouple state updates from rendering?
- nine_k 3y agoGPU could compute state separate from rendering, if such updates are simple and independent enough. (In many cases, they are not.)
- taneq 3y ago100%. This relatively small amount of extra work makes it SO much easier to debug both rendering and state update code.
- matsemann 3y agoEven better would probably be to run the state update X amount of times per second, not depending on framerate at all. Otherwise you mess with physics. And then render based on delta and interpolate movements.
- nottorp 3y agoI thought thats what i meant by decoupling :) Although using a time delta makes OT easier than having to force X times Per second
- matsemann 3y agoYeah my comment was probably supposed to be to a different sibling. Using a time delta would give different results when doing non-linear stuff. Like applying forces. It's different to apply "force * dt" vs doing "force * 0.5dt" twice. Also if you use a big dt you could end up going through walls or similar. So therefore the physics engine should run at defined ticks in most instances, or it will behave differently based on framerate. https://gafferongames.com/post/fix_your_timestep/ https://gafferongames.com/post/fix_your_timestep/
- iamflimflam1 3y agoIn all the, admittedly amateur, games I’ve made I’ve been very religious about this. It never even occurred to me that not splitting this was an option. And I can’t think of any reason why you’d combine them.
- roenxi 3y agoQuite practical I imagine, there is no reason to keep all the frames in memory. Keep the last n frames and get most of the benefits. The point of this programming style in a game is to have a few previous states when there is a crash/bug to figure out how the state ended up in a bad way. Maybe save a framesworth of state every keypress for debugging purposes. Then on a crash or bad state there is a debugging environment all ready to go, for the cost of a small amount of memory. Good deal. Although in practice "purely functional" programming is a bit silly here. The point of PacMan is all the IO which is fundamentally all side effects. There are a lot of practical compromises that would be called "functional" like storing all the major state of the game in a big array (something like [turn1, turn2, turn3, ...]) and then implement a turnX -> turnX+1 animation function. The fundamental insight of the functional approach is implementing a frame->frame function was already something that implicitly had to be done to make the game. If it is an explicit function then debugging it is easier. Because the input can be saved. No extra work is needed.
- taneq 3y ago> The point of PacMan is all the IO which is fundamentally all side effects. In fact, the point of a huge proportion of programming is IO and is "all side effects." It's still possible to adopt a functional style for internal calculations (and in fact this helps to decouple components and makes testing much easier, at the expense of relatively negligible copying overhead, so it's worth doing) but once you get beyond your internal toolkit and start actually trying to test the whole system, you're interacting with the outside world.
- epgui 3y agoFunctional programming is not some sort of mythical effect-free beast… All functional languages I know (including Haskell and Clojure) do side effects just fine, and there are plenty of different approaches for performing effectful computation sensibly within FP.
- sdenton4 3y agoCalling I/o side effects makes FP seem hopelessly academic. Everything having to do with the world is seemingly a corner case - the core of the programming philosophy is only concerned with the part where we push bits around uselessly. To be sure, I don't think this is a correct interpretation, but I do think it may play a role in the difficulty of getting people onboard with FP.
- retrac 3y agoAs an educational project, I wrote a PDP-8 emulator in Haskell that was structured essentially like that. It was a function from a Machine to a Machine, where Machine was a datatype representing the entire RAM, registers, and other associated state. This abstraction was almost fully stripped away by the compiler. Since the original Machine object is no longer required when the step function returns from executing an instruction, and since the size of the emulated RAM never changes, the compiler can, for example, represent the list of RAM values as an array, and mutate the array in place and internally just keep track of the pointer to the mutated object and return that. It isn't necessary to copy most of the data structure most of the time.
- __s 3y agoMade a mostly pure brainfuck in haskell where output collects to list in state & tape is a zipper. Machine type is Brain. Compute returns Brain & bool for whether program halted or is wanting input to overwrite current tape value & resume pure evaluation minihs keeps itself small, uses a stack of copies of source to implement []. Whereas the larger version parses the source to a vector of opcodes (which collapses repeated +-<>) & includes a jump table https://github.com/serprex/bfhs/blob/master/minibf.hs https://github.com/serprex/bfhs/blob/master/minibf.hs
- taeric 3y agoThis exact point is covered in the series of posts, though? With some thoughts on why this is difficult. I confess that sometimes things do, in fact, get a ton easier if you expand your abstraction out such that you are abstracting the whole board, and not a bunch of individual pieces. That comes with its own set of road blocks, though. In particular, game worlds are a lot bigger now than they used to be.
- pshirshov 3y agoIt is indeed practical. I was able to prototype a game engine in pure functional style which, after several development iterations was able to greatly improve performance and maintainability of the game. Essentially the game world and physics was represented as several layers of cellular automatons interacting with locks and condition checks. The production engine is not pure though due to various reasons.
- sandermvanvliet 3y agoI’m using this approach for an add-on I wrote for Zwift. Even in such a scenario the functional (state machine style) approach works really well and allows me to deal with all sorts of random inputs or glitches
- agumonkey 3y agoeverything is a reduce .. but then the reducer will diverge into a sad hardcoded state machine in lambda form
- 082349872349872 3y agoFor a book length (1960's course length) take on this idea, see Conway, Regular Algebra and Finite Machines
- agumonkey 3y agoI have a feeling I will enjoy it a lot.
- jjtheblunt 3y ago> Whether it's practical is another question. I imagine that comes down to how smart the compiler is.