Gamepads
To handle gamepads, we will use the gilrs library. This library abstracts platform specific APIs to provide unified interfaces for working with gamepads and supports a wide variety of controllers.
To integrate this library, let's first add gilrs
as a dependency.
cargo add gilrs -p phantom_dependencies
Then, export it in crates/phantom_dependencies/src/lib.rs
:
pub use gilrs as glm;
Let's extend this gamepad support to our game states by adding it to our Resources
bundle in crates/phantom_app/src/resources.rs
.
...
use phantom_dependencies::{
gilrs::Gilrs,
...
};
pub struct Resources<'a> {
...
pub gilrs: &'a mut Gilrs,
}
Next, we'll have to add an instance of Gilrs
to the Resources
that we declare in the application boilerplate in crates/phantom_app/src/app.rs
.
...
use phantom_dependencies::{
anyhow::{self, anyhow},
env_logger,
gilrs::Gilrs,
...
}
#[derive(Error, Debug)]
pub enum ApplicationError {
...
#[error("Failed to initialize the gamepad input library!")]
InitializeGamepadLibrary(#[source] gilrs::Error),
...
}
...
pub fn run(...) {
...
let mut gilrs = Gilrs::new().map_err(ApplicationError::InitializeGamepadLibrary)?;
...
event_loop.run(move |event, _, control_flow| {
let resources = Resources {
...
gilrs: &mut gilrs,
};
...
})
}
pub fn run_loop(...) {
...
if let Some(event) = resources.gilrs.next_event() {
state_machine
.on_gamepad_event(&mut resources, event)
.map_err(ApplicationError::HandleEvent)?;
}
...
}
...
At this point, you'll notice that we haven't implemented anything in our state machine to handle gamepad events. Let's add a method to our State
trait in crates/phantom_app/src/state.rs
!
...
use phantom_dependencies::{
...
gilrs::Event as GilrsEvent,
};
...
trait State {
...
fn on_gamepad_event(
&mut self,
_resources: &mut Resources,
_event: GilrsEvent,
) -> Result<Transition> {
Ok(Transition::None)
}
}
With this declared, we can now command our state machine to forward gamepad events to the game states.
Add the following method to our StateMachine
in crates/phantom_app/src/state.rs
.
pub fn on_gamepad_event(&mut self, resources: &mut Resources, event: GilrsEvent) -> StateResult<()> {
if !self.running {
return Ok(());
}
let transition = self
.active_state_mut()?
.on_gamepad_event(resources, event)?;
self.transition(transition, resources)
}
Let's make use of this new event handler by adding the following code to our editor at apps/editor/src/main.rs
.
use phantom::{
...
dependencies::{
anyhow::{Context, Result},
gilrs::Event as GilrsEvent,
...
},
};
...
impl State for Editor {
...
fn on_gamepad_event(
&mut self,
_resources: &mut Resources,
event: GilrsEvent,
) -> Result<Transition> {
let GilrsEvent { id, time, event } = event;
log::trace!("{:?} New gamepad event from {}: {:?}", time, id, event);
Ok(Transition::None)
}
}
Now, with a controller hooked up you'll be able to interact with your application!