#include <M5Unified.h>
#include <random>
// ===== キャラクターのグラフィックデータ =====
const int CAT_WIDTH = 24;
const int CAT_HEIGHT = 24;
const int FISH_WIDTH = 32;
const int FISH_HEIGHT = 16;
#define C_BG TFT_BLACK
#define C_CAT TFT_YELLOW
#define C_FISH_BODY TFT_WHITE
#define C_FISH_FIN TFT_RED
#define C_BLACK TFT_BLACK
#define C_WHITE TFT_WHITE
#define C_PINK 0xFE19
// ★★★★★ 猫のドット絵データ ★★★★★
const uint16_t cat_sprite[CAT_WIDTH * CAT_HEIGHT] = {
C_BG, C_BG, C_BG, C_BG, C_CAT, C_CAT, C_BG, C_BG, C_BG, C_BG, C_BG, C_BG, C_BG, C_BG, C_BG, C_BG, C_CAT, C_CAT, C_BG, C_BG, C_BG, C_BG, C_BG, C_BG,
C_BG, C_BG, C_BG, C_CAT, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_BG, C_BG, C_BG, C_BG, C_BG, C_BG, C_CAT, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_BG, C_BG, C_BG,
C_BG, C_BG, C_CAT, C_CAT, C_PINK, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_BG, C_BG, C_BG, C_BG, C_CAT, C_CAT, C_CAT, C_PINK, C_CAT, C_CAT, C_BG, C_BG, C_BG, C_BG,
C_BG, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_BG, C_BG, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_BG,
C_BG, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_BG,
C_CAT, C_CAT, C_CAT, C_WHITE, C_WHITE, C_CAT, C_CAT, C_CAT, C_CAT, C_WHITE, C_WHITE, C_CAT, C_CAT, C_CAT, C_CAT, C_WHITE, C_WHITE, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_BG, C_BG,
C_CAT, C_CAT, C_BLACK, C_WHITE, C_WHITE, C_BLACK, C_CAT, C_CAT, C_BLACK, C_WHITE, C_WHITE, C_BLACK, C_CAT, C_CAT, C_BLACK, C_WHITE, C_WHITE, C_BLACK, C_CAT, C_CAT, C_CAT, C_CAT, C_BG, C_BG,
C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_BG, C_BG,
C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_BG, C_BG,
C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_BLACK, C_CAT, C_CAT, C_BLACK, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_BG, C_BG,
C_BG, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_BLACK, C_BLACK, C_BLACK, C_BLACK, C_BLACK, C_BLACK, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_BG,
C_BG, C_BG, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_BG, C_BG,
C_BG, C_BG, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_BG, C_BG,
C_BG, C_BG, C_BG, C_CAT, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_CAT, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_BG, C_BG, C_BG, C_BG,
C_BG, C_BG, C_BG, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_BG, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_BG, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_BG, C_BG, C_BG, C_BG,
C_BG, C_BG, C_BG, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_BG, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_BG, C_CAT, C_CAT, C_CAT, C_BG, C_BG, C_BG, C_BG, C_BG, C_BG,
};
// ★★★★★ 魚のドット絵データ ★★★★★
const uint16_t fish_sprite[FISH_WIDTH * FISH_HEIGHT] = {
C_BG,C_BG,C_BG,C_BG,C_BG,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,
C_BG,C_BG,C_BG,C_BG,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,
C_BG,C_BG,C_BG,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_BG,C_FISH_FIN,C_FISH_FIN,C_FISH_FIN,C_FISH_FIN,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,
C_BG,C_BG,C_FISH_BODY,C_FISH_BODY,C_BLACK,C_BLACK,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_BLACK,C_FISH_FIN,C_FISH_FIN,C_FISH_FIN,C_FISH_FIN,C_FISH_FIN,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,
C_BG,C_FISH_BODY,C_FISH_BODY,C_BLACK,C_BLACK,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_BLACK,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,
C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_BLACK,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_FIN,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,
C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_BLACK,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_FIN,C_FISH_FIN,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,
C_BG,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_BLACK,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_FIN,C_FISH_FIN,C_FISH_FIN,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,
C_BG,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_BLACK,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_FIN,C_FISH_FIN,C_FISH_FIN,C_FISH_FIN,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,
C_BG,C_BG,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_BLACK,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_FIN,C_FISH_FIN,C_FISH_FIN,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,
C_BG,C_BG,C_BG,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_BG,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_FIN,C_FISH_FIN,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,
C_BG,C_BG,C_BG,C_BG,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_BG,C_BG,C_FISH_FIN,C_FISH_FIN,C_FISH_FIN,C_FISH_FIN,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,
C_BG,C_BG,C_BG,C_BG,C_BG,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_FISH_BODY,C_BG,C_BG,C_BG,C_BG,C_FISH_FIN,C_FISH_FIN,C_FISH_FIN,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,
C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,C_BG,
};
// ===== ゲームのロジック =====
enum GameState { TITLE, PLAYING, GAME_OVER };
GameState gameState = TITLE;
const int MOVE_SPEED = 5;
const float GRAVITY = 0.5;
const float JUMP_STRENGTH = -10;
const int GAME_DURATION_SEC = 30;
float catX, catY, catVY = 0;
int fishX, fishY, score = 0, groundY;
unsigned long gameStartTime = 0;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distribX, distribY;
M5Canvas canvas(&M5.Display);
void placeFish() {
fishX = distribX(gen);
fishY = distribY(gen);
}
void resetGame() {
score = 0;
catX = M5.Display.width() / 2 - CAT_WIDTH / 2;
catY = groundY;
catVY = 0;
placeFish();
gameStartTime = millis();
}
void setup() {
M5.begin();
M5.Speaker.begin();
groundY = M5.Display.height() - CAT_HEIGHT - 5;
float jump_displacement = (JUMP_STRENGTH * JUMP_STRENGTH) / (2 * GRAVITY);
int jump_peak_y = groundY - jump_displacement;
distribX = std::uniform_int_distribution<>(FISH_WIDTH, M5.Display.width() - FISH_WIDTH);
distribY = std::uniform_int_distribution<>(jump_peak_y, groundY - FISH_HEIGHT);
canvas.createSprite(M5.Display.width(), M5.Display.height());
}
void loop() {
M5.update();
switch (gameState) {
case TITLE:
canvas.fillScreen(C_BG);
canvas.setTextDatum(middle_center);
canvas.setTextColor(C_CAT);
canvas.setTextSize(3);
canvas.drawString("Meow Meow", M5.Display.width() / 2, M5.Display.height() / 2 - 40);
canvas.drawString("Catch!", M5.Display.width() / 2, M5.Display.height() / 2);
canvas.setTextColor(C_WHITE);
canvas.setTextSize(2);
canvas.drawString("Press B to Start", M5.Display.width() / 2, M5.Display.height() / 2 + 50);
canvas.pushSprite(0, 0);
if (M5.BtnB.wasPressed()) {
resetGame();
gameState = PLAYING;
}
break;
case PLAYING: {
if (M5.BtnA.isPressed()) catX -= MOVE_SPEED;
if (M5.BtnC.isPressed()) catX += MOVE_SPEED;
if (M5.BtnB.wasPressed() && catY == groundY) catVY = JUMP_STRENGTH;
catVY += GRAVITY;
catY += catVY;
if (catY >= groundY) { catY = groundY; catVY = 0; }
if (catX < 0) catX = 0;
if (catX > M5.Display.width() - CAT_WIDTH) catX = M5.Display.width() - CAT_WIDTH;
if (abs((catX + CAT_WIDTH / 2) - (fishX + FISH_WIDTH / 2)) < (CAT_WIDTH + FISH_WIDTH) / 2 - 4 &&
abs((catY + CAT_HEIGHT / 2) - (fishY + FISH_HEIGHT / 2)) < (CAT_HEIGHT + FISH_HEIGHT) / 2 - 4) {
score++;
placeFish();
M5.Speaker.tone(1200, 150);
}
canvas.fillScreen(C_BG);
canvas.pushImage(fishX, fishY, FISH_WIDTH, FISH_HEIGHT, fish_sprite, C_BG);
canvas.pushImage(catX, catY, CAT_WIDTH, CAT_HEIGHT, cat_sprite, C_BG);
unsigned long elapsedTime = (millis() - gameStartTime) / 1000;
int remainingTime = GAME_DURATION_SEC - elapsedTime;
canvas.setTextColor(C_WHITE, C_BG);
canvas.setTextSize(2);
canvas.setCursor(10, 10);
canvas.printf("Score: %d", score);
canvas.setTextDatum(top_right);
canvas.drawString("Time: " + String(remainingTime), M5.Display.width() - 10, 10);
canvas.setTextDatum(top_left);
canvas.pushSprite(0, 0);
if (remainingTime <= 0) {
M5.Speaker.tone(500, 300);
gameState = GAME_OVER;
}
break;
}
case GAME_OVER:
canvas.fillScreen(C_BG);
canvas.setTextDatum(middle_center);
canvas.setTextColor(TFT_RED);
canvas.setTextSize(3);
canvas.drawString("Time's Up!", M5.Display.width() / 2, M5.Display.height() / 2 - 40);
canvas.setTextColor(C_WHITE);
canvas.setTextSize(2);
canvas.drawString("Final Score", M5.Display.width() / 2, M5.Display.height() / 2);
canvas.setTextSize(3);
canvas.drawString(String(score), M5.Display.width() / 2, M5.Display.height() / 2 + 30);
canvas.setTextSize(2);
canvas.drawString("Press B to Restart", M5.Display.width() / 2, M5.Display.height() / 2 + 70);
canvas.pushSprite(0, 0);
if (M5.BtnB.wasPressed()) {
gameState = TITLE;
}
break;
}
delay(10);
}
Comments