Handling Input
Amethyst uses an InputHandler
to handle user input.
You initialise this InputHandler
by creating an InputBundle
and adding it to the game data.
extern crate amethyst;
use amethyst::{
prelude::*,
input::{InputBundle, StringBindings},
};
struct Example;
impl SimpleState for Example {}
fn main() -> amethyst::Result<()> {
// StringBindings is the default BindingTypes
let input_bundle = InputBundle::<StringBindings>::new();
let mut world = World::new();
let game_data = GameDataBuilder::default()
//..
.with_bundle(input_bundle)?
//..
;
Ok(())
}
To use the InputHandler
inside a System
you have to add it to the SystemData
. With this you can check for events from input devices.
extern crate amethyst;
use amethyst::{
prelude::*,
input::{InputHandler, ControllerButton, VirtualKeyCode, StringBindings},
core::SystemDesc,
derive::SystemDesc,
ecs::{Read, System, SystemData, World},
};
#[derive(SystemDesc)]
struct ExampleSystem;
impl<'s> System<'s> for ExampleSystem {
// The same BindingTypes from the InputBundle needs to be inside the InputHandler
type SystemData = Read<'s, InputHandler<StringBindings>>;
fn run(&mut self, input: Self::SystemData) {
// Gets mouse coordinates
if let Some((x, y)) = input.mouse_position() {
//..
}
// Gets all connected controllers
let controllers = input.connected_controllers();
for controller in controllers {
// Checks if the A button is down on each connected controller
let buttonA = input.controller_button_is_down(controller, ControllerButton::A);
//..
}
// Checks if the A button is down on the keyboard
let buttonA = input.key_is_down(VirtualKeyCode::A);
//..
}
}
You can find all the methods from InputHandler
here.
Now you have to add the System
to the game data, just like you would do with any other System
. A System
that uses an InputHandler
needs "input_system"
inside its dependencies.
extern crate amethyst;
use amethyst::{prelude::*, ecs::*, core::SystemDesc, derive::SystemDesc};
#[derive(SystemDesc)]
struct ExampleSystem;
impl<'a> System<'a> for ExampleSystem { type SystemData = (); fn run(&mut self, _: ()) {}}
let game_data = GameDataBuilder::default()
//..
.with(ExampleSystem, "example_system", &["input_system"])
//..
;
Defining Key Bindings in a File
Instead of hard coding in all the key bindings, you can store all the bindings in a config file. A config file for key bindings with the RON format looks something like this:
(
axes: {
"vertical": Emulated(pos: Key(W), neg: Key(S)),
"horizontal": Emulated(pos: Key(D), neg: Key(A)),
},
actions: {
"shoot": [[Key(Space)]],
},
)
The axis values range from -1.0
to 1.0
. For an Emulated
axis controller such as keyboard buttons, the values are distinct:
0.0
when neither, or both theneg
orpos
buttons are pressed.-1.0
when theneg
button is pressed.1.0
when thepos
button is pressed.
Values between 0.0
and 1.0
are possible when using a controller such as a joystick. This can be enabled via the "sdl_controller"
feature.
The action is a boolean, which is set to true when the buttons are pressed. The action binding is defined by a two-level array:
- The inner array specifies the buttons that must be pressed at the same time to send the action.
- The outer array specifies different combinations of those buttons that send the action.
The possible inputs you can specify for axes are listed here. The possible inputs you can specify for actions are listed here.
To add these bindings to the InputBundle
you simply need to call the with_bindings_from_file
function on the InputBundle
.
extern crate amethyst;
use amethyst::{prelude::*, input::*, utils::*};
fn main() -> amethyst::Result::<()> {
let root = application_root_dir()?;
let bindings_config = root.join("config").join("bindings.ron");
let input_bundle = InputBundle::<StringBindings>::new()
.with_bindings_from_file(bindings_config)?;
//..
Ok(()) }
And now you can get the axis and action values from the InputHandler
.
extern crate amethyst;
use amethyst::{
prelude::*,
core::{Transform, SystemDesc},
derive::SystemDesc,
ecs::{Component, DenseVecStorage, Join, Read, ReadStorage, System, SystemData, World, WriteStorage},
input::{InputHandler, StringBindings},
};
struct Player {
id: usize,
}
impl Player {
pub fn shoot(&self) {
println!("PEW! {}", self.id);
}
}
impl Component for Player {
type Storage = DenseVecStorage<Self>;
}
#[derive(SystemDesc)]
struct MovementSystem;
impl<'s> System<'s> for MovementSystem {
type SystemData = (
WriteStorage<'s, Transform>,
ReadStorage<'s, Player>,
Read<'s, InputHandler<StringBindings>>,
);
fn run(&mut self, (mut transforms, players, input): Self::SystemData) {
for (player, transform) in (&players, &mut transforms).join() {
let horizontal = input.axis_value("horizontal").unwrap_or(0.0);
let vertical = input.axis_value("vertical").unwrap_or(0.0);
let shoot = input.action_is_down("shoot").unwrap_or(false);
transform.move_up(horizontal);
transform.move_right(vertical);
if shoot {
player.shoot();
}
}
}
}