KAEDE Hack blog

kaede0902 技術ブログ お仕事募集中。

JS ゲーム製作 -- p5.js で円の衝突を実装する

why

前回のライブラリがうまく使えなくて困っていたところ

パフォーマンス遅くてもいいなら自分で作ればすぐだよ

と聞いたので作ってみる

参考

www.youtube.com

ヘロンの数学 さんのチャンネル

この動画では円と線分の衝突を扱っているが、円の方が圧倒的にシンプルなのでとりあえずこれをマスターすることにする

円などの名前

  • 円0: c0
  • 円1: c1
  • 円0 の半径: r0
  • 円1 の半径: r1
  • 円 0 と 円 1 の距離: d

接触条件

円 0 と 円 1 の半径を足したものが 2つの円の 距離と同じであれば、その時

二つの円はちょうど接触していることになる。

つまり d が r0 + r1 以下の時になる。計算誤差によりちょうど接触するときは取れないため、未満の条件で考えるので良いらしい。

p5 で実際に出す

ヘロン氏は HTML Canvas を直接かかかず、p5.js を使っている

p5js.jp

公式サイト

github.com

リポジトリ

www.npmjs.com

ちゃんと npm にもあった

しかし CDN でなく、DL だと $25 かかるらしい?

実際に p5 を動かし、 100 x 100 の Canvas に 20, 50, 100, の円を書く

癖で npm i p5 したが、今回は React まで使わないので必要なかった

index.html を書いて

<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>

CDN で読み込む

ファイル名を sketch.js にして

<script src="sketch.js"></script>

読み込んで スケッチを書き始める

function setUp() {
  createCanvas(100, 100);
}
function draw() {
  ellipse(50,50,20,20);
}

f:id:kei_s_lifehack:20210413210846p:plain

100 x 100 の canvas を作って 50,50 から 20,20 の 円を作れた。小さい。

f:id:kei_s_lifehack:20210413210952p:plain

50, 50, のサイズにもできた。少し大きい。

f:id:kei_s_lifehack:20210413211107p:plain

100 x 100 のサイズだとギリギリはみ出す。

Chrome Dev Tool の Mobile S だと 1/4 しか出ないバグ

f:id:kei_s_lifehack:20210413211409p:plain

Dev Tool の 320px の Mobile S の枠でリロードすると、なぜか 1/4 相当しか出ない。

f:id:kei_s_lifehack:20210413211536p:plain

カスタムレイアウトの Mi9 の 343px だと全部でる。なぜだろう


x 100 y100 を超えられない問題 は typo だった

f:id:kei_s_lifehack:20210413212327p:plain

動画のコードでは createCanvas で 領域を 480 x 480 に伸ばしているが

CDN 読み込みで

function setUp() {
  createCanvas(480, 480);
}
function draw() {
  ellipse(100,50,100,100);
}

480 480 の領域まで使えるようにして、円を 50 から 150 の位置まで描けるようにしても、途中で切れてしまう。

DevTool で見てみると Canvas の大きさが 100 から拡張できていない。

原因は setup() 関数を setUp() と書き続けていたからだった。

キャメルケースじゃないんかい!!!!!!!!!!

f:id:kei_s_lifehack:20210413220146p:plain

解決!100 x 100 の壁を超えられた!!


二種類の重なっている円を描く

動画のコードをほぼそのまま使って円を描く

function setup() {
  createCanvas(900, 900);
}

function draw() {
  background(124,124,124);

  let x0 = 120;
  let y0 = 180;
  let r0 = 60;

  let x1 = 240;
  let y1 = 150;
  let r1 = 120;

  noFill();
  strokeWeight(3);

  circle(x0, y0, 2*r0)
  circle(x1, y1, 2*r1)
}

この時に backgroud を setup の方に書くと重なりまくって大変なことになる。

f:id:kei_s_lifehack:20210413225437p:plain

2円の中心からの距離を測る

  let d = sqrt( (x1-x0)**2 + (y1-y0)**2 );
  let distanceText = `Distance: ${d}`
  console.log(distanceText);

  strokeWeight(1);
  stroke(0);
  textSize(32);
  text(distanceText, 3, 420);

二点間の距離 D は

円 0 の x 座標と 円 1 の x 座標 の差分 の二乗

円 0 の y 座標と 円 1 の y 座標 の差分 の二乗

を足して、ルートにすれば出る

f:id:kei_s_lifehack:20210413230337p:plain

これで距離が出た。現在の円の中心同士の距離は 120 のようだ。

円1 の中心をマウスカーソルの位置と同期する

非常に難しそうだが、p5.js にはこの機能が組み込まれている

  let x1 = mouseX;
  let y1 = mouseY;

mouseX, mouseY と入れるだけ!!!

f:id:kei_s_lifehack:20210413231952p:plain

f:id:kei_s_lifehack:20210413232005p:plain

円を近づけたり離したりすると、中心からの距離が減ったり増えたりするのがわかる!

距離から衝突判定の処理を作る

先ほどので距離は取れているので

  let isHit = distance < r0 + r1;

を追加すれば OK

あとは表示のコードを書いて

  let isHit = distance < r0 + r1;
  let isHitText = `2 Circles are Hit?: ${isHit}`;
  text(isHitText, 3, 520)

f:id:kei_s_lifehack:20210413233220p:plain

f:id:kei_s_lifehack:20210413233231p:plain

衝突がわかる!!!ゲーム製作の第一歩だ!!!

kaede0902.github.io

動く!!!よし!!

f:id:kei_s_lifehack:20210413233643p:plain

スマフォだとこのサイズ感。

機能確認の目標達成!!!

まとめ

p5.js は

function setUp() {
  createCanvas(480, 480);
}
function draw() {
  ellipse(100,50,100,100);
}

これで Canvas 初期化して円が描ける

  let x0 = 120;
  let y0 = 180;
  let r0 = 60;

  let x1 = mouseX;
  let y1 = mouseY;
  let r1 = 120;

  noFill();
  strokeWeight(3);

  circle(x0, y0, 2*r0)
  circle(x1, y1, 2*r1)

こうすると、片方の円はマウスの座標と同期する。マウスで円を動かせる。

  let distance = sqrt( (x1-x0)**2 + (y1-y0)**2 );
  let isHit = distance < r0 + r1;

この公式で距離を計算し、当たっているかを計算できる

それを全て実装するとこうなる!!!

kaede0902.github.io