www.mtkn.jp

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

xlib_playground2.html (8025B)


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