某BBSで「UNIX上のX Window System(Xlib)でゲーム作りたい」という話題があった。興味があったので過去の知識からベースになるプログラムを書いてみた。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/poll.h>
#include <sys/times.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#define EVENT (ExposureMask|KeyPressMask|KeyReleaseMask)
typedef struct {
Display *d;
int s;
Window w;
GC g;
XImage *osb;
Bool alive;
unsigned char vram[640 * 480];
unsigned char clut[256];
} AC;
static unsigned char sp1[] = {
0,0,0,7,7,0,0,0,
0,0,0,7,7,0,0,0,
0,0,0,7,7,0,0,0,
2,0,0,5,5,0,0,2,
7,0,7,5,5,7,0,7,
7,0,7,7,7,7,0,7,
7,7,7,7,7,7,7,7,
7,7,7,0,0,7,7,7
};
Bool InitApp( AC *ac, char *srv, char *title )
{
ac->d = XOpenDisplay( srv );
{
Colormap c;
XColor p, t;
c = DefaultColormap( ac->d, 0 );
XAllocNamedColor( ac->d, c, "black" , &p, &t );
ac->clut[0] = p.pixel;
XAllocNamedColor( ac->d, c, "blue" , &p, &t );
ac->clut[1] = p.pixel;
XAllocNamedColor( ac->d, c, "red" , &p, &t );
ac->clut[2] = p.pixel;
XAllocNamedColor( ac->d, c, "magenta", &p, &t );
ac->clut[3] = p.pixel;
XAllocNamedColor( ac->d, c, "green" , &p, &t );
ac->clut[4] = p.pixel;
XAllocNamedColor( ac->d, c, "cyan" , &p, &t );
ac->clut[5] = p.pixel;
XAllocNamedColor( ac->d, c, "yellow" , &p, &t );
ac->clut[6] = p.pixel;
XAllocNamedColor( ac->d, c, "white" , &p, &t );
ac->clut[7] = p.pixel;
}
ac->w = XCreateSimpleWindow( ac->d, DefaultRootWindow(ac->d)
, 0, 0, 320, 240, 0
, ac->clut[7], ac->clut[0]
);
ac->g = XCreateGC( ac->d, DefaultRootWindow(ac->d), 0, 0 );
ac->osb = XCreateImage( ac->d
, DefaultVisual(ac->d,DefaultScreen(ac->d))
, 8, ZPixmap
, 0, ac->vram
, 640, 480, 8, 0
);
XSelectInput( ac->d, ac->w, EVENT );
XStoreName( ac->d, ac->w, title );
XMapWindow( ac->d, ac->w );
XFlush( ac->d );
ac->alive = True;
return True;
}
void CleanUpApp( AC *ac )
{
XUnmapWindow( ac->d, ac->w );
XDestroyWindow( ac->d, ac->w );
XFreeGC( ac->d, ac->g );
XCloseDisplay( ac->d );
}
void pset( AC *ac, int x, int y, int c )
{
XPutPixel( ac->osb, x, y, ac->clut[c] );
}
void sp_put( AC *ac, int x, int y, unsigned char *s )
{
int i;
unsigned char *b;
b = &ac->vram[ac->osb->bytes_per_line * y + x];
for ( i = 0; i < 64; i++ ) {
if ( i % 8 == 0 ) {
b += (ac->osb->bytes_per_line - 8);
}
if ( *s ) {
*b = ac->clut[*s];
}
b++;
s++;
}
}
void Regulation( void )
{
#if 1
static clock_t pre;
clock_t now;
clock_t diff;
now = times(NULL);
diff = now - pre;
pre = now;
if ( diff >= 2 ) {
return;
}
poll( NULL, 0, (2 - diff) * 10 );
#endif
}
void refresh( AC *ac )
{
XPutImage( ac->d, ac->w, ac->g, ac->osb, 0, 0, 0, 0, 320, 240 );
XFlush( ac->d );
}
typedef struct {
int x;
unsigned char y;
unsigned char c;
} star;
int main( void )
{
static AC ac;
Bool left = False;
Bool right = False;
star s[64];
int i;
for ( i = 0; i < 64; i++ ) {
s[i].x = rand() % 320;
s[i].y = rand();
s[i].c = (rand() % 6) + 1;
}
InitApp( &ac, NULL, "X-Game" );
while ( ac.alive ) {
XEvent e;
if ( XCheckMaskEvent(ac.d,EVENT,&e) ) {
switch ( e.type ) {
case Expose:
refresh( &ac );
break;
case KeyPress:
switch ( XKeycodeToKeysym(ac.d,e.xkey.keycode,0) ) {
case XK_Left:
left = True;
break;
case XK_Right:
right = True;
break;
}
break;
case KeyRelease:
switch ( XKeycodeToKeysym(ac.d,e.xkey.keycode,0) ) {
case XK_Escape:
ac.alive = False;
break;
case XK_Left:
left = False;
break;
case XK_Right:
right = False;
break;
}
break;
}
} else {
static int x = 160;
if ( left != right ) {
if ( left && x > 8 ) {
x--;
}
if ( right && x < 320 ) {
x++;
}
}
memset( ac.vram, 0, 640 * 480 );
for ( i = 0; i < 64; i ++ ) {
pset( &ac, s[i].x, s[i].y++, s[i].c );
}
sp_put( &ac, x, 230, sp1 );
refresh( &ac );
Regulation();
}
}
CleanUpApp( &ac );
return EXIT_SUCCESS;
}
|
基本的にはWindowsのWinGを使ったゲーム作成と同じである。
関数Regulation()はゲーム速度を一定に保つためのもの。で、時間調整にusleep()の代わりにpoll()を使っているが、これは何となくpoll()の方が精度が良い気がしたからである。usleep()だと背景スクロールがガタガタした感じになった。
ちなみにusleep()はBSD由来の関数で、SYSV系には存在しない事が多かった。そんな時にpoll()を使うのは常套手段だった。
あと、このプログラムはLinuxで開発したのだが、WindowsのGetTickCount()に相当する関数はtimes()しかないようだ。これは分解能が1/100秒なので16ミリ秒でタイミングをとろうにもムリな感じである。仕方ないので、このプログラムでは20ミリ秒でタイミングを取っている。
そりゃ、gettimeofday()でミリ秒単位の時間が取得できるが、timeval構造体のtv_usecメンバはすぐに桁溢れして0に戻ってしまう。使い物にならない。一度XMAMEのオートフレームスキップ辺りのコードでも読んでみるか…。案外/dev/rtcとか直アクセスしてたりして。
後は色関係の所を説明すべきだろうが、面倒なので省略。昔雑記にWindowsでのパレットの扱いについて書いたが、その時に少し触れているので参照すべし(しても役に立たないと思うが)。変数名のclutはColor Look-Up Tableだ。PlayStationのプログラマなら身近な呼び方だろう。古来からの呼び方なら「パレット」か。paletteと書くよりタイプ量が少ないので良い。
その他注意が必要なのは、CURSESライブラリをリンクするとrefresh()がコンフリクトするくらいかな(笑)。
昔の雑記にも書いたかもしれないが、ウィンドウシステムのパレットカラー周りはグチャグチャしていて扱い辛い。って事でWindowsなプログラムでは主にハイカラー(15ビット色)を使うようになった(ちなみにローカルパレットを持っているのでパレットアニメーションも可能な予定)。
X Window Systemでは、この辺りの知識を仕入れた当時はパレットカラーなサーバしか手元に存在しなかった。で、ハイカラーやトゥルーカラーでのプログラミング方法を知らない。昔(4〜5年前だっけか?)NetscapeNevigatorのUNIX版がこれらのモードで実行するとcore吐いて死ぬという報告を読んだ事があるが、上記のプログラムも同じ運命かもしれぬ…。