Face Value Handler v1.0.0 Beta Released

GGJ got me thinking that it would be great if developers did not have to worry about the communication aspects of their game and, instead, could focus on the actual game and this led me to try to create an API that would simplify this considerably. Enter the FaceValueHandler class.

Let’s go directly to a simple usage example:

#include "face_value_handler.h"

// We have a single piece of data we want to propagate, so it starts at offset 0.
#define OFFSET_GAME_MODE 0

// As we have a single piece of information, it is at index 0.
#define INDEX_GAME_MODE 0

void loop() {
  // This creates a handler for a single piece of face value data (field), detects if it changed 
  // since the last loop iteration and, if did, automatically propagates it to all faces. 
  FaceValueHandler face_value_handler(nullptr, OFFSET_GAME_MODE);

  // Now we get the (possibly updated above) game mode from any of the output faces. 
  // We use 0 here but could as well have used any other number.
  byte game_mode = face_value_handler.GetOutputFieldValue(0, INDEX_GAME_MODE);

  // Update the game mode in case the button was clicked.
  if (buttonSingleClicked()) {
    game_mode = (game_mode + 1) % 4;

    // Change the output value for the game mode field in all output faces.
    face_value_handler.SetOutputFieldValueOnAllFaces(INDEX_GAME_MODE,
                                                     game_mode);
  }

  // Simple Blink rendering that shows game modes as colors.
  switch (game_mode) {
    case 0:
      setColor(RED);
      break;
    case 1:
      setColor(YELLOW);
      break;
    case 2:
      setColor(GREEN);
      break;
    case 3:
      setColor(CYAN);
      break;
  }

  // Before loop() returns, input face values will be cached and output face values will be 
  // set.
}

And that is it. This code is doing the automatic propagation of only the pieces of data (a single one in this example) that change in the input to the output.

No need to fiddle with bits. No need to explicitly set the output face values or to get input face values.

The code supports a callback for custom handling of fields, but I will write an example using it another time.

To use it, you just need to copy the .h and .cpp files from the repository below to the directory your .ino file is and "#include “face_value_handler.h” at the top of your .ino file. Then you can just instantiate it and call any methods you want.

Right now it is using a bit more storage than I think is reasonable, but I did not do any optimization pass yet so things should improve on later versions.

Let me know what you think!

7 Likes

A bit more advanced example. Let’s suppose you have 2 values: One is the game state as in the previous example (which should be propagated to all faces) but the other one is a single bit value that must be propagated only to the face opposite to the one it was received in. Now instead of a single index and offset, we have 2 indexes and offsets:

#define OFFSET_GAME_MODE 0
#define INDEX_GAME_MODE 0  // First field.

// The offset below also implies that the size of the GAME_MODE data can be up to 2 bits,
// which is what we need.
#define OFFSET_ENABLE_SOMETHING 2
#define INDEX_ENABLE_SOMETHING 1  // Second field.

Then, we need a ChangeCallback function to handle things:

// The function name is irrelevant, but it must return a bool and have the parameters below.
bool callback(byte face, byte field_index, FaceValueHandler* face_value_handler) {
  switch (field_index) {
    case INDEX_GAME_MODE:
      // We just break here as we will then return false at the end of the function. 
      break;
    case INDEX_ENABLE_SOMETHING:
      // We want to propagate this to the opposite face only.
      byte value_to_propagate = face_value_handler->GetInputFieldValue(face, field_index);
      face_value_handler->SetOutputFieldValue(opposite_face[face], field_index, 
          value_to_propagate);
      return true;  // Change was handled. Will not be propagated to all faces. 
  }

  // If we get to here, the change will be propagated to all faces.
  return false;
}

And, finally, we create our FaceValueHandler like this:

FaceValueHandler face_value_handler(callback, OFFSET_GAME_MODE, 
    OFFSET_ENABLE_SOMETHING);

And that is it. :slight_smile:

2 Likes

I fixed a few bugs and added a complete example in the repository. I think it is now working reliably although still a bit heavy. I will get back to optimize it as soon as I finish other stuff I am working on.