#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); ///受信時のコールバック関数を指定する } }