Skip to content

Instantly share code, notes, and snippets.

@Yuikawa-Akira
Created June 24, 2025 11:04
Show Gist options
  • Save Yuikawa-Akira/deb038b485f6e250e07cf839cf232eb7 to your computer and use it in GitHub Desktop.
Save Yuikawa-Akira/deb038b485f6e250e07cf839cf232eb7 to your computer and use it in GitHub Desktop.
Pong Wars for Unit Puzzle
/*
PONG WARS部分は以下を参考にした
https://github.com/anoken/pong-wars-forM5Stack
*/
#include <M5Unified.h>
#include <FastLED.h>
M5Canvas canvas_0;
M5Canvas canvas_LED;
const int pin_to_leds = 2;
const int num_of_leds = 64;
CRGB leds[num_of_leds];
int MODE = 0;
uint8_t Brightness = 0;
#define LED_WIDTH 8
#define LED_HEIGHT 8
void drawPanel_LEDmatrix() {
int i = 0;
for (int y = 0; y < LED_HEIGHT; y++) {
for (int x = 0; x < LED_WIDTH; x++) {
int32_t colorRGB = canvas_LED.readPixelValue(x, y);
int8_t colorR = (colorRGB >> 0) & 0xFF;
int8_t colorG = (colorRGB >> 8) & 0xFF;
int8_t colorB = (colorRGB >> 16) & 0xFF;
leds[i] = CRGB(colorR, colorG, colorB);
i++;
}
}
FastLED.show();
}
uint32_t color_A = 0;
uint32_t color_B = 0;
uint32_t color_P = 0;
// 画像の幅と高さ、四角形のサイズ、四角形の数を定義
int img_width = 64;
int img_height = 64;
int SQUARE_SIZE = 8; //2以上でないとおかしくなる
int numSquaresX = img_width / SQUARE_SIZE; // + 1;
int numSquaresY = img_height / SQUARE_SIZE; // + 1;
// 四角形の位置情報を格納する2次元ベクトル
std::vector<std::vector<int>> squares;
// M5UnifiedのCanvasオブジェクト
M5Canvas canvas[2];
static int_fast16_t sprite_height;
// ボールの情報を格納する構造体のベクトル
struct _pong {
double x;
double y;
double dx;
double dy;
int class_num;
};
double vel = SQUARE_SIZE / 2;
std::vector<_pong> pong;
// 乱数生成関数
double randomNum(double min, double max) {
int rand_int = random(5, 100) % 100;
double rand_uni = (rand_int / 100.0) * (max - min) + min;
return rand_uni;
}
// 数値の符号を返す関数
double sign(double A) {
if (A == 0)
return 0;
else
return A / abs(A);
}
// 四角形とボールの衝突判定と反射を処理する関数
int updateSquareAndBounce(int numSquaresX, int numSquaresY, double x, double y, double &dx, double &dy, int class_num) {
double updatedDx = dx;
double updatedDy = dy;
// ボールの円周上の複数のポイントをチェックする
for (double angle = 0; angle < M_PI * 2; angle += M_PI / 4) {
int checkX = x + cos(angle) * (SQUARE_SIZE / 2);
int checkY = y + sin(angle) * (SQUARE_SIZE / 2);
// チェックしたポイントが画面内かどうか確認
int i = floor(checkX / SQUARE_SIZE);
int j = floor(checkY / SQUARE_SIZE);
if (i >= 0 && i < numSquaresX && j >= 0 && j < numSquaresY) {
if (squares[i][j] != class_num) {
squares[i][j] = class_num;
// 角度からバウンド方向を決定
if (abs(cos(angle)) > abs(sin(angle))) {
updatedDx = -updatedDx;
} else {
updatedDy = -updatedDy;
}
}
}
}
// ボールがループにはまらないように、バウンドにノイズを加える
double theta = M_PI / 4 * (1 + (map(random(1, 10), 1, 10, -1, 10)) * 0.1);
dx = sign(updatedDx) * vel * cos(theta);
dy = sign(updatedDy) * vel * sin(theta);
return 0;
}
// 画面の境界とボールの衝突を判定し、必要に応じて反射させる関数
int checkBoundaryCollision(int image_width, int img_height, double x, double y, double &dx, double &dy) {
if (x + dx > image_width - SQUARE_SIZE / 2 || x + dx < SQUARE_SIZE / 2) {
dx = -dx;
}
if (y + dy > img_height - SQUARE_SIZE / 2 || y + dy < SQUARE_SIZE / 2) {
dy = -dy;
}
return 0;
}
void initDisplays(void) {
auto cfg = M5.config();
M5.begin(cfg);
canvas_LED.setColorDepth(24);
canvas_LED.createSprite(8, 8);
canvas_0.setColorDepth(24);
canvas_0.createSprite(img_width, img_height);
FastLED.addLeds<WS2812B, pin_to_leds, GRB>(leds, num_of_leds);
Brightness = 3;
FastLED.setBrightness(Brightness);
delay(100);
}
void setup(void) {
initDisplays();
uint32_t div = 2;
for (;;) {
sprite_height = (img_height + div - 1) / div;
bool fail = false;
for (std::uint32_t i = 0; !fail && i < div; ++i) {
canvas[i].setColorDepth(16);
canvas[i].setFont(&fonts::Font2);
fail = !canvas[i].createSprite(img_width, sprite_height);
}
if (!fail)
break;
for (std::uint32_t i = 0; i < div; ++i) {
canvas[i].deleteSprite();
}
++div;
}
// 四角形の位置情報を初期化
squares.resize(numSquaresX);
for (int i = 0; i < numSquaresX; i++) {
squares[i].resize(numSquaresY);
}
// 画面を4つのエリアに分割し、それぞれに異なるクラスの四角形を配置
int pong_no = 2;
pong.resize(pong_no);
for (int i = 0; i < numSquaresX; i++) {
for (int j = 0; j < numSquaresY; j++) {
if (i < (numSquaresX / 2)) {
squares[i][j] = 0;
} else {
squares[i][j] = 1;
}
}
}
// ボールの初期位置と速度を設定
pong[0].x = img_width / 4;
pong[0].y = img_height / 4;
pong[0].dx = vel * random(5, 100);
pong[0].dy = -vel * random(5, 100);
pong[0].class_num = 0;
pong[1].x = img_width * 3 / 4;
pong[1].y = img_height / 4;
pong[1].dx = -vel * random(5, 100);
pong[1].dy = vel * random(5, 100);
pong[1].class_num = 1;
color_A = 0x000000;
color_B = 0xFFFFFF;
}
void loop(void) {
static uint8_t flip = 0;
if (M5.BtnA.wasPressed()) {
MODE = (MODE + 1) % 4;
switch (MODE) {
case 0:
color_A = 0x000000;
color_B = 0xFFFFFF;
break;
case 1:
color_A = 0x0000FF;
color_B = 0xFFFF00;
break;
case 2:
color_A = 0xFF0000;
color_B = 0x00FFFF;
break;
case 3:
color_A = 0x00FF00;
color_B = 0xFF00FF;
break;
}
delay(1);
}
for (int i = 0; i < pong.size(); i++) {
// 四角形との衝突判定と反射、境界との衝突判定
updateSquareAndBounce(numSquaresX, numSquaresY, pong[i].x, pong[i].y, pong[i].dx, pong[i].dy, pong[i].class_num);
checkBoundaryCollision(img_width, img_height, pong[i].x, pong[i].y, pong[i].dx, pong[i].dy);
}
for (int_fast16_t y = 0; y < img_height; y += sprite_height) {
flip = flip ? 0 : 1;
canvas[flip].clear();
// 四角形の描画
for (int i = 0; i < squares.size(); i++) {
for (int j = 0; j < squares[i].size(); j++) {
// 各クラスに応じて四角形を描画
if (squares[i][j] == 0) {
canvas[flip].fillRect(i * SQUARE_SIZE, j * SQUARE_SIZE - y, SQUARE_SIZE, SQUARE_SIZE, color_A);
}
if (squares[i][j] == 1) {
canvas[flip].fillRect(i * SQUARE_SIZE, j * SQUARE_SIZE - y, SQUARE_SIZE, SQUARE_SIZE, color_B);
}
}
}
// ボールの描画と更新
for (int i = 0; i < pong.size(); i++) {
// 各クラスに応じてボールの色を設定
if (pong[i].class_num == 0)
color_P = color_B;
if (pong[i].class_num == 1)
color_P = color_A;
// ボールを描画
//canvas[flip].fillCircle(pong[i].x, pong[i].y - y, SQUARE_SIZE / 2, color_P);
canvas[flip].fillRect(pong[i].x - SQUARE_SIZE / 2, pong[i].y - y - SQUARE_SIZE / 2, SQUARE_SIZE, SQUARE_SIZE, color_P); //(x,y,w,h,color)
// ボールの位置を更新
pong[i].x += pong[i].dx;
pong[i].y += pong[i].dy;
}
// Canvasに描画された内容をスクリーンに転送
canvas[flip].pushSprite(&canvas_0, 0, y);
}
canvas_0.pushRotateZoom(&canvas_LED, 4, 4, 0, 0.125, 0.125);
drawPanel_LEDmatrix();
delay(30);
M5.update();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment