diff --git a/Another.ino b/Another.ino new file mode 100644 index 0000000..8b256ab --- /dev/null +++ b/Another.ino @@ -0,0 +1,338 @@ +#include +#include +#include +#include + +// --- 色定義 --- +#define BLACK 0x0000 +#define WHITE 0xFFFF +#define GREEN 0x07E0 +#define RED 0xF800 +#define GRAY 0x8410 // おじゃまブロック用の色 + +#define SCREEN_W 240 +#define SCREEN_H 135 + +#define STACK_MAX 10 // おじゃまブロックの送信スタックの最大数 + +// --- バー設定 --- +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; +// ブロックの状態: 0 = なし, 1 = 通常, 2 = おじゃま +int blocks[BLOCK_ROWS][BLOCK_COLS]; + +// --- ゲーム状態 --- +bool gameWon = false; +bool gameOver = false; +int life = 3; + +// --- 通信とおじゃまブロック用のスタック --- +int sent_stack = 0; // 送信用のスタック +volatile int recv_stack = 0; // 受信したおじゃまブロックのスタック (ISRから変更されるためvolatile) + +// --- ESP-NOW関連 --- +// ブロードキャストアドレス(すべてのESP-NOWデバイスに送信) +uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +esp_now_peer_info_t peerInfo; + +// 送受信するデータの構造体 +typedef struct struct_message { + char type; // 'A' = Attack (おじゃまブロック送信) +} struct_message; + +struct_message myData; + + +// --- 関数プロトタイプ --- +void Init_ESPNOW(); +void onESPNOWSent(const uint8_t *mac_addr, esp_now_send_status_t status); +// ★★★ エラー修正箇所 ★★★ +void onESPNOWReceive(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len); +void sendAttack(); +void addOjamaBlock(); +void resetBlocks(); +void resetBall(); + +// ESP-NOW データ送信後のコールバック +void ICACHE_RAM_ATTR onESPNOWSent(const uint8_t *mac_addr, esp_now_send_status_t status) { + // 送信結果はここでは特に処理しない +} + +// ESP-NOW データ受信時のコールバック +// ★★★ ESP32コアのバージョンアップに対応した引数に変更 ★★★ +void ICACHE_RAM_ATTR onESPNOWReceive(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len) { + struct_message msg; + memcpy(&msg, incomingData, sizeof(msg)); + if (msg.type == 'A') { + recv_stack++; // おじゃまスタックを増やす + } +} + +// ESP-NOW初期化 +void Init_ESPNOW() { + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + if (esp_now_init() == ESP_OK) { + M5.Display.println("ESP-Now Init Success"); + esp_now_register_send_cb(onESPNOWSent); + // ★★★ ここで呼び出す関数のシグネチャがコンパイルエラーの原因だった ★★★ + esp_now_register_recv_cb(onESPNOWReceive); + } else { + M5.Display.println("ESP-Now Init Failed"); + return; + } + + memcpy(peerInfo.peer_addr, broadcastAddress, 6); + peerInfo.channel = 0; + peerInfo.encrypt = false; + + if (esp_now_add_peer(&peerInfo) != ESP_OK){ + M5.Display.println("Failed to add peer"); + return; + } + M5.Display.println("Peer added"); +} + +// 攻撃(おじゃまブロック)を送信 +void sendAttack() { + myData.type = 'A'; + esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData)); +} + +// おじゃまブロックを追加 +void addOjamaBlock() { + // 下の行から空いている場所を探す + for (int i = BLOCK_ROWS - 1; i >= 0; --i) { + for (int j = 0; j < BLOCK_COLS; ++j) { + if (blocks[i][j] == 0) { // 空のマスを見つけたら + blocks[i][j] = 2; // おじゃまブロック(2)を設置 + return; // 1つ追加したら終了 + } + } + } +} + + +void resetBlocks() { + for (int i = 0; i < BLOCK_ROWS; ++i) + for (int j = 0; j < BLOCK_COLS; ++j) + blocks[i][j] = 1; // 通常ブロック(1)で初期化 + gameWon = false; + gameOver = false; + life = 3; + sent_stack = 0; + recv_stack = 0; +} + +bool allBlocksCleared() { + for (int i = 0; i < BLOCK_ROWS; ++i) + for (int j = 0; j < BLOCK_COLS; ++j) + if (blocks[i][j] != 0) return false; // ブロックが1つでも残っていたら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); + + + delay(500); // 通信初期化のための待機 + M5.Display.clear(); + + resetBlocks(); +} + +void loop() { + M5.update(); + + if (gameWon) { + showMessage("You Win!"); + resetBlocks(); + resetBall(); + return; + } + + if (gameOver) { + showMessage("You Lose..."); + resetBlocks(); + resetBall(); + return; + } + + // 受信したおじゃまブロックを処理 + if (recv_stack > 0) { + addOjamaBlock(); + recv_stack--; + } + + // 操作(傾き) + 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; + M5.Speaker.tone(2000, 100); + } + if (ballX >= SCREEN_W - ballR) { + ballX = SCREEN_W - ballR - 1; + ballVX *= -1; + M5.Speaker.tone(2000, 100); + } + if (ballY <= ballR) { + ballY = ballR + 1; + ballVY *= -1; + M5.Speaker.tone(2000, 100); + } + + // バー反射 + 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; + M5.Speaker.tone(2000, 100); // バーに当たった音 + + 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] > 0) { // ブロックが存在すれば + int bx = j * blockW; + int by = i * blockH; + if (ballX + ballR > bx && ballX - ballR < bx + blockW && + ballY + ballR > by && ballY - ballR < by + blockH) { + + if(blocks[i][j] == 1) { // 通常ブロックの場合 + blocks[i][j] = 0; // ブロックを消す + sent_stack++; // 送信用スタックを+1 + if (sent_stack >= STACK_MAX) { + sendAttack(); // STACK_MAXつ溜まったら攻撃 + sent_stack = 0; // スタックリセット + } + } else if (blocks[i][j] == 2) { // おじゃまブロックの場合 + blocks[i][j] = 1; // 通常ブロックに変化させる + } + + // 反射方向の計算 + 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; + M5.Speaker.tone(2000, 100); + } else { + ballVY *= -1; + M5.Speaker.tone(2000, 100); + } + + 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.startWrite(); + M5.Display.fillScreen(BLACK); + + // ブロック + for (int i = 0; i < BLOCK_ROWS; ++i) { + for (int j = 0; j < BLOCK_COLS; ++j) { + int bx = j * blockW; + int by = i * blockH; + if (blocks[i][j] == 1) { // 通常ブロック + M5.Display.fillRect(bx + 1, by + 1, blockW - 2, blockH - 2, GREEN); + } else if (blocks[i][j] == 2) { // おじゃまブロック + M5.Display.fillRect(bx + 1, by + 1, blockW - 2, blockH - 2, GRAY); + } + } + } + + // バー + 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); + M5.Display.endWrite(); + + delay(16); +} \ No newline at end of file diff --git a/Another.ino.txt b/Another.ino.txt deleted file mode 100644 index e26a4a6..0000000 --- a/Another.ino.txt +++ /dev/null @@ -1,332 +0,0 @@ -#include -#include -#include -#include - -// --- 色定義 --- -#define BLACK 0x0000 -#define WHITE 0xFFFF -#define GREEN 0x07E0 -#define RED 0xF800 -#define GRAY 0x8410 // おじゃまブロック用の色 - -#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; -// ブロックの状態: 0 = なし, 1 = 通常, 2 = おじゃま -int blocks[BLOCK_ROWS][BLOCK_COLS]; - -// --- ゲーム状態 --- -bool gameWon = false; -bool gameOver = false; -int life = 3; - -// --- 通信とおじゃまブロック用のスタック --- -int sent_stack = 0; // 送信用のスタック -volatile int recv_stack = 0; // 受信したおじゃまブロックのスタック (ISRから変更されるためvolatile) - -// --- ESP-NOW関連 --- -// ブロードキャストアドレス(すべてのESP-NOWデバイスに送信) -uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; -esp_now_peer_info_t peerInfo; - -// 送受信するデータの構造体 -typedef struct struct_message { - char type; // 'A' = Attack (おじゃまブロック送信) -} struct_message; - -struct_message myData; - - -// --- 関数プロトタイプ --- -void Init_ESPNOW(); -void onESPNOWSent(const uint8_t *mac_addr, esp_now_send_status_t status); -// ★★★ エラー修正箇所 ★★★ -void onESPNOWReceive(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len); -void sendAttack(); -void addOjamaBlock(); -void resetBlocks(); -void resetBall(); - -// ESP-NOW データ送信後のコールバック -void ICACHE_RAM_ATTR onESPNOWSent(const uint8_t *mac_addr, esp_now_send_status_t status) { - // 送信結果はここでは特に処理しない -} - -// ESP-NOW データ受信時のコールバック -// ★★★ ESP32コアのバージョンアップに対応した引数に変更 ★★★ -void ICACHE_RAM_ATTR onESPNOWReceive(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len) { - struct_message msg; - memcpy(&msg, incomingData, sizeof(msg)); - if (msg.type == 'A') { - recv_stack++; // おじゃまスタックを増やす - } -} - -// ESP-NOW初期化 -void Init_ESPNOW() { - WiFi.mode(WIFI_STA); - WiFi.disconnect(); - if (esp_now_init() == ESP_OK) { - M5.Display.println("ESP-Now Init Success"); - esp_now_register_send_cb(onESPNOWSent); - // ★★★ ここで呼び出す関数のシグネチャがコンパイルエラーの原因だった ★★★ - esp_now_register_recv_cb(onESPNOWReceive); - } else { - M5.Display.println("ESP-Now Init Failed"); - return; - } - - memcpy(peerInfo.peer_addr, broadcastAddress, 6); - peerInfo.channel = 0; - peerInfo.encrypt = false; - - if (esp_now_add_peer(&peerInfo) != ESP_OK){ - M5.Display.println("Failed to add peer"); - return; - } - M5.Display.println("Peer added"); -} - -// 攻撃(おじゃまブロック)を送信 -void sendAttack() { - myData.type = 'A'; - esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData)); -} - -// おじゃまブロックを追加 -void addOjamaBlock() { - // 下の行から空いている場所を探す - for (int i = BLOCK_ROWS - 1; i >= 0; --i) { - for (int j = 0; j < BLOCK_COLS; ++j) { - if (blocks[i][j] == 0) { // 空のマスを見つけたら - blocks[i][j] = 2; // おじゃまブロック(2)を設置 - return; // 1つ追加したら終了 - } - } - } -} - - -void resetBlocks() { - for (int i = 0; i < BLOCK_ROWS; ++i) - for (int j = 0; j < BLOCK_COLS; ++j) - blocks[i][j] = 1; // 通常ブロック(1)で初期化 - gameWon = false; - gameOver = false; - life = 3; - sent_stack = 0; - recv_stack = 0; -} - -bool allBlocksCleared() { - for (int i = 0; i < BLOCK_ROWS; ++i) - for (int j = 0; j < BLOCK_COLS; ++j) - if (blocks[i][j] != 0) return false; // ブロックが1つでも残っていたら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 task0(void *pvParameters) { - Init_ESPNOW(); - vTaskDelete(NULL); // 初期化が終わったらタスクを削除 -} - -void setup() { - auto cfg = M5.config(); - M5.begin(cfg); - M5.Display.setRotation(3); - M5.Display.setTextFont(1); - M5.Display.fillScreen(BLACK); - - // Core1でESP-NOWの初期化タスクを実行 - xTaskCreatePinnedToCore(task0, "Task0", 4096, NULL, 1, NULL, 1); - - delay(500); // 通信初期化のための待機 - M5.Display.clear(); - - resetBlocks(); -} - -void loop() { - M5.update(); - - if (gameWon) { - showMessage("You Win!"); - resetBlocks(); - resetBall(); - return; - } - - if (gameOver) { - showMessage("You Lose..."); - resetBlocks(); - resetBall(); - return; - } - - // 受信したおじゃまブロックを処理 - if (recv_stack > 0) { - addOjamaBlock(); - recv_stack--; - } - - // 操作(傾き) - 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] > 0) { // ブロックが存在すれば - int bx = j * blockW; - int by = i * blockH; - if (ballX + ballR > bx && ballX - ballR < bx + blockW && - ballY + ballR > by && ballY - ballR < by + blockH) { - - if(blocks[i][j] == 1) { // 通常ブロックの場合 - blocks[i][j] = 0; // ブロックを消す - sent_stack++; // 送信用スタックを+1 - if (sent_stack >= 3) { - sendAttack(); // 3つ溜まったら攻撃 - sent_stack = 0; // スタックリセット - } - } else if (blocks[i][j] == 2) { // おじゃまブロックの場合 - blocks[i][j] = 1; // 通常ブロックに変化させる - } - - // 反射方向の計算 - 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) { - int bx = j * blockW; - int by = i * blockH; - if (blocks[i][j] == 1) { // 通常ブロック - M5.Display.fillRect(bx + 1, by + 1, blockW - 2, blockH - 2, GREEN); - } else if (blocks[i][j] == 2) { // おじゃまブロック - M5.Display.fillRect(bx + 1, by + 1, blockW - 2, blockH - 2, GRAY); - } - } - } - - // バー - 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); -} \ No newline at end of file diff --git a/OurSketch.ino b/OurSketch.ino index e77698f..cc5b305 100644 --- a/OurSketch.ino +++ b/OurSketch.ino @@ -1,65 +1,146 @@ +#include #include #include #include #include // --- 色定義 --- -#define BLACK 0x0000 -#define WHITE 0xFFFF -#define GREEN 0x07E0 -#define RED 0xF800 +#define BLACK 0x0000 +#define WHITE 0xFFFF +#define GREEN 0x07E0 +#define RED 0xF800 +#define GRAY 0x8410 // おじゃまブロック用の色 #define SCREEN_W 240 #define SCREEN_H 135 +#define STACK_MAX 10 // おじゃまブロックの送信スタックの最大数 + +M5Canvas canvas(&M5.Display); + +// --- バー設定 --- 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]; +// ブロックの状態: 0 = なし, 1 = 通常, 2 = おじゃま +int blocks[BLOCK_ROWS][BLOCK_COLS]; +// --- ゲーム状態 --- bool gameWon = false; bool gameOver = false; int life = 3; -int sent_stack = 0; -int recv_stack = 0; +// --- 通信とおじゃまブロック用のスタック --- +int sent_stack = 0; // 送信用のスタック +int recv_stack = 0; // 受信したおじゃまブロックのスタック -void sent_block(){ +// --- ESP-NOW関連 --- +// ブロードキャストアドレス(すべてのESP-NOWデバイスに送信) +uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +esp_now_peer_info_t peerInfo; +// 送受信するデータの構造体 +typedef struct struct_message { + char type; // 'A' = Attack (おじゃまブロック送信) +} struct_message; + +struct_message myData; + +// --- 関数プロトタイプ --- +void Init_ESPNOW(); +void onESPNOWSent(const uint8_t *mac_addr, esp_now_send_status_t status); +void onESPNOWReceive(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len); +void sendAttack(); +void addOjamaBlock(); +void resetBlocks(); +void resetBall(); + +// ESP-NOW データ送信後のコールバック +void ICACHE_RAM_ATTR onESPNOWSent(const uint8_t *mac_addr, esp_now_send_status_t status) { + // 送信結果はここでは特に処理しない } -void recv_block(){ +// ESP-NOW データ受信時のコールバック +void ICACHE_RAM_ATTR onESPNOWReceive(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len) { + struct_message msg; + memcpy(&msg, incomingData, sizeof(msg)); + if (msg.type == 'A') { + recv_stack++; // おじゃまスタックを増やす + } +} + +// ESP-NOW初期化 +void Init_ESPNOW() { + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + if (esp_now_init() == ESP_OK) { + M5.Display.println("ESP-Now Init Success"); + esp_now_register_send_cb(onESPNOWSent); + esp_now_register_recv_cb(onESPNOWReceive); + } else { + M5.Display.println("ESP-Now Init Failed"); + return; + } + memcpy(peerInfo.peer_addr, broadcastAddress, 6); + peerInfo.channel = 0; + peerInfo.encrypt = false; + + if (esp_now_add_peer(&peerInfo) != ESP_OK){ + M5.Display.println("Failed to add peer"); + return; + } + M5.Display.println("Peer added"); } +// 攻撃(おじゃまブロック)を送信 +void sendAttack() { + myData.type = 'A'; + esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData)); +} - +// おじゃまブロックを追加 +void addOjamaBlock() { + // 下の行から空いている場所を探す + for (int i = BLOCK_ROWS - 1; i >= 0; --i) { + for (int j = 0; j < BLOCK_COLS; ++j) { + if (blocks[i][j] == 0) { // 空のマスを見つけたら + blocks[i][j] = 2; // おじゃまブロック(2)を設置 + return; // 1つ追加したら終了 + } + } + } +} void resetBlocks() { for (int i = 0; i < BLOCK_ROWS; ++i) for (int j = 0; j < BLOCK_COLS; ++j) - blocks[i][j] = true; + blocks[i][j] = 1; // 通常ブロック(1)で初期化 gameWon = false; gameOver = false; life = 3; + sent_stack = 0; + recv_stack = 0; } bool allBlocksCleared() { for (int i = 0; i < BLOCK_ROWS; ++i) for (int j = 0; j < BLOCK_COLS; ++j) - if (blocks[i][j]) return false; + if (blocks[i][j] != 0) return false; // ブロックが1つでも残っていたらfalse return true; } @@ -84,7 +165,12 @@ M5.Display.setRotation(3); M5.Display.setTextFont(1); M5.Display.fillScreen(BLACK); - xTaskCreatePinnedToCore(task0, "Task0", 4096, NULL, 1, NULL, 1); + canvas.createSprite(SCREEN_W, SCREEN_H); + Init_ESPNOW(); + + delay(500); // 通信初期化のための待機 + M5.Display.clear(); + resetBlocks(); } @@ -105,6 +191,12 @@ return; } + // 受信したおじゃまブロックを処理 + if (recv_stack > 0) { + addOjamaBlock(); + recv_stack--; + } + // 操作(傾き) if (M5.Imu.update()) { auto data = M5.Imu.getImuData(); @@ -123,14 +215,17 @@ if (ballX <= ballR) { ballX = ballR + 1; ballVX *= -1; + M5.Speaker.tone(2000, 100); } if (ballX >= SCREEN_W - ballR) { ballX = SCREEN_W - ballR - 1; ballVX *= -1; + M5.Speaker.tone(2000, 100); } if (ballY <= ballR) { ballY = ballR + 1; ballVY *= -1; + M5.Speaker.tone(2000, 100); } // バー反射 @@ -143,30 +238,46 @@ ballVX = speed * sin(offset >= 0 ? angle_rad : -angle_rad); ballVY = -speed * cos(angle_rad); ballY = barY - ballR - 1; + M5.Speaker.tone(2000, 100); // バーに当たった音 - // 壁に接近してる場合は少し内側に押し戻す - if (ballX <= ballR) ballX = 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]) { + if (blocks[i][j] > 0) { // ブロックが存在すれば 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; + + if(blocks[i][j] == 1) { // 通常ブロックの場合 + blocks[i][j] = 0; // ブロックを消す + sent_stack++; // 送信用スタックを+1 + if (sent_stack >= STACK_MAX) { + sendAttack(); // STACK_MAXつ溜まったら攻撃 + sent_stack = 0; // スタックリセット + } + } else if (blocks[i][j] == 2) { // おじゃまブロックの場合 + blocks[i][j] = 1; // 通常ブロックに変化させる + } + + // 反射方向の計算 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; + M5.Speaker.tone(2000, 100); } else { ballVY *= -1; + M5.Speaker.tone(2000, 100); } goto block_hit_done; @@ -191,61 +302,37 @@ gameWon = true; return; } + // === 描画 === - M5.Display.fillScreen(BLACK); + canvas.fillSprite(TFT_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); + int bx = j * blockW; + int by = i * blockH; + if (blocks[i][j] == 1) { // 通常ブロック + canvas.fillRect(bx + 1, by + 1, blockW - 2, blockH - 2, GREEN); + } else if (blocks[i][j] == 2) { // おじゃまブロック + canvas.fillRect(bx + 1, by + 1, blockW - 2, blockH - 2, GRAY); } } } // バー - M5.Display.fillRect(barX, barY, barW, barH, WHITE); + canvas.fillRect(barX, barY, barW, barH, WHITE); // ボール - M5.Display.fillCircle((int)ballX, (int)ballY, ballR, RED); + canvas.fillCircle((int)ballX, (int)ballY, ballR, RED); // ライフ表示 - M5.Display.setTextColor(WHITE); - M5.Display.setCursor(5, 5); - M5.Display.printf("Life: %d", life); + canvas.setTextColor(WHITE); + canvas.setCursor(5, 5); + canvas.printf("Life: %d", life); + + //canvas 反映 + canvas.pushSprite(0, 0); 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); ///受信時のコールバック関数を指定する - } } \ No newline at end of file