Sunday, November 1, 2015

Game state and input handling in component-based entity systems


My question is:


How can I handle game states in my entity system, without resorting to keeping a stack of game state objects around?



So the design of my entity system means that when an entity needs to register for input events for instance, the input component calls the input system and says "register this entity for this input". This is all fine and well, however if you add into this the concept of game states (say a pause screen), it becomes a problem to work out if an entity is in the current state and should receive the input.


I could augment the input component/system so that it says, "register this entity for this input while in these game states", but this requires that every entity know which states it's going to be used in, and that may not be obvious. Also, keeping a list of game states around per registered input (and other systems that use callbacks) doesn't sound too efficient.


Another idea I had is since there will be an entity that represents the game state, mark that as being disabled, then when generating the input event check that the entity is not a descendant of a disabled game state entity. Seems expensive to work out the parent for every callback.


Another idea is to have all the systems store their data keyed against the current state, that way when generating the input, the target entity won't even be a candidate. However this really hurts the ability to allow communication between entities in different states (not so much a problem for pause screens, but think lock picking in Oblivion/Skyrim).


The only other idea I've had is to have all components handle a state change events and communicate with their relevant system to disable anything they have registered, and re-enable it when switching back to this state.


The second (mark an object as disabled) and forth (have each component deal with state changes) seem like the best of my ideas, but none of them jump out at me as being particularly great.


Does anyone else have any other ideas on how to do this?


edit While I talk about input specifically in this question, it can mean any system capable of sending messages/events to entities, such as collisions, timer events, etc...



Answer



What is often used is an intermediate Intent System which abstracts the input and keeps track of the context and relevant gamestates.



The Intent system will stop transmitting inputs when the simulation is paused for example. It also handles the mapping between controller events and intents (move in direction, run, shoot, reload...).


This way your other conponents are not dependent on specific gamepads/inputs (BUTTON_A, BUTTON_B vs BUTTON_X, BUTTON_O...) but they all react to the same intents (IntentRun, IntentReload...).


Another advantage is that the intent system can be aware of available controllers being added/removed, as it can send intents to any subscriber even outside the simulation you can handle intents like AddPlayer(controllerID).


How much information about the game state you provide to the system either through events/message or directly is up to you. But the time invested in the Intent system is usually worth it.


You can manage Intent Contexts which will generate intents when they are attached to the system.


The context can be prioritized, i.e.:



  • SimulationAvailableContext sends intents to the simulation while it is available (but not running) for example move the camera, zoom in zoom out, add/remove player...

  • SimulationRunningContext sends intents to the simulation while it is not paused move player, send unit to position, shoot...



This way you can add and remove the contexts which are currently relevant.


And one thing about the whole intent systems is that it should run while the simulation is paused.


One way which is often used to play/pause the game simulation without breaking non simulation related updates is to use a different sets of times. i.e. GenericSystem::onTime(Long time, Long deltaTime, Long simTime, Long simDeltaTime).


With this approach your engine can simply block the increments on the games's simTime which in turn will block updates on the relevant animation & physics engines which use simTime and simDeltaTime while allowing continuous updates of your camera spring effect if it has to move even during pause, the animation of the loading effect on a virtual in-game billboard while data is being downloaded...


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...