xlib_playground4.html (4963B)
1 +++ 2 date = '2023-01-02T00:00:00+09:00' 3 draft = false 4 title = 'Xlibで遊んでみる4' 5 +++ 6 <time>2023-01-02</time> 7 8 <p> 9 前回: <a href="xlib_playground3.html">Xlibで遊んでみる3</a> 10 </p> 11 <p> 12 言語: C言語<br /> 13 ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a> 14 </p> 15 16 <h2>衝突判定とその処理</h2> 17 <p> 18 これまでは一つの四角形だけを描画していたが、今回は複数の四角形を作成して動かしてみた。ランダムな場所にランダムな運動量で動かして、他のものやウィンドウの縁とぶつかったら跳ね返るようにした。</p> 19 <p> 20 回転しない四角形どうしの衝突判定は簡単である。x軸方向とy軸方向の両方に重なりがあれば衝突している: 21 </p> 22 <pre><code>struct square { 23 float ppx, ppy; // previous position 24 float px, py; // current position 25 float vx, vy; // velocity 26 int w, h; // width and height 27 }; 28 29 int 30 test_collision(struct square *s1, struct square* s2) 31 { 32 return s1->px < s2->px + s2->w && s2->px < s1->px + s1->w && 33 s2->py < s1->py + s1->h && s1->py < s2->py + s2->h; 34 } 35 </code></pre> 36 37 <p> 38 衝突後の処理は多少めんどくさかった。衝突した時は既にめりこんでいるので、まずはそれぞれをめりこんだ距離の半分ずつずらして衝突を解消するようにした。この際、x軸方向にぶつかったのか、y軸方向にぶつかったのかで、それぞれの軸方向にひっぺがすようにしている。二つの四角形の各軸に関するめりこんだ距離<code>lapx</code>、<code>lapy</code>と各軸に関する相対速度<code>rel_vx</code>、<code>rel_vy</code>の比を比べればどちらの軸方向にぶつかったかが分かるはずである、多分 : 39 </p> 40 <pre><code>void 41 handle_collision_mm(struct square *s1, struct square *s2) 42 { 43 if (!test_collision(s1, s2)) 44 return; 45 46 float lapx = min(s1->px + s1->w, s2->px + s2->w) - max(s1->px, s2->px); 47 float lapy = min(s1->py + s1->h, s2->py + s2->h) - max(s1->py, s2->py); 48 float rel_vx = max(s1->vx - s2->vx, s2->vx - s1->vx); 49 float rel_vy = max(s1->vy - s2->vy, s2->vy - s1->vy); 50 51 if (lapx / rel_vx < lapy / rel_vy) { 52 if (s1->px + s1->w < s2->px + s2->w / 2) { 53 s1->px -= lapx / 2; 54 s2->px += lapx / 2; 55 } else { 56 s1->px += lapx / 2; 57 s2->px -= lapx / 2; 58 } 59 } else { 60 if (s1->py + s1->h < s2->py + s2->h / 2) { 61 s1->py -= lapy / 2; 62 s2->py += lapy / 2; 63 } else { 64 s1->py += lapy / 2; 65 s2->py -= lapy / 2; 66 } 67 } 68 } 69 </code></pre> 70 <p> 71 衝突は弾性衝突として、衝突したそれぞれの四角形の速度を更新した。質量は四角形の面積として計算している。衝突後の速度はエネルギー保存則と運動量保存則から導いたのでしんどかった。 72 </p> 73 <pre><code>void 74 handle_collision_elastic(struct square *s1, struct square *s2) 75 { 76 if(!test_collision(s1, s2)) 77 return; 78 79 float v1, v2; 80 float m1 = s1->w * s1->h; 81 float m2 = s2->w * s2->h; 82 83 float lapx = min(s1->px + s1->w, s2->px + s2->w) - max(s1->px, s2->px); 84 float lapy = min(s1->py + s1->h, s2->py + s2->h) - max(s1->py, s2->py); 85 86 if (lapx < lapy) { 87 v1 = s1->vx; 88 v2 = s2->vx; 89 s1->vx = 2*m2/(m1+m2)*v2 + (m1-m2)/(m1+m2)*v1; 90 s2->vx = 2*m1/(m1+m2)*v1 + (m2-m1)/(m1+m2)*v2; 91 } else { 92 v1 = s1->vy; 93 v2 = s2->vy; 94 s1->vy = 2*m2/(m1+m2)*v2 + (m1-m2)/(m1+m2)*v1; 95 s2->vy = 2*m1/(m1+m2)*v1 + (m2-m1)/(m1+m2)*v2; 96 } 97 98 handle_collision_mm(s1, s2); 99 } 100 </code></pre> 101 102 <h2>サブティック</h2> 103 <p> 104 この名前が適切かどうか分からないが、前のフレームから次のフレームまでの時間をさらに何等分かして衝突判定の制度を上げた(マクロは括弧でかこって分かりにくいバグを防げとどこかに書いていたのでそうすることにした): 105 </p> 106 <pre><code>#define SUB_TIC (4) 107 108 void 109 game_play(void) 110 { 111 /* ... */ 112 while (next_menu == GAME_PLAY) { 113 /* ... */ 114 for (int j = 0; j < SUB_TICK; j++) { 115 for (int i = 0; i < NUM_SQUARE; i++) 116 next_tick(&square[i], 1000 * 1000 * 1000 / FPS / SUB_TICK); 117 118 for (int i = 0; i < NUM_SQUARE; i++) 119 for (int j = i + 1; j < NUM_SQUARE; j++) { 120 handle_collision_elastic(&square[i], &square[j]); 121 /* ... */ 122 } 123 /* ... */ 124 } 125 /* ... */ 126 } 127 /* ... */ 128 } 129 </code></pre> 130 131 <h2>完成品</h2> 132 <p> 133 <a href="https://git.mtkn.jp/xlib_playground/file/ex4/ex4.c.html">git</a> 134 </p> 135 <p> 136 <video controls> 137 <source src="videos/ex4.webm" type="video/webm"> 138 </video> 139 </p> 140 141 <h2>参考</h2> 142 <ul> 143 <li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li> 144 </ul> 145 <p> 146 次の記事: <a href="xlib_playground5.html">Xlibで遊んでみる5</a> 147 </p> 148