Skip to content

Instantly share code, notes, and snippets.

@Yuikawa-Akira
Last active May 23, 2025 17:40
Show Gist options
  • Save Yuikawa-Akira/1cb6fcb367ee46bd4ef842d55f3c83eb to your computer and use it in GitHub Desktop.
Save Yuikawa-Akira/1cb6fcb367ee46bd4ef842d55f3c83eb to your computer and use it in GitHub Desktop.
Random-Particle-Snow-for-Tab5
#include <M5Unified.h> // 0.2.7 以降
// --- パラメータ設定 ---
const int dotScale = 16; // 2の倍数
const int CANVAS_WIDTH = 1280 / dotScale;
const int CANVAS_HEIGHT = 720 / dotScale;
// ドット落下速度
const int FALL_SPEED_MS = 10;
// 新しいドットが出現する間隔
const int NEW_DOT_INTERVAL_MS = 10;
// 同時に落下できるドットの最大数
const int MAX_FALLING_DOTS = CANVAS_HEIGHT * FALL_SPEED_MS / NEW_DOT_INTERVAL_MS;
// グリッド上に固定されたドットの数
int fixedDotCount = 0;
// グリッドの状態を保持する2D配列 (0: 空, 1: ドットあり)
uint8_t grid[CANVAS_WIDTH][CANVAS_HEIGHT];
// 落下中のドットを表す構造体
struct FallingDot {
int x;
int y;
int r;
int g;
int b;
bool active; // 落下中かどうか
};
FallingDot fallingDots[MAX_FALLING_DOTS];
// タイマー用変数
unsigned long lastNewDotTime = 0;
unsigned long lastFallTime = 0;
// グリッドを初期化する関数
void initializeGrid() {
memset(grid, 0, sizeof(grid));
}
// 落下中のドットを初期化する関数
void initializeFallingDots() {
for (int i = 0; i < MAX_FALLING_DOTS; ++i) {
fallingDots[i].active = false;
}
}
void setup() {
auto cfg = M5.config();
M5.begin(cfg);
M5.Display.fillScreen(TFT_WHITE);
initializeGrid();
initializeFallingDots();
M5.delay(500);
// 最初の描画
M5.Display.fillScreen(TFT_BLACK); // 背景を黒でクリア
}
void loop() {
unsigned long currentTime = millis();
// 新しいドットの出現を試みる
if (currentTime - lastNewDotTime > NEW_DOT_INTERVAL_MS) {
lastNewDotTime = currentTime;
// 空いている落下中のドットスロットを探す
int freeSlot = -1;
for (int i = 0; i < MAX_FALLING_DOTS; ++i) {
if (!fallingDots[i].active) {
freeSlot = i;
break;
}
}
if (freeSlot != -1) {
int newX = random(CANVAS_WIDTH); // x座標はランダム
int newY = 0; // 最上段に出現
// 出現位置にドットが存在しないかチェック
if (grid[newX][newY] == 0) {
fallingDots[freeSlot].x = newX;
fallingDots[freeSlot].y = newY;
fallingDots[freeSlot].r = random(2, 15) * 16;
fallingDots[freeSlot].g = random(2, 15) * 16;
fallingDots[freeSlot].b = random(2, 15) * 16;
fallingDots[freeSlot].active = true;
}
}
}
// ドットの落下処理
if (currentTime - lastFallTime > FALL_SPEED_MS) {
lastFallTime = currentTime;
for (int i = 0; i < MAX_FALLING_DOTS; ++i) {
if (fallingDots[i].active) {
M5.Display.fillRect((fallingDots[i].y - 1) * dotScale, fallingDots[i].x * dotScale, dotScale, dotScale, TFT_BLACK); // 1個上を消す
M5.Display.fillRect(fallingDots[i].y * dotScale, fallingDots[i].x * dotScale, dotScale, dotScale, M5.Display.color565(fallingDots[i].r, fallingDots[i].g, fallingDots[i].b));
int currentX = fallingDots[i].x;
int currentY = fallingDots[i].y;
int nextY = currentY + 1;
// 落下可能かチェック (最下段に達していない & 下にドットがない)
if (nextY < CANVAS_HEIGHT && grid[currentX][nextY] == 0) {
// 落下中のドットの座標を更新
fallingDots[i].y = nextY;
} else {
// 落下終了
grid[currentX][currentY] = 1; // グリッドにドットを配置
M5.Display.fillRect(currentY * dotScale, currentX * dotScale, dotScale, dotScale, M5.Display.color565(fallingDots[i].r, fallingDots[i].g, fallingDots[i].b));
fallingDots[i].active = false; // 落下を停止
fixedDotCount++; // ここでカウントを増やす
}
}
}
}
M5.delay(1);
// 全てのグリッドが埋まったらリセット
if (fixedDotCount >= (CANVAS_WIDTH * CANVAS_HEIGHT)) {
initializeGrid();
initializeFallingDots();
fixedDotCount = 0; // カウントをリセット
M5.Display.fillScreen(TFT_BLACK); // 画面をクリア
M5.delay(1000); // 1秒待機
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment