Bit Masking: Control Movement

Photo by Euwen on Unsplash

This example is provided as a FlutterPen and I won’t detail how to create a Flutter app. Instead, I will explain the code that manages the state as a decimal value using bitwise operators like AND, OR, or NOT.

Control movement

We’ll alter a state using the WASD keys (or the equivalent arrow keys ⇧⇦⇩⇨) and use that state to move an object on the screen.

That object will be a logo and its default position will be centered on the screen. The AnimatedPositioned widget will help us to move a bit more fluid, but the example would also work with a simple Positioned widget.

Moving the A Flutter Dev logo using keys and bit masking

The state is kept in an integer and its default state equals 0. The binary representation where we need to store all 4 possible movement directions is 4 bit long: 0000

The mask values that we’ll use are as follows:

Keys and their equivalent bit position and decimal mask

The following code snippets are written in Dart (Flutter’s core programming language).

We define some constants that will hold our raw key codes and their masks. These will help us identify which key was pressed or released.

static const int keyW = 119;
static const int keyA = 97;
static const int keyS = 115;
static const int keyD = 100;
static const int arrowUp = 4295426130;
static const int arrowLeft = 4295426128;
static const int arrowDown = 4295426129;
static const int arrowRight = 4295426127;
// decimal masks
static const int maskW = 8; // 1000
static const int maskA = 4; // 0100
static const int maskS = 2; // 0010
static const int maskD = 1; // 0001

We then listen to keyboard events on the entire screen (while it is in focus).

return RawKeyboardListener(
autofocus: true,
focusNode: FocusNode(),
onKey: (RawKeyEvent keyboardEvent) {
// on keyboard event
}
);

When a specific key is pressed we make sure we turn on the bit corresponding to that key and when it is released we make sure we turn off only the corresponding bit. Turning a specific bit on is done using the |(OR) operator and the mask. Turning it off is done using the & (AND) operator and negation of the mask. The negation of the mask helps us keep the other bits unaltered.

For example, below we have the code that does what I just described:

// the complete code is in this CodePen
if(keyboardEvent.runtimeType == RawKeyDownEvent) {
switch(keyboardEvent.logicalKey.keyId) {
case keyD:
case arrowRight:
state |= maskD;
break;
case keyS:
case arrowDown:
state |= maskS;
break;
case keyA:
case arrowLeft:
state |= maskA;
break;
case keyW:
case arrowUp:
state |= maskW;
break;
}
} else {
switch(keyboardEvent.logicalKey.keyId) {
case keyD:
case arrowRight:
state &= ~maskD;
break;
case keyS:
case arrowDown:
state &= ~maskS;
break;
case keyA:
case arrowLeft:
state &= ~maskA;
break;
case keyW:
case arrowUp:
state &= ~maskW;
break;
}
}

After we altered the state we process it and do what is to be done:

// the complete code is in this CodePen
if(state == 0) {
left = (MediaQuery.of(context).size.width-PenConfig.logoSize)/2;
top = (MediaQuery.of(context).size.height-PenConfig.logoSize)/2;
} else if(state != (maskA+maskD+maskW+maskS)
&& state != (maskA+maskD)
&& state != (maskW+maskS)) {
left += (state & maskD == maskD)?moveBy:0; // move right
top += (state & maskS == maskS)?moveBy:0; // move down
left -= (state & maskA == maskA)?moveBy:0; // move left
top -= (state & maskW == maskW)?moveBy:0; // move up
}

A state equal to 0 means that no key is pressed and in this case, we set the position of the AnimatedPositioned widget to screen's center.

Then, after we make sure not all keys or opposite direction keys are pressed at the same time, we modify the visual positioning of the logo with the moveBy amount to reflect the state's current value.

Conclusions

This way we manage to condense the state of 8 buttons (2 for each direction) on 4 bits inside a single integer.

The alternative would be to store each key state in a dedicated boolean value and then manage 4 variables.

When it comes to an online multiplayer game having a player input mapped like this may save important time in sharing user actions across all players and servers.

Also, working with bits and binary operators may be a bit faster (depending on the platform, the quantity of operations, and other factors).

For understanding the previous example, it is very important to understand the binary system and its operators.

I’ve also detailed the insides in a practical example of using bit masking and binary in your software with pros and cons.

If you want to find more such use cases, please let me know and I’ll be happy to bring more to the surface.

Tha(nk|t’)s all!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store