Arduboyでボタンのリピートを取得する

Arduboyのボタン入力の取得は、pressed(), notPressed(), justPressed(), justReleased()があるが、ボタンのリピートも欲しくなる。

ドキュメントには次のようにする。と書かれているが少々使いづらそうだ。

if (arduboy.everyXframes(5)) {
  if (arduboy.pressed(A_BUTTON)) {
    fireShot();
  }
}


そこでボタンのリピートを簡単に取れるようにした。
f:id:raohu69:20180321183800p:plain

#include <Arduboy2.h>

Arduboy2 arduboy;

void setup()
{
  arduboy.begin();
  arduboy.setFrameRate(60);
  arduboy.clear();
}

void loop()
{
  if (!arduboy.nextFrame()) return;
  ButtonUpdate();
  arduboy.clear();

  static int16_t x, y;

  if (ButtonRepeat(UP_BUTTON)) {
    if (--y < 0) y = 5 - 1;
  } else if (ButtonRepeat(DOWN_BUTTON)) {
    ++y %= 5;
  }
  if (ButtonRepeat(LEFT_BUTTON)) {
    if (--x < 0) x = 2 - 1;
  } else if (ButtonRepeat(RIGHT_BUTTON)) {
    ++x %= 2;
  }

  for (int16_t i = 0; i < 2; i++) {
    for (int16_t j = 0; j < 5; j++) {
      arduboy.setCursor(i * 56, j * 8);
      arduboy.print(i == x && j == y ? ">" : " "); arduboy.print("ITEM "); arduboy.print(i * 5 + j);
      arduboy.print("\n");
    }
  }

  arduboy.display();
}

// ボタン情報
static uint8_t btnOn;
static uint8_t btnPress;
static uint8_t btnRelease;
static uint8_t btnRepeat;
static uint8_t btnPrev;
static uint16_t repeatStart = 30;
static uint16_t repeatInterval = 10;
static uint16_t repeatCounter[2][6];
static uint8_t buttons[] = { UP_BUTTON, DOWN_BUTTON, LEFT_BUTTON, RIGHT_BUTTON, A_BUTTON, B_BUTTON };

// ボタン情報の更新(毎フレーム呼び出す)
void ButtonUpdate()
{
  btnOn = arduboy.buttonsState(); // 現在のボタンの状態
  btnPress = ~btnPrev & btnOn;    // 今ボタンが押された
  btnRelease = btnPrev & ~btnOn;  // 今ボタンが離された
  uint8_t hold = btnPrev & btnOn; // ボタンが押され続けている
  btnPrev = btnOn;                // 前回のボタンの状態

  btnRepeat = 0;
  for (int16_t i = 0; i < 6; i++) {
    if (hold & buttons[i]) {
      // ボタンが押され続けている
      if (repeatCounter[0][i] > 0) {
        // 1回目のリピートまで待つ
        repeatCounter[0][i]--;
      } else if (repeatCounter[1][i] == 0) {
        // 2回目以降のリピートを開始する
        btnRepeat |= buttons[i];              // リピートがかかった
        repeatCounter[1][i] = repeatInterval; // 次のリピートまでの待ち時間
      } else {
        // 次のリピートまで待つ
        repeatCounter[1][i]--;
      }
    } else {
      // ボタンが押された。または押されていない
      btnRepeat |= (btnOn & buttons[i]);  // 押されていたらbitを立てる
      repeatCounter[0][i] = repeatStart;  // 1回目のリピート開始までの待ち時間
      repeatCounter[1][i] = 0;            // リセット
    }
  }
}

// ボタンが押されているか
bool ButtonOn(uint8_t button)
{
  return btnOn & button;
}

// ボタンが押されたか
bool ButtonPress(uint8_t button)
{
  return btnPress & button;
}

// ボタンが離されたか
bool ButtonRelease(uint8_t button)
{
  return btnRelease & button;
}

// ボタンがリピートしたか
bool ButtonRepeat(uint8_t button)
{
  return btnRepeat & button;
}

// リピート速度を設定する
void ButtonSetRepeat(uint16_t start, uint16_t interval)
{
  repeatStart = start;
  repeatInterval = interval;

  btnOn = btnPress = btnRelease = btnRepeat = btnPrev = 0;

  for (int16_t i = 0; i < 2; i++) {
    for (int16_t j = 0; j < 6; j++) {
      repeatCounter[i][j] = 0;
    }
  }
}