www.mtkn.jp

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

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 &lt; 1.0 * 1000 * 1000 * 1000 / FPS){
     38 			clock_gettime(CLOCK_MONOTONIC, &ts);
     39 			t1 = ts.tv_nsec;
     40 			dt = t1 &gt; t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000;
     41 		}
     42 		// count fps.
     43 		fps_count++;
     44 		if (t1 &lt; 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) &gt; 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 &lt; 0)
    196 			px = 0;
    197 		if (win_width &lt; px + width)
    198 			px = win_width - width;
    199 		if (py &lt; 0)
    200 			py = 0;
    201 		if (win_height &lt; 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>