Class: RGame::Engine::InputMap
- Inherits:
-
Object
- Object
- RGame::Engine::InputMap
- Defined in:
- lib/rgame/engine/input/input_map.rb
Overview
What physical inputs mean, for one player.
map = InputMap.new(
thrust: { axis: [Controls::KEY_DOWN, Controls::KEY_UP], stick: Controls::AXIS_TRIGGER_RIGHT },
fire: { buttons: [Controls::KEY_SPACE, Controls::PAD_A] }
)
One entry per action, naming physical ids from RGame::Util::Controls directly. That is the whole point of this class: it is the single table a rebinding screen edits, and it holds nothing but integers, so the engine layer may own one outright.
Three kinds of source, and an action may combine them:
| Key | Reads as | Meaning |
|---|---|---|
buttons: |
held? |
down if any listed id is down |
axis: |
axis |
[negative_id, positive_id], or a list of such pairs — a digital axis from buttons |
stick: |
axis |
an analog axis id, for a real stick or trigger |
One table serves every device
Listing a key and a pad button in the same entry is safe, and needs no
per-device branching, because a device only answers for its own kind of
input — asking a gamepad about a keyboard scancode is false, never the
keyboard's answer (see docs/api/input.md). So fire can be "Space or A"
and each player's device picks out the half that applies to it.
An axis can have several pairs, like a button can have several ids
axis: [KEY_LEFT, KEY_RIGHT] is the common case and stays a bare pair.
A list of pairs binds more than one control to the same axis:
move_x: { axis: [[Controls::KEY_LEFT, Controls::KEY_RIGHT],
[Controls::PAD_DPAD_LEFT, Controls::PAD_DPAD_RIGHT]],
stick: Controls::AXIS_LEFT_X }
Without it a d-pad cannot drive movement at all, because the same action already needed the arrow keys. The largest deflection wins, so the pairs cost nothing on a device that has only one of them.
This replaces a two-stage scheme in which a game's action map named RGame::Core::Input's action names, which named physical ids — two tables in series, neither of them the one a config screen wanted, and the lower one unreachable from the engine layer, which may not name Core at all.
A stick's sign is the device's, not the game's
AXIS_LEFT_Y is positive downwards, like screen coordinates. An action
that wants the opposite ("thrust", "climb") negates at the call site or
binds a trigger instead — the map stays declarative rather than growing an
inversion flag that every reader would then have to check for.
Defined Under Namespace
Classes: Binding
Constant Summary collapse
- Controls =
RGame::Util::Controls
- SOURCES =
%i[buttons axis stick].freeze
- UI =
The universal set, merged into every map unless the game overrides it.
The UI package navigates and activates through these, so a control can rely on them existing for every player without a game having declared them. They are prefixed rather than plain (
ui_up, notup) so a game is free to use:upfor something of its own.ui_cancelis Escape, which is why RGame::Game's quit key is F2: the button a player expects to back out of a menu belongs to the menu. { ui_up: { buttons: [Controls::KEY_UP, Controls::PAD_DPAD_UP] }, ui_down: { buttons: [Controls::KEY_DOWN, Controls::PAD_DPAD_DOWN] }, ui_left: { buttons: [Controls::KEY_LEFT, Controls::PAD_DPAD_LEFT] }, ui_right: { buttons: [Controls::KEY_RIGHT, Controls::PAD_DPAD_RIGHT] }, ui_confirm: { buttons: [Controls::KEY_RETURN, Controls::KEY_SPACE, Controls::PAD_A] }, ui_cancel: { buttons: [Controls::KEY_ESCAPE, Controls::PAD_B] } }.freeze
- DEFAULT_ACTIONS =
A playable starting point: eight-way movement on the arrows or the left stick, and a fire button. A game that wants exactly this declares nothing at all.
{ move_x: { axis: [[Controls::KEY_LEFT, Controls::KEY_RIGHT], [Controls::KEY_A, Controls::KEY_D], [Controls::PAD_DPAD_LEFT, Controls::PAD_DPAD_RIGHT]], stick: Controls::AXIS_LEFT_X }, move_y: { axis: [[Controls::KEY_UP, Controls::KEY_DOWN], [Controls::KEY_W, Controls::KEY_S], [Controls::PAD_DPAD_UP, Controls::PAD_DPAD_DOWN]], stick: Controls::AXIS_LEFT_Y }, fire: { buttons: [Controls::KEY_SPACE, Controls::PAD_A] } }.freeze
Instance Attribute Summary collapse
-
#bindings ⇒ Object
readonly
Every action this map can answer for, as
name => Binding.
Class Method Summary collapse
-
.default ⇒ Object
The default map: the UI set plus DEFAULT_ACTIONS.
Instance Method Summary collapse
- #[](action) ⇒ Object
- #actions ⇒ Object
-
#initialize(entries = {}) ⇒ InputMap
constructor
entriesare merged over the universal UI set, so declaring a game's actions never costs it the ones the UI needs. -
#merge(entries) ⇒ Object
A copy with
entriesoverriding, which is how a game rebinds one action without restating the rest. -
#to_h ⇒ Object
The entries in the shape they were declared in, so a map can be edited and rebuilt (a config screen) or merged.
Constructor Details
#initialize(entries = {}) ⇒ InputMap
entries are merged over the universal UI set, so declaring a game's
actions never costs it the ones the UI needs.
108 109 110 |
# File 'lib/rgame/engine/input/input_map.rb', line 108 def initialize(entries = {}) @bindings = UI.merge(entries).to_h { |name, entry| [name, build(name, entry)] }.freeze end |
Instance Attribute Details
#bindings ⇒ Object (readonly)
Every action this map can answer for, as name => Binding.
104 105 106 |
# File 'lib/rgame/engine/input/input_map.rb', line 104 def bindings @bindings end |
Class Method Details
.default ⇒ Object
The default map: the UI set plus DEFAULT_ACTIONS.
113 |
# File 'lib/rgame/engine/input/input_map.rb', line 113 def self.default = new(DEFAULT_ACTIONS) |
Instance Method Details
#[](action) ⇒ Object
119 |
# File 'lib/rgame/engine/input/input_map.rb', line 119 def [](action) = @bindings[action] |
#actions ⇒ Object
121 |
# File 'lib/rgame/engine/input/input_map.rb', line 121 def actions = @bindings.keys |
#merge(entries) ⇒ Object
A copy with entries overriding, which is how a game rebinds one action
without restating the rest.
117 |
# File 'lib/rgame/engine/input/input_map.rb', line 117 def merge(entries) = self.class.new(to_h.merge(entries)) |
#to_h ⇒ Object
The entries in the shape they were declared in, so a map can be edited and rebuilt (a config screen) or merged.
125 126 127 128 129 130 131 132 133 |
# File 'lib/rgame/engine/input/input_map.rb', line 125 def to_h @bindings.to_h do |name, binding| entry = {} entry[:buttons] = binding. if binding. entry[:axis] = binding.pairs.size == 1 ? binding.pairs.first : binding.pairs if binding.pairs entry[:stick] = binding.stick if binding.stick [name, entry] end end |