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()
関数内で行なう。A
、S
、D
、W
のうちどれかのキーが押されているとそれぞれ左、下、右、上方向に速度を加算するようにした。また、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