/** * Rallx.<br> * 迷路脱出ゲームレーダー付き<br><br> * * Copyright 2004 Kenta Cho. All rights reserved. * * @p5 description * -車をマウスで操作して、全ての旗を回収、迷路の中央の出口に向かってください。 * -Control a car with your mouse, get all flags and go to the exit at the center of the maze. * -赤い岩にぶつかると車を失います。 * -Avoid red square shaped rocks. You lose a car when you crash into a rock. * -マウスボタンを押しっぱなしでレーダーを見られます。迷路全体を見渡すことができますが、岩は表示されません。 * -Hold a mouse button to see the radar screen. You can see the entire maze but rocks are not shown on the radar. * -画面下にある残り時間がなくなっても車を失います。 * -You also lose a car when a time(displayed at the bottom of screen) runs out. */ public class RallX extends BApplet { /** * 迷路データ(0-空、1-壁、2-ゴール、3-旗、10〜-岩) */ int[][] maze; /** * 迷路や出口の大きさ用定数 */ int MAZE_SIZE = 49, EXIT_SIZE = 7; /** * 迷路1ブロックのレーダー上の大きさ */ int mazeRectSize; /** * 迷路1ブロックのスクロール画面上の大きさ */ float mazeWallSize; /** * 出口の方向 */ int exitD; /** * スクロール画面内に表示するブロック数 */ int wallSightRange; /** * 自車関連データ */ float cx, cy, cd, cmd, cmx, cmy, csp, cmsp; /** * 自車のいるブロック位置 */ int wx, wy; /** * レーダー切り替え時カウンタ */ int rcnt; /** * レーダー切り替え時アルファ値 */ int ca; /** * ラウンドクリア時カウンタ */ int ccnt; /** * 自車クラッシュ時カウンタ */ int dcnt; /** * 画面中央に表示するメッセージ */ String msg; /** * メッセージ用カウンタ */ int mcnt; /** * パーティクルの最大数 */ int PARTICLE_NUM = 32; /** * パーティクルインスタンスプール */ Particle[] particle; /** * 岩の最大数 */ int ROCK_NUM = 50; /** * 岩インスタンスプール */ Rock[] rock; /** * 残りの旗の数 */ int flagNum; /** * ステージ数 */ int stage; /** * スコア */ float score; /** * 残機数 */ int left; /** * 残り時間 */ int time; /** * ゲーム状態用定数 */ int TITLE = 0, IN_GAME = 1; /** * ゲームの状態(タイトル/ゲーム中) */ int state; /** * 各面のスタート時の残り時間(30frame * 60sec) */ int STAGE_TIME = 1800; /** * フォントデータ */ BFont font; /** * BGMデータ */ BSound bgm; /** * SEデータ */ BSound flagSe, crashSe, clearSe; /** * 初期化(フォント、サウンド読み込み、インスタンス生成) */ void setup() { size(300, 300); framerate(30); smooth(); cursor(); ellipseMode(CENTER_DIAMETER); font = loadFont("OCR-A.vlw.gz"); textFont(font); bgm = loadSound("bgm.wav"); flagSe = loadSound("flag.wav"); crashSe = loadSound("crash.wav"); clearSe = loadSound("clear.wav"); mazeRectSize = width / MAZE_SIZE; maze = new int[MAZE_SIZE][MAZE_SIZE]; mazeWallSize = 40; wallSightRange = (int) (width / mazeWallSize) / 2 + 2; particle = new Particle[PARTICLE_NUM]; for (int i = 0; i < particle.length; i++) particle[i] = new Particle(); rock = new Rock[ROCK_NUM]; for (int i = 0; i < rock.length; i++) rock[i] = new Rock(); startTitle(); } /** * タイトル初期化 */ void startTitle() { state = TITLE; stop(bgm); exitD = 0; stage = 0; left = 0; initStage(); ca = 150; time = -999; float d = random(PI * 2); cmx = sin(d); cmy = cos(d); mcnt = 0; } /** * ゲーム開始 */ void startGame() { state = IN_GAME; exitD = 0; stage = -1; score = 0; left = 2; initStage(); volume(bgm, 1.0f); jump(bgm, 0); repeat(bgm); } /** * ステージ生成 */ void initStage() { // Init a car potision. switch (exitD) { case 0 : cx = MAZE_SIZE * mazeWallSize / 2; cy = mazeWallSize * 1.5f; cd = PI; break; case 1 : cx = MAZE_SIZE * mazeWallSize - mazeWallSize * 1.5f; cy = MAZE_SIZE * mazeWallSize / 2; cd = PI / 2 * 3; break; case 2 : cx = MAZE_SIZE * mazeWallSize / 2; cy = MAZE_SIZE * mazeWallSize - mazeWallSize * 1.5f; cd = 0; break; case 3 : cx = mazeWallSize * 1.5f; cy = MAZE_SIZE * mazeWallSize / 2; cd = PI / 2; break; } cmx = cmy = 0; csp = 0; rcnt = 0; ccnt = -8; dcnt = 0; time = STAGE_TIME; // Set a stage pattern. stage++; int rockNum = 0; switch (stage % 6) { case 0 : mazeGrid = 8; mazePtn = 0; rockNum = stage * 3; flagNum = 4; break; case 1 : mazeGrid = 4; mazePtn = 0; rockNum = stage - 1; flagNum = 0; break; case 2 : mazePtn = 1; rockNum = 10 + stage * 4; flagNum = 8; break; case 3 : mazeGrid = 6; mazePtn = 0; rockNum = stage * 3; flagNum = 4; break; case 4 : mazeGrid = 3; mazePtn = 0; rockNum = stage; flagNum = 0; break; case 5 : mazeGrid = 6; mazePtn = 2; rockNum = 12 + stage * 3; flagNum = 3; break; } if (rockNum > rock.length) rockNum = rock.length; if (stage == 0) { msg = "Get flags."; mcnt = 60; } else if (stage == 2) { msg = "Avoid rocks."; mcnt = 60; } else if (flagNum == 0) { msg = "Go to the exit."; mcnt = 60; } else { mcnt = 0; } // Create a maze and set flags and rocks. createMaze(); wx = (int) (cx / mazeWallSize); wy = (int) (cy / mazeWallSize); maze[wx][wy] = 1; for (int i = 0; i < flagNum; i++) { int x, y; do { x = (int) random(MAZE_SIZE - 2) + 1; y = (int) random(MAZE_SIZE - 2) + 1; } while (maze[x][y] != 0); maze[x][y] = 3; } for (int i = 0; i < rockNum; i++) { int x = (int) random(MAZE_SIZE - 2) + 1; int y = (int) random(MAZE_SIZE - 2) + 1; if (maze[x][y] == 0) { maze[x][y] = i + 10; rock[i].ox = random(mazeWallSize / 2) + mazeWallSize / 4; rock[i].oy = random(mazeWallSize / 2) + mazeWallSize / 4; rock[i].x = x * mazeWallSize + rock[i].ox; rock[i].y = y * mazeWallSize + rock[i].oy; } } maze[wx][wy] = 0; } /** * 迷路の壁と壁の間隔 */ int mazeGrid; /** * 迷路パターン(0-通常、1-なにもなし、2-うずまき) */ int mazePtn; /** * 壁を延ばした地点の数(迷路作成終了判定用) */ int pointNum; /** * 壁を延ばしている座標 */ int stx, sty, std; /** * 壁を延ばす方向の移動量定数 */ int[] DMX = { 0, 1, 0, -1 }, DMY = { -1, 0, 1, 0 }; /** * うずまき型迷路作成用カウンタ */ int turnCnt; /** * 壁を延ばして迷路作成 */ void createMaze() { for (int x = 0; x < MAZE_SIZE; x++) maze[x][0] = maze[x][MAZE_SIZE - 1] = 1; for (int y = 1; y < MAZE_SIZE - 1; y++) { maze[0][y] = maze[MAZE_SIZE - 1][y] = 1; for (int x = 1; x < MAZE_SIZE - 1; x++) maze[x][y] = 0; } int ep = (MAZE_SIZE - EXIT_SIZE) / 2; int bn = 0; switch (mazePtn) { case 0 : bn = 100; pointNum = MAZE_SIZE / mazeGrid; pointNum -= 1; pointNum *= pointNum; pointNum -= (int) ((EXIT_SIZE + 1) / mazeGrid) * (int) ((EXIT_SIZE + 1) / mazeGrid); break; case 1 : bn = 0; pointNum = 0; break; case 2 : bn = 1; pointNum = 999; turnCnt = 0; break; } for (int i = 0; i < bn; i++) { if (pointNum <= 0) break; if (mazePtn == 0) { stx = (int) random((MAZE_SIZE - 1) / mazeGrid - 1) * mazeGrid + mazeGrid; sty = (int) random((MAZE_SIZE - 1) / mazeGrid - 1) * mazeGrid + mazeGrid; std = (int) random(4); if (maze[stx][sty] == 1) { if (!moveTillNoWall()) continue; } else { if (!moveTillWall()) continue; } } else { switch (exitD) { case 0 : stx = ((int) (MAZE_SIZE / mazeGrid) / 2 + 1) * mazeGrid; sty = 0; std = 2; break; case 1 : stx = MAZE_SIZE - 1; sty = ((int) (MAZE_SIZE / mazeGrid) / 2 + 1) * mazeGrid; std = 3; break; case 2 : stx = ((int) (MAZE_SIZE / mazeGrid) / 2 - 1) * mazeGrid; sty = MAZE_SIZE - 1; std = 0; break; case 3 : stx = 0; sty = ((int) (MAZE_SIZE / mazeGrid) / 2 - 1) * mazeGrid; std = 1; break; } } createMazeBranch(); } // Create the exit hole. fillMaze(ep - mazeGrid + 1, EXIT_SIZE + (mazeGrid - 1) * 2, 0); fillMaze(ep, EXIT_SIZE, 1); fillMaze(ep + 1, EXIT_SIZE - 2, 2); exitD = (int) random(4); int ex, ey; switch (exitD) { case 0 : case 2 : ex = ep + 2; if (exitD == 0) ey = ep; else ey = ep + EXIT_SIZE - 1; for (int x = ex; x < ex + EXIT_SIZE - 4; x++) maze[x][ey] = 0; break; default : ey = ep + 2; if (exitD == 3) ex = ep; else ex = ep + EXIT_SIZE - 1; for (int y = ey; y < ey + EXIT_SIZE - 4; y++) maze[ex][y] = 0; break; } } /** * 壁がない地点まで移動(壁延ばし開始地点探索用) * @return 地点が見つかった */ boolean moveTillNoWall() { int tx = stx, ty = sty; for (;;) { tx += DMX[std] * mazeGrid; ty += DMY[std] * mazeGrid; if (tx < 0 || tx >= MAZE_SIZE || ty < 0 || ty >= MAZE_SIZE) return false; if (maze[tx][ty] == 0) return true; stx = tx; sty = ty; } } /** * 壁がある地点まで移動(壁延ばし開始地点探索用) * @return 地点が見つかった */ boolean moveTillWall() { for (;;) { stx -= DMX[std] * mazeGrid; sty -= DMY[std] * mazeGrid; if (stx < 0 || stx >= MAZE_SIZE || sty < 0 || sty >= MAZE_SIZE) return false; if (maze[stx][sty] == 1) return true; } } /** * 壁を延ばす(一定の確率で曲がり、四方がふさがれたら終了) */ void createMazeBranch() { for (;;) { if (maze[stx + DMX[std] * mazeGrid][sty + DMY[std] * mazeGrid] == 0) { for (int i = 0; i < mazeGrid; i++) { stx += DMX[std]; sty += DMY[std]; maze[stx][sty] = 1; } if (mazePtn == 2) { if ((turnCnt % 6) == 0) { std++; std &= 3; turnCnt++; } } else { if (random(1) < 0.4f) { std += (int) random(2) * 2 - 1; std &= 3; } } pointNum--; if (pointNum <= 0) return; continue; } else { if (mazePtn == 2) { std++; std &= 3; turnCnt++; } else { std += (int) random(2) * 2 - 1; std &= 3; } if (maze[stx + DMX[std] * mazeGrid][sty + DMY[std] * mazeGrid] == 0) continue; std += 2; std &= 3; if (maze[stx + DMX[std] * mazeGrid][sty + DMY[std] * mazeGrid] == 1) return; } } } /** * 出口作成用に迷路を正方形に埋める * @param p 左上の座標 * @param w 大きさ * @param v 埋める値 */ void fillMaze(int p, int w, int v) { for (int y = p; y < p + w; y++) for (int x = p; x < p + w; x++) maze[x][y] = v; } /** * 1フレームごとの処理 */ void loop() { if (state == IN_GAME) loopGame(); else loopTitle(); } /** * マウスリリース時にゲームスタートするためのフラグ */ boolean startPressed = false; /** * タイトル時の1フレーム処理 */ void loopTitle() { background(255); cx += cmx; cy += cmy; wx = (int) (cx / mazeWallSize); wy = (int) (cy / mazeWallSize); if (cx < 0 || cx > MAZE_SIZE * mazeWallSize) cmx *= -1; if (cy < 0 || cy > MAZE_SIZE * mazeWallSize) cmy *= -1; drawMaze(); boolean onStart; if (mouseX > width / 2 - 50 && mouseX < width / 2 + 50 && mouseY > height - 100 && mouseY < height - 75) onStart = true; else onStart = false; push(); translate(0, 0, 30); textSize(42); textMode(ALIGN_CENTER); fill(0, 0, 0); text("RallX", width / 2, height / 3); stroke(250, 100, 50); if (onStart) fill(250, 180, 100); else fill(200, 150, 50); strokeWeight(2); rect(width / 2 - 50, height - 100, 100, 25); strokeWeight(1); fill(0, 0, 0); textSize(25); text("START", width / 2, height - 80); pop(); drawStatus(); if (onStart && mousePressed) startPressed = true; if (!mousePressed && startPressed) { startPressed = false; if (onStart) startGame(); } } /** * ゲーム中の1フレーム処理 */ void loopGame() { background(255); float pcx = cx, pcy = cy; cx += cmx; cy += cmy; cmx += (sin(cd) * csp - cmx) * 0.04f; cmy += (cos(cd) * csp - cmy) * 0.04f; int mox = mouseX - width / 2, moy = mouseY - height / 2; float mouseDst = sqrt(mox * mox + moy * moy); float brk = 0.85f + (mouseDst / (width / 2)) * 0.15f; if (brk > 1) brk = 1; cmx *= brk; cmy *= brk; cmd = atan2(cmx, cmy); cmsp = sqrt(cmx * cmx + cmy * cmy); score += cmsp * 0.05f; float md = adjustDeg(atan2(mox, moy) - cd); cd += md * 0.2f; cd = adjustDeg(cd); wx = (int) (cx / mazeWallSize); wy = (int) (cy / mazeWallSize); int pwx = (int) (pcx / mazeWallSize); int pwy = (int) (pcy / mazeWallSize); if (maze[wx][wy] == 1) { if (maze[pwx][wy] == 0) { cx = pcx; cmx *= -0.8f; } else if (maze[wx][pwy] == 0) { cy = pcy; cmy *= -0.8f; } else { cx = pcx; cmx *= -0.8f; cy = pcy; cmy *= -0.8f; } } else if (maze[wx][wy] == 2 && ccnt == 0 && flagNum <= 0) { ccnt = 1; stop(clearSe); play(clearSe); } else if (maze[wx][wy] == 3) { maze[wx][wy] = 0; score += 500; flagNum--; stop(flagSe); play(flagSe); if (flagNum <= 0) { msg = "Go to the exit."; mcnt = 60; } } else if (maze[wx][wy] >= 10) { int ri = maze[wx][wy] - 10; if (abs(rock[ri].x - cx) + abs(rock[ri].y - cy) < 20) miss(); } float od = abs(adjustDeg(cd - cmd)); if (od > 0.5f && cmsp > 6) addParticle( width / 2, height / 2, cmd + PI + random(-0.3f, 0.3f), cmsp); if (mousePressed) { csp += (5 - csp) * 0.3f; if (rcnt < 8) rcnt++; } else { csp += (20 - csp) * 0.3f; if (rcnt > 0) rcnt--; } if (ccnt > 0) { ccnt++; rcnt = 0; cmx *= 0.8; cmy *= 0.8; csp *= 0.6; if (time > 0) { time -= 30; score += 100; if (time > 30) { time -= 30; score += 100; } } if (ccnt > 30) { initStage(); return; } } else if (ccnt < 0) { ccnt++; } if (dcnt > 0) { dcnt--; rcnt = 0; cmx *= 0.8; cmy *= 0.8; csp *= 0.6; if (dcnt > 30) { float v = 1 - (float) (250 - dcnt) / 60; if (v < 0) v = 0; volume(bgm, v); csp = 0; push(); translate(0, 0, 30); textSize(32); textMode(ALIGN_CENTER); fill(0, 0, 0); text("Game Over", width / 2, height / 3); pop(); if (dcnt < 100 || (dcnt < 230 && mousePressed)) { startTitle(); return; } } if (dcnt == 1) time = STAGE_TIME; } if (dcnt == 0 && ccnt == 0) { time--; if (time <= 0) miss(); } if (rcnt < 8) { push(); if (ccnt > 0) { translate(0, 0, ccnt * 5); ca = 255 - ccnt * 12; if (ca < 0) ca = 0; } else { translate(0, 0, -rcnt * 30); ca = 255 - rcnt * 31; if (ccnt < 0) ca = 255 + ccnt * 31; } if (dcnt <= 0) drawCar(width / 2, height / 2, cd, cmd); drawMaze(); noStroke(); for (int i = 0; i < particle.length; i++) if (particle[i].cnt >= 0) particle[i].loop(); pop(); } if (rcnt > 0) drawMazeOnRadar(); drawStatus(); } /** * 自車が岩に突っ込んだ/時間切れ */ void miss() { stop(crashSe); play(crashSe); dcnt = 30; for (int i = 0; i < 24; i++) addParticle(width / 2, height / 2, random(PI * 2), random(20)); maze[wx][wy] = 0; left--; if (left < 0) dcnt = 250; } /** * 方向を -PI〜PI に収めることで曲がる方向の計算を楽に * @param d 補正前の角度 * @return 補正後の角度 */ float adjustDeg(float d) { if (d > PI) return d - PI * 2; else if (d < -PI) return d + PI * 2; else return d; } /** * 車を書く * @param x x座標 * @param y y座標(残機表示兼用のため座標が指定できる) * @param d1 前輪の方向(マウスの方向を向く) * @param d2 後輪の方向(移動方向を向く後輪) */ void drawCar(int x, int y, float d1, float d2) { fill(100, 150, 250, ca); stroke(150, 200, 255, ca); push(); translate(x, y); rotateZ(-d1); translate(0, 6); rect(-3, -6, 6, 12); pop(); push(); translate(x, y); rotateZ(-d2); translate(-6, -6); rect(-3, -6, 6, 12); translate(12, 0); rect(-3, -6, 6, 12); pop(); } /** * スクロール画面上の迷路表示 */ void drawMaze() { float wsx, wsy; float swsx = -cx + (wx - wallSightRange) * mazeWallSize + width / 2; float swsy = -cy + (wy - wallSightRange) * mazeWallSize + height / 2; wsy = swsy; beginShape(QUADS); for (int y = wy - wallSightRange; y <= wy + wallSightRange; y++, wsy += mazeWallSize) { wsx = swsx; for (int x = wx - wallSightRange; x <= wx + wallSightRange; x++, wsx += mazeWallSize) { if (x >= 0 && x < MAZE_SIZE && y >= 0 && y < MAZE_SIZE) { if (maze[x][y] == 1) drawWall(wsx, wsy); else if (maze[x][y] == 2) drawGoal(wsx, wsy); else if (maze[x][y] == 3) drawFlag(wsx, wsy); else if (maze[x][y] >= 10) { int ri = maze[x][y] - 10; drawRock(wsx, wsy, rock[ri].ox, rock[ri].oy); } } } } endShape(); } /** * 壁のz軸方向高さ */ float WALL_height = 20; /** * 壁を書く * @param sx スクロール画面上のx座標 * @param sy スクロール画面上のy座標 */ void drawWall(float sx, float sy) { fill(200, 250, 50, ca); stroke(100, 150, 100, ca); vertex(sx, sy, WALL_height); vertex(sx + mazeWallSize, sy, WALL_height); vertex(sx + mazeWallSize, sy + mazeWallSize, WALL_height); vertex(sx, sy + mazeWallSize, WALL_height); fill(220, 250, 150, ca); stroke(150, 200, 100, ca); vertex(sx, sy, WALL_height); vertex(sx + mazeWallSize, sy, WALL_height); vertex(sx + mazeWallSize, sy, 0); vertex(sx, sy, 0); vertex(sx + mazeWallSize, sy, WALL_height); vertex(sx + mazeWallSize, sy + mazeWallSize, WALL_height); vertex(sx + mazeWallSize, sy + mazeWallSize, 0); vertex(sx + mazeWallSize, sy, 0); vertex(sx + mazeWallSize, sy + mazeWallSize, WALL_height); vertex(sx, sy + mazeWallSize, WALL_height); vertex(sx, sy + mazeWallSize, 0); vertex(sx + mazeWallSize, sy + mazeWallSize, 0); vertex(sx, sy + mazeWallSize, WALL_height); vertex(sx, sy, WALL_height); vertex(sx, sy, 0); vertex(sx, sy + mazeWallSize, 0); } /** * 旗を回収し終わっていたらゴールを書く * @param sx スクロール画面上のx座標 * @param sy スクロール画面上のy座標 */ void drawGoal(float sx, float sy) { if (flagNum > 0) return; fill(250, 200, 150, ca); stroke(250, 150, 200, ca); vertex(sx, sy, 0); vertex(sx + mazeWallSize, sy, 0); vertex(sx + mazeWallSize, sy + mazeWallSize, 0); vertex(sx, sy + mazeWallSize, 0); } /** * 岩を書く * @param x スクロール画面上のブロックのx座標 * @param y スクロール画面上のブロックのx座標 * @param ox ブロック座標からの補正xオフセット * @param oy ブロック座標からの補正yオフセット */ void drawRock(float x, float y, float ox, float oy) { float sx = x + ox, sy = y + oy; sy -= 3; stroke(250, 50, 100, ca); fill(200, 100, 50, ca); vertex(sx - 8, sy - 8, 0.2f); vertex(sx + 8, sy - 8, 0.2f); vertex(sx + 8, sy + 8, 0.2f); vertex(sx - 8, sy + 8, 0.2f); fill(240, 200, 100, ca); sx += 5; sy += 8; vertex(sx - 8, sy - 8, 0.1f); vertex(sx + 8, sy - 8, 0.1f); vertex(sx + 8, sy + 8, 0.1f); vertex(sx - 8, sy + 8, 0.1f); fill(200, 220, 120, ca); sx -= 9; sy -= 4; vertex(sx - 8, sy - 8, 0); vertex(sx + 8, sy - 8, 0); vertex(sx + 8, sy + 8, 0); vertex(sx - 8, sy + 8, 0); } /** * 旗を書く * @param x スクロール画面上のx座標 * @param y スクロール画面上のy座標 */ void drawFlag(float x, float y) { float sx = x + mazeWallSize / 3, sy = y + mazeWallSize / 3; fill(250, 250, 100, ca); stroke(250, 100, 50, ca); vertex(sx, sy, WALL_height); vertex(sx + 3, sy, WALL_height); vertex(sx + 3, sy + mazeWallSize / 3 * 2, 0); vertex(sx, sy + mazeWallSize / 3 * 2, 0); vertex(sx + 3, sy, WALL_height); vertex(sx + 13, sy, WALL_height); vertex(sx + 13, sy + 10, WALL_height / 3 * 2); vertex(sx + 3, sy + 10, WALL_height / 3 * 2); } /** * レーダー画面上の迷路と旗と自車を表示 */ void drawMazeOnRadar() { int a = rcnt * 32 - 1; push(); translate(0, 0, -WALL_height * 2 + (8 - rcnt) * 20); noStroke(); fill(255, 220, 180, a); for (int y = 0; y < MAZE_SIZE; y++) for (int x = 0; x < MAZE_SIZE; x++) if (maze[x][y] == 1) rect( x * mazeRectSize, y * mazeRectSize, mazeRectSize, mazeRectSize); else if (maze[x][y] == 3) { fill(200, 200, 50, a); rect( x * mazeRectSize, y * mazeRectSize, mazeRectSize, mazeRectSize); fill(255, 220, 180, a); } fill(100, 200, 255, a); rect(wx * mazeRectSize, wy * mazeRectSize, mazeRectSize, mazeRectSize); pop(); } /** * スコアと残機と残りタイムとメッセージを表示 */ void drawStatus() { push(); translate(0, 0, 30); textSize(26); textMode(ALIGN_RIGHT); fill(0, 0, 0); text((int) score, width - 18, 36); if (time > -999) { textMode(ALIGN_CENTER); textSize(33); text((time + 29) / 30, width / 2, height - 20); } for (int i = 0; i < left; i++) drawCar(i * 20 + 30, 30, PI, PI); if (mcnt > 0) { mcnt--; textMode(ALIGN_CENTER); textSize(24); fill(0, 0, 0); text(msg, width / 2, height / 3); } pop(); } /** * スモーク状のパーティクル */ class Particle { /** * 表示サイズ */ float SIZE = 20; /** * 位置 */ float x, y; /** * 広がる方向および速度 */ float deg, speed; /** * アルファ値 */ int alpha; /** * 消えるまでのカウンタ */ int cnt; /** * カウンタを-1に設定して非表示に */ Particle() { cnt = -1; } /** * cntを設定して有効にする * @param x 初期x座標 * @param y 初期y座標 * @param d 方向 * @param s スピード */ void set(float x, float y, float d, float s) { this.x = x; this.y = y; deg = d; speed = s; alpha = 200 + (int) random(50); cnt = 15 + (int) random(15); } /** * 1フレーム毎の処理 */ void loop() { x += sin(deg) * speed; y += cos(deg) * speed; speed *= 0.95f; alpha *= 0.9f; cnt--; fill(200, 190, 120, alpha); ellipse(x, y, SIZE, SIZE); } } /** * パーティクルプールから次のインスタンスを指し示すためのインデックス */ int particleIdx = 0; /** * パーティクル追加 * @param x 初期x座標 * @param y 初期y座標 * @param d 方向 * @param s スピード */ void addParticle(float x, float y, float d, float s) { particleIdx--; if (particleIdx < 0) particleIdx += particle.length; particle[particleIdx].set(x, y, d, s); } /** * 岩(当たると痛い) */ class Rock { /** * 迷路ブロック内のオフセット */ float ox, oy; /** * どの迷路ブロックに配置されているか */ float x, y; } }