ESP32C3 super mini+SH1106とボタン
調子に乗って6回目になってしまった。「Fruit Basket」は遊びにあるが全く関係ない。落ちてくる果物をかごで受け止めるだけのゲーム。
ただし、さるが落ちてくるので受け取ってしまったら減点。
5ステージまであり、時間と得点との戦い。
スケッチはこのようになった。
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
const int BTN_LEFT = 2;
const int BTN_RIGHT = 3;
const int BUZZER_PIN = 4;
// --- ドット絵データ (8x8) ---
static const unsigned char apple_bits = { 0x0c, 0x1e, 0x3f, 0x3f, 0x3f, 0x3f, 0x1e, 0x00 };
static const unsigned char cherry_bits = { 0x06, 0x0c, 0x18, 0x18, 0x6c, 0xfe, 0xfe, 0x6c };
static const unsigned char banana_bits = { 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80 };
static const unsigned char strawberry_bits = { 0x1c, 0x3e, 0x7f, 0x7f, 0x7f, 0x3e, 0x1c, 0x08 };
static const unsigned char monkey_bits[] = { 0x3c, 0x7e, 0xbd, 0xff, 0xbd, 0xa5, 0x81, 0x7e };
// ゲーム状態
enum GameState { TITLE, PLAYING, STAGE_RESULT, GAME_OVER, GAME_CLEAR };
GameState currentState = TITLE;
int playerX = 64;
int itemX, itemY, itemSpeed, itemType;
int score = 0;
int stage = 1;
const int goalScore = 10;
unsigned long startTime;
unsigned long resultStartTime;
const int gameDuration = 30000;
void resetItem() {
itemX = random(5, 115);
itemY = 0;
itemSpeed = random(1 + stage, 4 + stage);
itemType = random(0, 5);
}
void drawBasket(int x, int y) {
u8g2.drawFrame(x, y, 18, 8);
for(int i = 2; i < 18; i += 4) {
u8g2.drawLine(x + i, y, x + i - 2, y + 8);
u8g2.drawLine(x + i, y + 8, x + i + 2, y);
}
}
// タイトル画面の描画
void showTitle() {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawStr(5, 20, "Fruit Basket");
u8g2.setFont(u8g2_font_6x10_tf);
// ルール説明のレイアウト
u8g2.drawXBM(15, 30, 8, 8, apple_bits);
u8g2.drawXBM(30, 30, 8, 8, cherry_bits);
u8g2.drawXBM(45, 30, 8, 8, banana_bits);
u8g2.drawXBM(60, 30, 8, 8, strawberry_bits);
u8g2.drawStr(75, 38, ": +1pt");
u8g2.drawXBM(35, 45, 8, 8, monkey_bits);
u8g2.drawStr(50, 53, ": -2pt");
u8g2.drawStr(18, 63, "- Press Button -");
u8g2.sendBuffer();
}
void setup() {
pinMode(BTN_LEFT, INPUT_PULLUP);
pinMode(BTN_RIGHT, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
u8g2.begin();
resetItem();
}
void loop() {
if (currentState == TITLE) {
showTitle();
if (digitalRead(BTN_LEFT) == LOW || digitalRead(BTN_RIGHT) == LOW) {
tone(BUZZER_PIN, 1000, 100);
delay(200);
stage = 1; score = 0;
startTime = millis();
currentState = PLAYING;
}
}
else if (currentState == PLAYING) {
unsigned long elapsed = millis() - startTime;
if (elapsed >= gameDuration) {
currentState = GAME_OVER;
tone(BUZZER_PIN, 200, 500);
}
if (digitalRead(BTN_LEFT) == LOW) playerX -= (4 + stage);
if (digitalRead(BTN_RIGHT) == LOW) playerX += (4 + stage);
playerX = constrain(playerX, 0, 110);
itemY += itemSpeed;
if (itemY > 64) resetItem();
if (itemY >= 50 && itemY <= 58 && itemX >= playerX - 4 && itemX <= playerX + 18) {
if (itemType == 4) { // 猿
score -= 2; if (score < 0) score = 0;
tone(BUZZER_PIN, 150, 150);
} else { // フルーツ
score++;
tone(BUZZER_PIN, 1000 + (stage * 100), 50);
}
resetItem();
if (score >= goalScore) {
if (stage < 5) {
currentState = STAGE_RESULT;
resultStartTime = millis();
tone(BUZZER_PIN, 1500, 200); delay(200);
tone(BUZZER_PIN, 2000, 400);
} else {
currentState = GAME_CLEAR;
tone(BUZZER_PIN, 2500, 600);
}
}
}
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_6x10_tf);
u8g2.setCursor(0, 10); u8g2.print("STG:"); u8g2.print(stage);
u8g2.setCursor(45, 10); u8g2.print("SC:"); u8g2.print(score);
u8g2.setCursor(90, 10); u8g2.print("T:"); u8g2.print((gameDuration - elapsed) / 1000);
drawBasket(playerX, 55);
switch(itemType) {
case 0: u8g2.drawXBM(itemX, itemY, 8, 8, apple_bits); break;
case 1: u8g2.drawXBM(itemX, itemY, 8, 8, cherry_bits); break;
case 2: u8g2.drawXBM(itemX, itemY, 8, 8, banana_bits); break;
case 3: u8g2.drawXBM(itemX, itemY, 8, 8, strawberry_bits); break;
case 4: u8g2.drawXBM(itemX, itemY, 8, 8, monkey_bits); break;
}
u8g2.sendBuffer();
}
else if (currentState == STAGE_RESULT) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawStr(0, 30, "STAGE CLEAR!");
u8g2.setFont(u8g2_font_6x10_tf);
u8g2.setCursor(30, 50); u8g2.print("Next: STAGE "); u8g2.print(stage + 1);
u8g2.sendBuffer();
if (millis() - resultStartTime > 3000) {
stage++; score = 0; startTime = millis(); resetItem(); currentState = PLAYING;
}
}
else {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB14_tr);
if (currentState == GAME_CLEAR) u8g2.drawStr(5, 35, "ALL CLEAR!");
else u8g2.drawStr(15, 35, "TIME UP!");
u8g2.setFont(u8g2_font_6x10_tf);
u8g2.drawStr(5, 55, "Press BOTH to Title");
u8g2.sendBuffer();
if (digitalRead(BTN_LEFT) == LOW && digitalRead(BTN_RIGHT) == LOW) {
currentState = TITLE;
delay(500);
}
}
delay(30);
}
割合と短くてすんだ。
ミニゲームは単純がいいかな。パーツも小さくて小型化できるから携帯にいい。
といっても、工作が苦手な自分。どうしたもんやら?
接続
ESP32C3 SH1106 SW BUZZER
GPIO 8 SDA
GPIO9 SCL
GPIO4 +(もう一方はーへ)
GPIO3 R(もう一方はーへ)
GPIO2 L(もう一方はーへ)



動いているのを撮るのは難しい。サクランボはなかなかよくできたと思っているのだが・・・。撮れなかった。
この前にカーレースを作ってみようとしたのだが失敗。道路の方を常に描いて動いているように見せようとしたのだが結果的に失敗。Aiも頑張ってくれたのだが。こちらの指示の仕方が悪かったのだろう。Aiとのやりとりも最長になった。
疲れたので、またある程度仕様が定まったらやってみよう。