xlib_playground2.html (7144B)
1 <h1>Xlibで遊んでみる2</h1> 2 <time>2022-12-22</time> 3 4 <p>前回: <a href="xlib_playground1.html">Xlibで遊んでみる1</a></p> 5 <p>言語はC言語である。ソースコードは\ 6 <a href="https://git.mtkn.jp/xlib_playground">ここ</a>にある。 7 </p> 8 9 <h2>FPSの固定</h2> 10 <p>前のフレームからの経過時間を計測して<code>1.0/FPS</code>を越えるまで待機させる。\ 11 このときに<code>nanosleep()</code>を使うとなぜか上手くいかなかった。\ 12 ナノ秒単位で処理できそうな名前なのに使えない。\ 13 多分OSのコンテクストスイッチがどうとかいう話やと思う。\ 14 知らんけど。\ 15 組み込みとかで使うんかな? 16 </p> 17 18 <p> 19 とりあえず<code>while</code>ループの中で\ 20 ひたすら時刻を読んでいる。\ 21 リソースの無駄遣いではないのだろうか: 22 </p> 23 <pre><code>#defin FPS 60 24 25 int 26 main(void) 27 { 28 long t0, t1, dt; 29 int fps_count; 30 31 clock_gettime(CLOCK_MONOTONIC, &ts); 32 t0 = ts.tv_nsec; 33 34 while (!quit) { 35 // fix fps 36 dt = 0; 37 while (dt < 1.0 * 1000 * 1000 * 1000 / FPS){ 38 clock_gettime(CLOCK_MONOTONIC, &ts); 39 t1 = ts.tv_nsec; 40 dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000; 41 } 42 // count fps. 43 fps_count++; 44 if (t1 < t0){ 45 printf("fps: %u\n", fps_count); 46 fps_count = 0; 47 } 48 clock_gettime(CLOCK_MONOTONIC, &ts); 49 t0 = ts.tv_nsec; 50 } 51 } 52 </code></pre> 53 <p> 54 時刻は<code>clock_gettime()</code>で測定して1秒未満の部分: <code>tv_nsec</code>\ 55 だけを利用している。<code>tv_nsec</code>はナノ秒ナノで、10<sup>9</sup>を掛けている。\ 56 <code>dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000</code>で前回の\ 57 時刻と現在の時刻の少数部分を比較している。繰り上がりがあれば前回の時刻よりも\ 58 現在の時刻の方が小さくなるので1秒足すことで調整している。\ 59 </p> 60 <p> 61 FPSの計測の部分は、フレーム毎に<code>fps_count</code>を1ずつ増やし、\ 62 ナノ秒が繰り上がった時点での<code>fps_count</code>を表示している。\ 63 </p> 64 <p> 65 あまり正確な方法ではないように思うが、コンパクトにまとまったのではないだろうか。\ 66 </p> 67 68 <h2>キーボード入力の処理</h2> 69 <p>キーボードからの入力を受け取る:</p> 70 <pre><code>XSelectInput(display, window, 71 ExposureMask|KeyPressMask|KeyReleaseMask); 72 </code></pre> 73 <p>ここではキーボードのキーを押した時と離した時に<code>XEvent</code>の通知を\ 74 受け取るように設定した。 75 </p> 76 <p> 77 <code>XNextEvent()</code>からひとつずつ入力を受け取ると、\ 78 複数のキーが同時に押された時にうまく処理できなかったので、\ 79 押されているキーを配列に保存しておくことにした:\ 80 </p> 81 <pre><code>enum Keys { 82 Key_D, 83 Key_S, 84 Key_A, 85 Key_W, 86 Key_Space, 87 Num_Key, //number of keys in this enum 88 }; 89 enum Key_State { 90 Key_Up, 91 Key_Down, 92 }; 93 94 int key_state[Num_Key]; 95 </code></pre> 96 97 <p> 98 入力の処理は<code>handle_inputs()</code>関数内で行なう。\ 99 <code>A</code>、<code>S</code>、<code>D</code>、<code>W</code>\ 100 のうちどれかのキーが押されているとそれぞれ左、下、右、上方向に\ 101 速度を加算するようにした。また、<code>Q</code>が押されるか、\ 102 windowが破壊されると<code>quit</code>フラグを<code>1</code>にして\ 103 メインループから抜けるようにしている:\ 104 </p> 105 <pre><code>int quit; 106 107 void 108 handle_inputs(void) 109 { 110 XEvent event; 111 while (XPending(display) > 0) { 112 XNextEvent(display, &event); 113 switch (event.type) { 114 case KeyPress: { 115 switch (XLookupKeysym(&event.xkey, 0)) { 116 case 'q': 117 quit = 1; 118 break; 119 case 'd': 120 key_state[Key_D] = Key_Down; 121 break; 122 case 'a': 123 key_state[Key_A] = Key_Down; 124 break; 125 case 'w': 126 key_state[Key_W] = Key_Down; 127 break; 128 case 's': 129 key_state[Key_S] = Key_Down; 130 break; 131 default: 132 break; 133 } 134 } break; 135 case KeyRelease: { 136 switch (XLookupKeysym(&event.xkey, 0)) { 137 case 'd': 138 key_state[Key_D] = Key_Up; 139 break; 140 case 'a': 141 key_state[Key_A] = Key_Up; 142 break; 143 case 'w': 144 key_state[Key_W] = Key_Up; 145 break; 146 147 case 's': 148 key_state[Key_S] = Key_Up; 149 break; 150 default: 151 break; 152 } 153 } break; 154 case ClientMessage: { 155 if ((Atom) event.xclient.data.l[0] == wm_delete_window) { 156 quit = 1; 157 } 158 } break; 159 default: 160 break; 161 } 162 } 163 164 vx = vy = 0; 165 if (key_state[Key_D] == Key_Down) 166 vx += 300; 167 if (key_state[Key_A] == Key_Down) 168 vx += -300; 169 if (key_state[Key_S] == Key_Down) 170 vy += 300; 171 if (key_state[Key_W] == Key_Down) 172 vy += -300; 173 } 174 </code></pre> 175 176 <p> 177 入力によって変更された速度は、<code>main()</code>関数内で次の座標を\ 178 計算するために使用される: 179 </p> 180 <pre><code>float px = 200, py = 200; 181 float vx = 0, vy = 0; 182 int width = 40, height = 40; 183 184 int 185 main(void) 186 { 187 /* ... */ 188 quit = 0; 189 while (!quit) { 190 handle_input() 191 /* ... */ 192 px = px + vx * dt / 1000 / 1000 / 1000; 193 py = py + vy * dt / 1000 / 1000 / 1000; 194 // bind within the window 195 if (px < 0) 196 px = 0; 197 if (win_width < px + width) 198 px = win_width - width; 199 if (py < 0) 200 py = 0; 201 if (win_height < py + height) 202 py = win_height - height; 203 204 XClearArea(display, window, 205 0, 0, // position 206 win_width, win_height, // width and height 207 False); 208 XFillRectangle(display, window, gc, 209 px, py, // position 210 width, height); // width and height 211 } 212 /* ... */ 213 } 214 </code></pre> 215 216 <h2>完成品</h2> 217 <a href="https://git.mtkn.jp/xlib_playground/file/ex2/ex2.c.html">ソースコード</a> 218 <p>色を変えてみた。</p> 219 <video controls> 220 <source src="videos/ex2.webm" type="video/webm"> 221 </video> 222 223 <h2>参考</h2> 224 <ul> 225 <li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li> 226 </ul> 227 <p>次の記事: \ 228 <a href="xlib_playground3.html">Xlibで遊んでみる3</a> 229 </p>