www.mtkn.jp

Manuscripts for my personal webpage.
git clone https://git.mtkn.jp/www.mtkn.jp
Log | Files | Refs | LICENSE

xlib_playground2.html (7147B)


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