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