diff --git a/Another.ino.txt b/Another.ino.txt new file mode 100644 index 0000000..e26a4a6 --- /dev/null +++ b/Another.ino.txt @@ -0,0 +1,332 @@ +#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