Created
June 24, 2025 11:04
-
-
Save Yuikawa-Akira/deb038b485f6e250e07cf839cf232eb7 to your computer and use it in GitHub Desktop.
Pong Wars for Unit Puzzle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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