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