Newer
Older
OurSketch / OurSketch.ino
#include <M5Unified.h>
#include <esp_now.h>
#include <WiFi.h>
#include <cmath>

// --- 色定義 ---
#define BLACK 0x0000
#define WHITE 0xFFFF
#define GREEN 0x07E0
#define RED 0xF800

#define SCREEN_W 240
#define SCREEN_H 135

int barW = 40;
int barH = 5;
int barX = (SCREEN_W - barW) / 2;
int barY = SCREEN_H - 10;

float ballX = SCREEN_W / 2;
float ballY = SCREEN_H / 2;
float ballVX = 2.5;
float ballVY = -2.0;
int ballR = 3;

const int BLOCK_ROWS = 4;
const int BLOCK_COLS = 8;
int blockW = SCREEN_W / BLOCK_COLS;
int blockH = 12;
bool blocks[BLOCK_ROWS][BLOCK_COLS];

bool gameWon = false;
bool gameOver = false;
int life = 3;

int sent_stack = 0;
int recv_stack = 0;

void sent_block(){

}

void recv_block(){
  
}




void resetBlocks() {
  for (int i = 0; i < BLOCK_ROWS; ++i)
    for (int j = 0; j < BLOCK_COLS; ++j)
      blocks[i][j] = true;
  gameWon = false;
  gameOver = false;
  life = 3;
}

bool allBlocksCleared() {
  for (int i = 0; i < BLOCK_ROWS; ++i)
    for (int j = 0; j < BLOCK_COLS; ++j)
      if (blocks[i][j]) return false;
  return true;
}

void showMessage(const char* msg) {
  M5.Display.fillScreen(BLACK);
  M5.Display.setTextColor(WHITE);
  M5.Display.setCursor(60, SCREEN_H / 2 - 8);
  M5.Display.println(msg);
  delay(1500);
}

void resetBall() {
  ballX = SCREEN_W / 2;
  ballY = SCREEN_H / 2;
  ballVX = 2.5;
  ballVY = -2.0;
}

void setup() {
  auto cfg = M5.config();
  M5.begin(cfg);
  M5.Display.setRotation(3);
  M5.Display.setTextFont(1);
  M5.Display.fillScreen(BLACK);
  xTaskCreatePinnedToCore(task0, "Task0", 4096, NULL, 1, NULL, 1);
  resetBlocks();
}

void loop() {
  M5.update();

  if (gameWon) {
    showMessage("You Win!");
    resetBlocks();
    resetBall();
    return;
  }

  if (gameOver) {
    showMessage("You Lose...");
    resetBlocks();
    resetBall();
    return;
  }

  // 操作(傾き)
  if (M5.Imu.update()) {
    auto data = M5.Imu.getImuData();
    float tilt = -data.accel.y;
    float speed = tilt * 10.0;
    barX += (int)speed;
    if (barX < 0) barX = 0;
    if (barX > SCREEN_W - barW) barX = SCREEN_W - barW;
  }

  // ボール移動
  ballX += ballVX;
  ballY += ballVY;

  // 壁反射
  if (ballX <= ballR) {
    ballX = ballR + 1;
    ballVX *= -1;
  }
  if (ballX >= SCREEN_W - ballR) {
    ballX = SCREEN_W - ballR - 1;
    ballVX *= -1;
  }
  if (ballY <= ballR) {
    ballY = ballR + 1;
    ballVY *= -1;
  }

  // バー反射
  if (ballY + ballR >= barY && ballY + ballR <= barY + barH &&
      ballX >= barX && ballX <= barX + barW) {
    float offset = (ballX - (barX + barW / 2)) / (barW / 2);
    float angle_deg = 15 + 60 * fabs(offset);
    float angle_rad = angle_deg * M_PI / 180.0;
    float speed = sqrt(ballVX * ballVX + ballVY * ballVY);
    ballVX = speed * sin(offset >= 0 ? angle_rad : -angle_rad);
    ballVY = -speed * cos(angle_rad);
    ballY = barY - ballR - 1;

    // 壁に接近してる場合は少し内側に押し戻す
    if (ballX <= ballR) ballX = ballR + 1;
    if (ballX >= SCREEN_W - ballR) ballX = SCREEN_W - ballR - 1;
  }

  // ブロックとの衝突
  for (int i = 0; i < BLOCK_ROWS; ++i) {
    for (int j = 0; j < BLOCK_COLS; ++j) {
      if (blocks[i][j]) {
        int bx = j * blockW;
        int by = i * blockH;
        if (ballX + ballR > bx && ballX - ballR < bx + blockW &&
            ballY + ballR > by && ballY - ballR < by + blockH) {
          blocks[i][j] = false;

          float cx = bx + blockW / 2;
          float cy = by + blockH / 2;
          float dx = ballX - cx;
          float dy = ballY - cy;
          if (fabs(dx / blockW) > fabs(dy / blockH)) {
            ballVX *= -1;
          } else {
            ballVY *= -1;
          }

          goto block_hit_done;
        }
      }
    }
  }
block_hit_done:

  // ボールが下に落ちたらライフ減
  if (ballY > SCREEN_H) {
    life--;
    if (life <= 0) {
      gameOver = true;
      return;
    } else {
      resetBall();
    }
  }

  if (allBlocksCleared()) {
    gameWon = true;
    return;
  }

  // === 描画 ===
  M5.Display.fillScreen(BLACK);

  // ブロック
  for (int i = 0; i < BLOCK_ROWS; ++i) {
    for (int j = 0; j < BLOCK_COLS; ++j) {
      if (blocks[i][j]) {
        int bx = j * blockW;
        int by = i * blockH;
        M5.Display.fillRect(bx + 1, by + 1, blockW - 2, blockH - 2, GREEN);
      }
    }
  }

  // バー
  M5.Display.fillRect(barX, barY, barW, barH, WHITE);

  // ボール
  M5.Display.fillCircle((int)ballX, (int)ballY, ballR, RED);

  // ライフ表示
  M5.Display.setTextColor(WHITE);
  M5.Display.setCursor(5, 5);
  M5.Display.printf("Life: %d", life);

  delay(16);
}


void Init_ESPNOW()
{ // ESPNowの初期化
  // 引用: https://101010.fun/iot/esp32-m5stickc-plus-esp-now.html
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  if (esp_now_init() == ESP_OK)
  {
    M5.Display.println("ESP-Now Init Success");
  }
  else
  {
    M5.Display.println("ESP-Now Init failed");
  }
  // マルチキャスト用Slave登録
  memset(&peerInfo, 0, sizeof(peerInfo));
  for (int i = 0; i < 6; ++i)
  {
    peerInfo.peer_addr[i] = (uint8_t)0xff;
  }
  esp_err_t addStatus = esp_now_add_peer(&peerInfo);
  if (addStatus == ESP_OK)
  {
    // Pair success
    M5.Display.println("Pair success");
    esp_now_register_send_cb(onESPNOWSent);    //送信後のコールバック関数を指定する
    esp_now_register_recv_cb(onESPNOWReceive); ///受信時のコールバック関数を指定する
  }
}