3 ms·
TL;DR: I'm experimenting with that problem now. Currently I'm trying out this idea of 'transient state' described more below. ------- I've actually been worki
by westoncb 8y ago
TL;DR: I'm experimenting with that problem now. Currently I'm trying out this idea of 'transient state' described more below.
-------
I've actually been working on something that deals a lot with that problem. It's a game with all the rendering done in a single pixel shader.
All the gameplay logic is in a class called GameStateTransformer. The idea was to treat the game like a dynamical system where the state evolves as a function of 1) time 2) a sequence of 'events'. The core of it is a loop that runs 60 times per second, evolving state via time-dependent logic and by grabbing/processing any events in the queue at that time.
The rendering (i.e. view) is connected to the state (i.e. model) by executing a function `mapStateToUniforms()`[0] every frame. It always works the same way regardless of whether there is an animation going on or whatever; animation logic just updates parts of the state that will get mapped to GLSL uniforms (mostly it's things like `deathAnimRatio` taking on values between 0 and 1.)
The situation with events taking place over time was certainly the most complex architectural aspect I had to deal with. There were two main things I used:
1) 'transient state'; I would set up a bit of temporary/transient state by making a call like `runTransientState('player.dying', {deathPosition: blah}, 1.5)` —first arg is a path into the state object, second is a 'data' object, third is the amount of time it should remain in the state. After that call my state object would contain something like `{..., player: {..., dying: {position: blah}}}` So other code could check `if (state.player.dying)` and access related sub-data (e.g. `state.player.dying.position`); and that part of the state would be deleted after 1.5 seconds.
2) Emitting events at key points during animations/transitions (generally when they finish). Also the 'transient state' system always emits an "[transientStatePath]_finished" event when a bit of transient state's time runs out (e.g. "player.dying_finished").
There was another subsystem I put in place related to this which I haven't had to use much so far though: 'contingent evolvers', which are objects that define a `condition(state)` function and an `evolve(state, deltaTime)` function, and are automatically only executed while their condition is met.
Not positive how well this would scale to larger applications, but it has worked well for the game so far.
Source is here if you're curious: https://github.com/westoncb/under-game https://github.com/westoncb/under-game
[0] For those unfamiliar with GLSL 'uniforms' they are variables passed into your shader from the application code. Since all of my rendering is in a pixel shader, the code there is function of the current values of the uniforms + x,y coordinate of some pixel.