Rena
Published

Fish Catch Game

This is a game where you move the cat to catch the fish. The time limit is 30 seconds. When you catch

BeginnerShowcase (no instructions)5 hours38
Fish Catch Game

Things used in this project

Hardware components

M5Stack Core2 ESP32 IoT Development Kit
M5Stack Core2 ESP32 IoT Development Kit
×1

Software apps and online services

PlatformIO IDE
PlatformIO IDE

Story

Read more

Code

Fish Catch Game

C/C++
Hard Wear: M5Stack Core2
#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);
}

Credits

Rena
1 project • 0 followers

Comments