JS ゲーム製作 -- p5.js で円の衝突を実装する
why
前回のライブラリがうまく使えなくて困っていたところ
パフォーマンス遅くてもいいなら自分で作ればすぐだよ
と聞いたので作ってみる
参考
ヘロンの数学 さんのチャンネル
この動画では円と線分の衝突を扱っているが、円の方が圧倒的にシンプルなのでとりあえずこれをマスターすることにする
円などの名前
- 円0: c0
- 円1: c1
- 円0 の半径: r0
- 円1 の半径: r1
- 円 0 と 円 1 の距離: d
接触条件
円 0 と 円 1 の半径を足したものが 2つの円の 距離と同じであれば、その時
二つの円はちょうど接触していることになる。
つまり d が r0 + r1 以下の時になる。計算誤差によりちょうど接触するときは取れないため、未満の条件で考えるので良いらしい。
p5 で実際に出す
ヘロン氏は HTML Canvas を直接かかかず、p5.js を使っている
公式サイト
ちゃんと 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); }
100 x 100 の canvas を作って 50,50 から 20,20 の 円を作れた。小さい。
50, 50, のサイズにもできた。少し大きい。
100 x 100 のサイズだとギリギリはみ出す。
Chrome Dev Tool の Mobile S だと 1/4 しか出ないバグ
Dev Tool の 320px の Mobile S の枠でリロードすると、なぜか 1/4 相当しか出ない。
カスタムレイアウトの Mi9 の 343px だと全部でる。なぜだろう
x 100 y100 を超えられない問題 は typo だった
動画のコードでは 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() と書き続けていたからだった。
キャメルケースじゃないんかい!!!!!!!!!!
解決!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 の方に書くと重なりまくって大変なことになる。
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 座標 の差分 の二乗
を足して、ルートにすれば出る
これで距離が出た。現在の円の中心同士の距離は 120 のようだ。
円1 の中心をマウスカーソルの位置と同期する
非常に難しそうだが、p5.js にはこの機能が組み込まれている
let x1 = mouseX; let y1 = mouseY;
mouseX, mouseY と入れるだけ!!!
円を近づけたり離したりすると、中心からの距離が減ったり増えたりするのがわかる!
距離から衝突判定の処理を作る
先ほどので距離は取れているので
let isHit = distance < r0 + r1;
を追加すれば OK
あとは表示のコードを書いて
let isHit = distance < r0 + r1; let isHitText = `2 Circles are Hit?: ${isHit}`; text(isHitText, 3, 520)
衝突がわかる!!!ゲーム製作の第一歩だ!!!
動く!!!よし!!
スマフォだとこのサイズ感。
機能確認の目標達成!!!
まとめ
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;
この公式で距離を計算し、当たっているかを計算できる
それを全て実装するとこうなる!!!