◀ prevnext ▶

How to programm Button Down, Up and Hold with 3 lines of code

Programming · Aug 1st, 2022

Maybe you are programming your own engine, and the API you are using to get buttons just returns whether a button is pressed or not. But more often than not, you only care about the single frame the button is pressed down, or released. Maybe you are already using an engine, that provides these features, but you want to implement a virtual button, and now you need to implement that behavior yourself.

Whatever your reason, you have a button that is either on or off, and you want to compute button down, up and hold. Here's a full implementation in Rust:

#[derive(Default)]
pub struct Buttons {
    up: u32,
    down: u32,
    hold: u32,
}

pub trait IButtons {
    fn up(&self) -> u32;
    fn down(&self) -> u32;
    fn hold(&self) -> u32;

    fn update(&mut self, new_state: &u32);
}

impl IButtons for Buttons {
    fn up(&self) -> u32 {
        self.up
    }

    fn down(&self) -> u32 {
        self.down
    }

    fn hold(&self) -> u32 {
        self.hold
    }

    fn update(&mut self, new_state: &u32) {
        self.up = !new_state & self.hold;
        self.down = new_state & !self.hold;
        self.hold = *new_state;
    }
}

You may or may not be able to read Rust. That's fine. The key things you need is the struct and its update function. The rest is just boilerplate stuff, to provide an interface to client code. So here's the solution again, with the unimportant stuff stripped away:

pub struct Buttons {
    up: u32,
    down: u32,
    hold: u32,
}

fn update(&mut self, new_state: &u32) {
    self.up = !new_state & self.hold;
    self.down = new_state & !self.hold;
    self.hold = *new_state;
}

Note: This wont compile. As I said, the boilerplate was removed. Treat it as pseudo code.

Let's dissect this, so you can adapt it to your programming language and use case. The Buttons struct stores 3 values. Each value is a 32 bit unsigned integer. Each bit represents a single button, and whether it is pressed (1) or not (0). We need 3 ints, one for button down, button up and button hold. This means this struct stores 32 different buttons, and their down, up and hold state.

Depending on your usecase, 32 buttons may be too much or too little. So feel free to change it to a bool or an integer with more bits.

Now let's talk about the update function. It takes 2 parameters: self and new_state. self in Rust is literally the same as the this pointer in C/C++ and many other programming languages; it references the object itself, in this case an object of the Buttons struct. new_state is a snapshot of the signal, being fed into the buttons. This is simply your on/off signal.

The three lines in the update function are where the magic happens. The trick is, that self.hold is updated last. Thus, when computing self.up and self.down, self.hold stores the button value of the previous frame. If the previous frame was 1 and the current frame is 0, self.up will be 1, otherwise 0. If the previous frame was 0 and the current frame is 1, self.down will be 1, otherwise 0.

Note 1: This implementation uses the bitwise-AND operator &. If you plan to use a bool for your system, I recommend using logic-AND && instead, because && is lazy and thus can be a tiny bit faster (Potentionally. Maybe. Maybe not. I give no guarantees.)

Note 2: In Rust, the bitwise-NOT operator is !. In your programming language it's probably ~ (Potentionally. Maybe. Maybe not. I give no guarantees.)

Above is a diagram, visualizing the output of the code, run for 6 frames. The button is pressed on frame 2, 3 and 4 (new state). Notice that button hold has the same value as new state. button down is 1 only at frame 2, the first frame the button was pressed. And button up is 1 only at frame 5, the first frame the button was released.

If you implement an interface like this implementation, you would call update() each frame, where your input is handled. Then, wherever you have a reference to a Buttons object, you can simply call down, up or hold to get the desired value.

And that's literally it. Have a good one 😊

Next Post: Running Tests in Series in Rust

Programming · Aug 28th, 2022

Next Post: Running Tests in Series in Rust


Programming · Aug 28th, 2022

Previous Post: Rebinding Controls via a RebindMatrix
More Programming related Posts