PistonDevelopers / input

DEPRECATED - moved into the piston repo
MIT License
7 stars 11 forks source link

Provide a simple input tracker #107

Open RinCamelia opened 9 years ago

RinCamelia commented 9 years ago

Right now, input is passed to a Piston engine game by PressEvent, ReleaseEvent, and various kinds of mouse events from the event library. It's relatively trivial to convert this into input state usable by an update loop, however, I feel it would be of benefit to provide a convenience class within Piston that does this work for newer users. As an example, I threw together the below sample as practice to manage key and mouse button presses:

    use piston::input::Button;

    pub struct InputArray {
        current_buttons: Vec<Button>,
        last_buttons: Vec<Button>,
        temp_buttons: Vec<Button>
    }

    impl InputArray {
        pub fn new() -> InputArray {
            InputArray{
                current_buttons: Vec::new(),
                last_buttons: Vec::new(),
                temp_buttons: Vec::new()
            }
        }

        pub fn update(&mut self) {
            self.last_buttons = self.current_buttons.clone();
            self.current_buttons = self.temp_buttons.clone();
        }
        pub fn register_press(&mut self, button: &Button) {
            if !self.temp_buttons.iter().any(|b| b == button) {
                self.temp_buttons.push(button.clone());
            }
        }
        pub fn register_release(&mut self, button: &Button) {
            if let Some(index) = self.temp_buttons.iter().position(|b| b == button) {
                self.temp_buttons.remove(index);
            }
        }
        pub fn pressed_state(&mut self, button: &Button, last_state: bool, current_state: bool) -> bool {
            self.current_buttons.iter().any(|b| b == button) == current_state && self.last_buttons.iter().any(|b| b == button) == last_state
        }
    }

Questions:

bvssvni commented 9 years ago

Maybe call it ButtonArray to not confuse it with the Input enum?

bvssvni commented 9 years ago

@RCIX I edited your comment to enable Rust syntax highlighting.

bvssvni commented 9 years ago

The update method allocates, which will make it slow. This could be made faster.

bvssvni commented 9 years ago

Also, release is slow because it removes from a Vec. Perhaps use HashMap instead?

bvssvni commented 9 years ago

Semantics is problematic, because a button does not necessarily give you the state you want to check. For example, if a character is moving forward, then character.moving_forward could be set to true on the button press. In most cases you want to map the input to some other state that describes how the application behaves.

bvssvni commented 9 years ago

When doing something reusable, one could think of it as a "controller" that lives in a separate library that you add when needed. For example https://github.com/PistonDevelopers/camera_controllers or https://github.com/PistonDevelopers/drag_controller

RinCamelia commented 9 years ago

Good feedback. Re: semantics, the idea is basically an input controller as you mentioned that manages all input events and presents some sort of API to access input state in an update loop (where you actually apply input state -> useful game logic). I'll start working on an updated version.

bvssvni commented 9 years ago

@RCIX If you want to work on a such library under PistonDevelopers I'll give you admin access so you can transfer the library. Alternatively I could create a new repo.

There has been some discussion about a cross platform input library https://github.com/PistonDevelopers/piston/issues/884 which sounds a bit similar.

Eskil Steenberg wrote an API called "Betray" in C which has lot of good ideas https://github.com/PistonDevelopers/input/issues/20

RinCamelia commented 9 years ago

Right now I'm writing the code in place in my toy Piston project, but eventually I'd like to create that repository. Cross platform input definitely sounds relevant, and this project would would probably be a good starting point as well.

agmcleod commented 9 years ago

:+1: i really like the idea of this. Whether it goes in a separate repo or not, definitely useful.

RinCamelia commented 9 years ago

Currently unable to test pending the previously referenced PR, but I have this:

use piston::input::Button;
use std::collections::HashMap;

pub struct ButtonController {
    current_buttons: HashMap<Button, bool>,
    last_buttons: HashMap<Button, bool>,
}

impl ButtonController {
    pub fn new() -> ButtonController {
        ButtonController {
            current_buttons: HashMap::new(),
            last_buttons: HashMap::new(),
        }
    }

    pub fn update(&mut self) {
        //leaving this as an idiomatic map until/unless performance becomes a concern
        self.current_buttons.iter().map(|(button, state)| self.last_buttons.insert(button, state));
    }
    pub fn register_press(&mut self, button: &Button) {
        self.current_buttons.insert(&button, true);
    }

    pub fn register_release(&mut self, button: &Button) {
        self.current_buttons.insert(&button, false);
    }

    fn current_pressed(&mut self, button: &Button) -> bool {
        if let Some((button, state)) = self.current_buttons.get(&button) {
            state
        } else {
            false
        }
    }

    fn last_pressed(&mut self, button: &Button) -> bool {
        if let Some((button, state)) = self.current_buttons.get(&button) {
            state
        } else {
            false
        }
    }

    pub fn pressed_state(&mut self, button: &Button, last_state: bool, current_state: bool) -> bool {
        self.last_pressed(button) == last_state && self.current_pressed(button) == current_state
    }
}

Population of the hashmap is lazy - a button is assumed to be unpressed until an initial PressEvent regarding one is passed in. Could be changed to full population if desired/more performant.

viperscape commented 9 years ago

Perhaps a HashSet is better for this, buttons in the set could be those that are activate, food for thought

RinCamelia commented 9 years ago

Oh, HashSet vs HashMap. Will consider.

On Wed, May 27, 2015 at 6:13 PM, Chris Gill notifications@github.com wrote:

Perhaps a HashSet is better for this, buttons in the set could be those that are activate, food for thought

— Reply to this email directly or view it on GitHub https://github.com/PistonDevelopers/input/issues/107#issuecomment-106129996 .

RinCamelia commented 9 years ago

That would be great, yes please.

On Wed, May 27, 2015 at 11:01 AM, Sven Nilsen notifications@github.com wrote:

@RCIX https://github.com/RCIX If you want to work on a such library under PistonDevelopers I'll give you admin access so you can transfer the library. Alternatively I could create a new repo.

There has been some discussion about a cross platform input library PistonDevelopers/piston#884 https://github.com/PistonDevelopers/piston/issues/884 which sounds a bit similar.

Eskil Steenberg wrote an API called "Betray" in C which has lot of good ideas #20 https://github.com/PistonDevelopers/input/issues/20

— Reply to this email directly or view it on GitHub https://github.com/PistonDevelopers/input/issues/107#issuecomment-106014573 .