主頁 | 自己紹介 | 日記 | 農業 | 台所 | 電算機 | | 本棚 | | Git

Xlibで遊んでみる2

前回: Xlibで遊んでみる1

言語はC言語である。ソースコードはここにある。

FPSの固定

前のフレームからの経過時間を計測して1.0/FPSを越えるまで待機させる。このときにnanosleep()を使うとなぜか上手くいかなかった。ナノ秒単位で処理できそうな名前なのに使えない。多分OSのコンテクストスイッチがどうとかいう話やと思う。知らんけど。組み込みとかで使うんかな?

とりあえずwhileループの中でひたすら時刻を読んでいる。リソースの無駄遣いではないのだろうか:

#defin FPS 60

int
main(void)
{
	long t0, t1, dt;
	int fps_count;

	clock_gettime(CLOCK_MONOTONIC, &ts);
	t0 = ts.tv_nsec;

	while (!quit) {
		// fix fps
		dt = 0;
		while (dt < 1.0 * 1000 * 1000 * 1000 / FPS){
			clock_gettime(CLOCK_MONOTONIC, &ts);
			t1 = ts.tv_nsec;
			dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000;
		}
		// count fps.
		fps_count++;
		if (t1 < t0){
			printf("fps: %u\n", fps_count);
			fps_count = 0;
		}
		clock_gettime(CLOCK_MONOTONIC, &ts);
		t0 = ts.tv_nsec;
	}
}

時刻はclock_gettime()で測定して1秒未満の部分: tv_nsecだけを利用している。tv_nsecはナノ秒ナノで、109を掛けている。dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000で前回の時刻と現在の時刻の少数部分を比較している。繰り上がりがあれば前回の時刻よりも現在の時刻の方が小さくなるので1秒足すことで調整している。

FPSの計測の部分は、フレーム毎にfps_countを1ずつ増やし、ナノ秒が繰り上がった時点でのfps_countを表示している。

あまり正確な方法ではないように思うが、コンパクトにまとまったのではないだろうか。

キーボード入力の処理

キーボードからの入力を受け取る:

XSelectInput(display, window,
    ExposureMask|KeyPressMask|KeyReleaseMask);

ここではキーボードのキーを押した時と離した時にXEventの通知を受け取るように設定した。

XNextEvent()からひとつずつ入力を受け取ると、複数のキーが同時に押された時にうまく処理できなかったので、押されているキーを配列に保存しておくことにした:

enum Keys {
	Key_D,
	Key_S,
	Key_A,
	Key_W,
	Key_Space,
	Num_Key, //number of keys in this enum
};
enum Key_State {
	Key_Up,
	Key_Down,
};

int key_state[Num_Key];

入力の処理はhandle_inputs()関数内で行なう。ASDWのうちどれかのキーが押されているとそれぞれ左、下、右、上方向に速度を加算するようにした。また、Qが押されるか、windowが破壊されるとquitフラグを1にしてメインループから抜けるようにしている:

int   quit;

void
handle_inputs(void)
{
       XEvent event;
       while (XPending(display) > 0) {
               XNextEvent(display, &event);
               switch (event.type) {
               case KeyPress: {
                       switch (XLookupKeysym(&event.xkey, 0)) {
                       case 'q':
                               quit = 1;
                               break;
                       case 'd':
                               key_state[Key_D] = Key_Down;
                               break;
                       case 'a':
                               key_state[Key_A] = Key_Down;
                               break;
                       case 'w':
                               key_state[Key_W] = Key_Down;
                               break;
                       case 's':
                               key_state[Key_S] = Key_Down;
                               break;
                       default:
                               break;
                       }
               } break;
               case KeyRelease: {
                       switch (XLookupKeysym(&event.xkey, 0)) {
                       case 'd':
                               key_state[Key_D] = Key_Up;
                               break;
                       case 'a':
                               key_state[Key_A] = Key_Up;
                               break;
                       case 'w':
                               key_state[Key_W] = Key_Up;
                               break;

                       case 's':
                               key_state[Key_S] = Key_Up;
                               break;
                       default:
                               break;
                       }
               } break;
               case ClientMessage: {
                       if ((Atom) event.xclient.data.l[0] == wm_delete_window) {
                               quit = 1;
                       }
               } break;
               default:
                       break;
               }
       }

       vx = vy = 0;
       if (key_state[Key_D] == Key_Down)
               vx += 300;
       if (key_state[Key_A] == Key_Down)
               vx += -300;
       if (key_state[Key_S] == Key_Down)
               vy += 300;
       if (key_state[Key_W] == Key_Down)
               vy += -300;
}

入力によって変更された速度は、main()関数内で次の座標を計算するために使用される:

float px = 200, py = 200;
float vx = 0, vy = 0;
int   width = 40, height = 40;

int
main(void)
{
	/* ... */
	quit = 0;
	while (!quit) {
		handle_input()
		/* ... */
		px = px + vx * dt / 1000 / 1000 / 1000;
		py = py + vy * dt / 1000 / 1000 / 1000;
		// bind within the window
		if (px < 0)
			px = 0;
		if (win_width < px + width)
			px = win_width - width;
		if (py < 0)
			py = 0;
		if (win_height < py + height)
			py = win_height - height;

		XClearArea(display, window,
		    0, 0,                  // position
		    win_width, win_height, // width and height
		    False);
		XFillRectangle(display, window, gc,
		    px, py,    // position
		    width, height);   // width and height
	}
	/* ... */
}

完成品

ソースコード

色を変えてみた。

参考

次の記事: Xlibで遊んでみる3