Arduboyで弾幕的なものを その2

sin,cos関数は重かろうということで、テーブルから求めるようにしてみた。
Arduboyの解像度だと、一周64方向もあれば十分かな?

まずは、Pythonでsinテーブル(64方向分)を作成。

import math

for i in range(64):
    v = math.sin(i / 64 * 2 * math.pi)
    print(str(v) + ',')

sinからcosを求める変形公式があります。(数学Iで習うようです)
 sin(90 + θ) = cosθ
つまり、sinテーブルがあれば、cosは求められるということですね。

今回は一周を64方向に決めたので、90°は16ということになり、
sinテーブルを16ずらすと、cosが求まります。

float getCos(int16_t angle)
{
  return getSin(16 + angle);
}

f:id:raohu69:20180505202405p:plain

#include <Arduboy2.h>

Arduboy2 arduboy;

#define BOSS_SHOT   10

typedef struct CharaData {
  float x, y;
  float vx, vy;
  int life;
};

CharaData boss;
CharaData bossShot[BOSS_SHOT];

uint16_t counter;
int16_t angle;

// 64方向
const float sinTbl[] PROGMEM = {
  0.0,
  0.0980171403295606,
  0.19509032201612825,
  0.29028467725446233,
  0.3826834323650898,
  0.47139673682599764,
  0.5555702330196022,
  0.6343932841636455,
  0.7071067811865475,
  0.7730104533627369,
  0.8314696123025451,
  0.8819212643483549,
  0.9238795325112867,
  0.9569403357322089,
  0.9807852804032304,
  0.9951847266721968,
  1.0,
  0.9951847266721969,
  0.9807852804032304,
  0.9569403357322089,
  0.9238795325112867,
  0.881921264348355,
  0.8314696123025455,
  0.7730104533627371,
  0.7071067811865476,
  0.6343932841636455,
  0.5555702330196022,
  0.4713967368259978,
  0.38268343236508984,
  0.29028467725446233,
  0.19509032201612858,
  0.09801714032956084,
  1.2246467991473532e-16,
  -0.09801714032956059,
  -0.19509032201612836,
  -0.29028467725446216,
  -0.38268343236508967,
  -0.47139673682599764,
  -0.555570233019602,
  -0.6343932841636453,
  -0.7071067811865475,
  -0.7730104533627367,
  -0.8314696123025452,
  -0.8819212643483549,
  -0.9238795325112865,
  -0.9569403357322088,
  -0.9807852804032303,
  -0.9951847266721969,
  -1.0,
  -0.9951847266721969,
  -0.9807852804032304,
  -0.9569403357322089,
  -0.9238795325112866,
  -0.881921264348355,
  -0.8314696123025456,
  -0.7730104533627369,
  -0.7071067811865477,
  -0.6343932841636459,
  -0.5555702330196022,
  -0.4713967368259979,
  -0.3826834323650904,
  -0.29028467725446244,
  -0.19509032201612872,
  -0.09801714032956052,
};

float getSin(int16_t angle)
{
  int16_t n = angle % 64;
  return pgm_read_float(sinTbl + n);
}

float getCos(int16_t angle)
{
  return getSin(16 + angle);
}

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

  // ボス(三角形)を画面中央に表示する
  boss.x = 128 / 2;
  boss.y = 64 / 2;
}

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

  // ボス(三角形)
  arduboy.drawTriangle(boss.x, boss.y - 8.f, boss.x - 8.f, boss.y + 8.f, boss.x + 8.f, boss.y + 8.f);

  // 弾の作成
  if (++counter % 10 == 0) {
    for (int8_t i = 0; i < BOSS_SHOT; i++) {
      CharaData *p = &bossShot[i];
      if (p->life == 0) {
        // 弾を使用中にする
        p->life = 1;

        // 弾の初期位置をボスの中央にする
        p->x = boss.x;
        p->y = boss.y + 2.f;

        ++angle;

        // 弾の移動方向を求める
        p->vx = getCos(angle);
        p->vy = getSin(angle);

        break;
      }
    }    
  }

  // 弾の表示
  for (int16_t i = 0; i < BOSS_SHOT; i++) {
    CharaData *p = &bossShot[i];
    if (p->life > 0) {
      // 弾の移動
      p->x += p->vx;
      p->y += p->vy;

      // 画面外の弾を消す
      if (p->x < 0.f || 128.f < p->x || p->y < 0.f || 64.f < p->y)
        p->life = 0;

      arduboy.fillCircle(p->x, p->y, 2);
    }
  }

  arduboy.display();
}