An engine for describing games built around Xerces, exprtk, and SFML.
Status: implemented
Three decoupled layers:
<action name="up"><move direction="up">step</move></action>.<input button="w"><trigger object="paddle1" action="up" /></input>. It has no idea what the action does.button="w".Consequences the author values:
up does for an object is one edit on the object, and every state that references it follows.up to whoever it addresses. This is polymorphism expressed in XML with no class hierarchy.The author points to this as one of the features that do not depend on the function-call syntax, and want to keep (03).
While a key bound to move.* is held, that direction’s step is recorded on the object. Velocity is recomputed from all four directions on every key change:
This replaced an earlier “last key wins” behavior in which a single key event overwrote a whole axis.
A key can be held while the state changes underneath it (hold Left, pause, unpause). The engine tracks every key’s press and release itself, so the question is what a state change should mean for a key that is still down. Three ways to answer it:
A. Latched to the press. A press runs the binding of the state that was active at that moment and remembers those commands. The matching release always goes to the same commands, whatever state is active by then. State changes neither cancel nor re-fire anything.
paused shows the player and binds only Space) does not stop it. The paddle keeps moving on the pause screen, and a key first pressed during the pause does nothing after unpausing until it is pressed again.B. Live held keys (built). The current state only gates what a held key means. On every state change the engine releases whatever the held keys were driving in the outgoing state, then resumes the incoming state’s continuous bindings (an action’s move.*) for the keys still down.
playing returns.fire run only on a real press. Otherwise holding Space through mainmenu to playing would press Space again in playing and pause immediately, and a held fire key would shoot on every unpause.Game::stateChangeCount() goes up on every push or pop, and Engine::syncHeldKeysToState() compares it after each key event and after each frame’s update (a <condition> can change the state too). CommandExecutor::executeHeldInput is the resume path.One-shot verbs are never resumed. fire and, since Frogger, hop.* belong to the press. A hop in an action is queued when the key goes down and made once; releasing does nothing, holding does nothing more, and coming back from a pause with the key still down does not hop again. That is the same rule that keeps a held Space from pausing straight after the menu, applied to a verb: a game whose input is a discrete step wants a fresh press for every step, and the resume path (executeHeldInput) only ever picks up move.*. The alternative for a hop, repeating while the key is held, would be a separate verb (or a repeat interval), not a behavior of this one.
C. Cancel on state change (not built). Leaving a state releases whatever the held keys had running there, and nothing is resumed on return.
B and C differ only on return: B resumes a held key, C waits for a fresh press. Switching to C means dropping the resume half of syncHeldKeysToState.