"mt_xor1.c"の解説
2004/01/22
ベイサイド
このドキュメントは、"mt_xor1.c"の説明だけをするものです。最初に読むべきドキュ
メントではありません。最初に読むべきドキュメントは"document.txt"です。"mt_xor0
.c"との違いは32ビットグラフィックボックスを使うことです。
1.アルゴリズム
今まで説明してきた描画方法は点を打つとか線を引くというものでした。しかし実際
のプログラムでは、もっと込み入ったことがやりたいというのはよくあることです。そ
の場合、あたかもビデオメモリにアクセスするように自由に読み書きできれば、なにか
と便利でしょう。・・・ここでは、そういうアクセス方法を説明します。
このアクセス方法は2つの手順で構成されます。まず、指定されたバッファ内に自由に
読み書きします。このバッファはグラフィックボックスに対応しており、4バイトが1ド
ットになっています(パックドピクセル)。そして一通りのアクセスが済んだらいじった
部分をflushします。flushというのは、メモリ内のバッファの内容を画面に確実に反映
させる方法だと理解してください。この操作をしなくてもOS側の都合でバッファの内容
が画面に反映されることはありますが、それは保証されていないことですのであてには
できません。いじったら必ずflushしてください(リードアクセスしかしていないときは
flushしなくてもよい)。
グラフィックボックスのバッファ構造は、(int *) gbox + 16から後ろのxsize * ysi
ze * 4がそっくりそのままバッファになっています。単純明快です。詳しいことはソー
スをご覧ください。
このプログラムはその方法でたくさんの点を描画して、ちょっとした「山」を描きま
す。
2.ライブラリ関数の説明
引数の型については、guigui00.hを参照してください。
lib_flushgraphbox(opt, win, x, y, sx, sy, skip, p) :
グラフィックボックスをflushします。optは0x8024にしてください。winはグラフィッ
クボックスが所属するウィンドウです。残りのパラメーターが少々ややこしいです。ま
ずグラフィックボックス全体をflushするなら、
x, y : グラフィックボックスオープン時に指定したx_pos, y_posの値
sx, sy : グラフィックボックスオープン時に指定したx_size, y_sizeの値
skip : 必ず0
p : (int *) gbox + 16
という風に指定します。しかしいじった範囲がグラフィックボックスのごく一部である
という場合もよくあるでしょう。そんなときこのような全範囲flushをすると処理時間に
おいてかなりの無駄になります。そんな時は部分flushを使います。
x, y : 部分flushしたい範囲の左上の座標(window内の座標系で指定・・・gboxの
座標ではない)
sx, sy : 部分flushしたい範囲のサイズ
skip : (グラフィックボックスオープン時に指定したx_size - この時のsx) * 4
p : x, yで指定した座標に相当するドットを指し示すintポインタ
これをよくご覧になれば分かるように、結局は全範囲flushも部分flushも同じルールで
す。
もしたとえば画面内でキャラクターが走っているとすれば、まずはバッファにアクセ
スして、元いた場所を背景に戻して移動先にキャラクターを書き込むでしょう。そして
flushすることになります。この場合、移動距離がある程度あれば、flushは2回やるべき
です。つまり移動元の部分と移動先の部分です。移動元と移動先を含むような広範囲のf
lushを1回やることでももちろん代用できますが、flushはOSにとって高負荷な処理です
。できるだけ狭い範囲に分解してやりましょう。・・・この時以下の2つの方法のどちら
がいいかと迷われるかもしれません。
1.移動元書き換え → 移動元flush → 移動先書き換え → 移動先flush
2.移動元書き換え → 移動先書き換え → 移動元flush → 移動先flush
もちろんどちらの手順でも問題なく実行できますが、どちらかといえば2.の方がおす
すめです。というのは、結局画面に反映されるのはflushの時なので、2.の方法の方が
キャラクターが画面上から消えている時間が短くて済むせいです。もし複数のキャラク
ターが介在しているのなら、まずはバッファ内の書き換えを集中させ、その後にflushを
集中的にやる方が良いでしょう。
3.改造への指針
このバッファへのアクセスとflushを使えば、かなり自由にグラフィック処理ができる
はずです。しかしflushの際にはVRAMへアクセスすることになり、これは結構重たい処理
です。広い範囲を細かい時間間隔でflushするのは今のところやらない方がいいでしょう
。そういう見栄えのするゲームは、OSASKがグラフィックアクセラレーターを使いこなせ
るようになるまではちょっと無理かもしれません。・・・それでもマシンパワーのある
マシンなら、なんとかゲームにはなるかもしれません。
// "mt_xor1.c"
// stack:4k malloc:6k
// copyright(C) 2004 ベイサイド
#include <guigui00.h>
#define AUTO_MALLOC 0
/* ちょっとしたマクロ */
#define pixel(x, y) *(p + (y) * 100 + (x))
/* 基本的なアルゴリズム */
/* 1.グラフィックボックスをオープンする */
/* 2.(int *) graphicbox + 16以降のバイトは、x_size * y_size * 4のcharのバッファになっているので
そこを好きなようにいじる(リードしてもライトしてもいい) */
/* 3.いじり終ったら、バッファをflushしなければいけない。この時点で画面に反映される */
void OsaskMain()
{
struct LIB_WINDOW *window;
struct LIB_TEXTBOX *wintitle;
struct LIB_GRAPHBOX *graphicbox;
int i, j;
int *p;
lib_init(AUTO_MALLOC);
window = lib_openwindow(AUTO_MALLOC, 0x0200, 136, 48);
wintitle = lib_opentextbox(0x1000, AUTO_MALLOC, 0, 7, 1, 0, 0, window, 0x00c0, 0);
lib_putstring_ASCII(0x0000, 0, 0, wintitle, 0, 0, "Mt.Xor0");
/* グラフィックボックスをウィンドウに用意する */
graphicbox = lib_opengraphbox(1, AUTO_MALLOC, 4, 8, 100, 48, 18, 0, window);
/* パラメーターの意味は以下の通り:
opt : 1(固定)
mode : 4(固定)
mode_opt : 初期の背景色(バッファはこのバイトで初期化される)
x_size : グラフィックボックスの大きさ(ドット単位)
y_size : グラフィックボックスの大きさ(ドット単位)
x_pos : グラフィックボックスの位置(ドット単位)
y_pos : グラフィックボックスの位置(ドット単位)
*/
p = (int *) graphicbox + 16;
/* 頂点に一つ点を打つ */
pixel(49, 0) = 0; /* 黒い点 */
/* ちょっとした演算 */
for (j = 0; j < 47; j++) {
for (i = 1; i < 99; i++)
pixel(i, j + 1) = ((pixel(i - 1, j) == 0) ^ (pixel(i + 1, j) == 0)) ? 0 : 0xC6C6C6;
}
j = 0;
for (;;) {
/* バッファの内容を確実に画面に反映させる */
lib_flushgraphbox(0x8024, window, 18, 0, 100, 48, 0, p);
/* パラメーターの意味は以下の通り:
opt : 0x8001(固定)
win : グラフィックボックスが所属するウィンドウ
x : グラフィックボックスのx_pos
y : グラフィックボックスのy_pos
sx : グラフィックボックスのx_size
sy : グラフィックボックスのy_size
skip : 0(固定)
p : (int *) graphbox + 16
なお、上記の例はグラフィックボックス全体をflushしている
*/
lib_waitsignaltime(0x0007, 0, 0, 0x80000000, 0, 0); /* 500msec.-wait */
/* 色変更 */
j = (j + 1) & 0x868686;
for (i = 0; i < 48 * 100; i++) {
if (p[i] != 0xC6C6C6)
p[i] = j;
}
}
}
修正した説明(案)
- 説明がt_xor0と重複しすぎていて、読み手が飛ばし読みをする恐れがあったので、違いのみをピックアップして書き直しました。
"mt_xor1.c"の解説
2004/01/24
川合秀実(kawai@osask.jp)
このドキュメントは、"mt_xor1.c"の説明だけをするものです。最初に読むべきドキュ
メントではありません。最初に読むべきドキュメントは"document.txt"です。
1.アルゴリズム
このmt_xor1は、mt_xor0の改造版に相当します。違いは8ビットグラフィックボックス
ではなく32ビットグラフィックボックスを使うことです。mt_xor0を理解してから読んで
ください。
グラフィックボックスのバッファ構造は、LIB_GBOX_BUF32(gbox)から後ろのxsize *
ysize * 4がそっくりそのままバッファになっています。詳しいことはソースをご覧く
ださい。1ドットが4バイトで構成され、
bit 0- 7:青成分(0-255)
bit 8-15:緑成分(0-255)
bit16-23:赤成分(0-255)
bit24-31:リザーブで必ず0
となっています。8bitグラフィックボックスと同様に、自由に読み書きして、必要に応
じてflushしてください。
2.ライブラリ関数の説明
引数の型については、guigui00.hを参照してください。
lib_flushgraphbox(opt, win, x, y, sx, sy, skip, p) :
グラフィックボックスをflushします。optは0x8004か0x8024にしてください。winは
グラフィックボックスが所属するウィンドウです。残りのパラメーターが少々ややこし
いです。まずグラフィックボックス全体をflushするなら、
x, y : グラフィックボックスオープン時に指定したx_pos, y_posの値
sx, sy : グラフィックボックスオープン時に指定したx_size, y_sizeの値
skip : 必ず0
p : LIB_GBOX_BUF32(gox)
という風に指定します。
32ビットグラフィックボックスで部分フラッシュをすることもできます。その場合は
次のように引数を指定します。
x, y : 部分flushしたい範囲の左上の座標(window内の座標系で指定・・・gboxの
座標ではない)
sx, sy : 部分flushしたい範囲のサイズ
skip : (グラフィックボックスオープン時に指定したx_size - この時のsx) * 4
p : x, yで指定した座標に相当するドットを指し示すポインタ
optの違いについてですが、通常は0x8004を使います。色表現力が犠牲になってもいい
から、速度を速くしてほしいという場合に限って(もしくはすべての画面モードで細い
線がタイリングで点線に化けたりするのが不都合な場合)、0x8024を指定します。
3.改造への指針
これをつかえば8ビットグラフィックボックスでは得られなかった高い表現力が得られ
るでしょう。今のところ(完成度が低いせいで)32ビットグラフィックボックス内でラ
インを引くなどができませんが、それでもバッファに書いてflushすれば、一応どんな絵
でもかけます。ラインなどの基本描画機能のサポートはもうしばらくお待ちください。
// "mt_xor1.c"
// stack:4k malloc:20k
// copyright(C) 2004 Hidemi KAWAI
#include <guigui00.h>
#define AUTO_MALLOC 0
/* ちょっとしたマクロ */
#define pixel(x, y) *(p + (y) * 100 + (x))
#if (!defined(LIB_GBOX_BUF))
#define LIB_GBOX_BUF(gbox) ((void *) ((char *) (gbox) + 64))
#define LIB_GBOX_BUF32(gbox) ((unsigned int *) LIB_GBOX_BUF(gbox))
#endif
void OsaskMain()
{
struct LIB_WINDOW *window;
struct LIB_TEXTBOX *wintitle;
struct LIB_GRAPHBOX *graphicbox;
int i, j;
int *p;
static colortable[8] = {
0x000000, 0x840000, 0x008400, 0x848400, 0x000084, 0x840084, 0x008484, 0x848484
};
lib_init(AUTO_MALLOC);
window = lib_openwindow(AUTO_MALLOC, 0x0200, 136, 48);
wintitle = lib_opentextbox(0x1000, AUTO_MALLOC, 0, 7, 1, 0, 0, window, 0x00c0, 0);
lib_putstring_ASCII(0x0000, 0, 0, wintitle, 0, 0, "Mt.Xor1");
/* グラフィックボックスをウィンドウに用意する */
graphicbox = lib_opengraphbox(1, AUTO_MALLOC, 4, 0xc6c6c6, 100, 48, 18, 0, window);
/* パラメーターの意味は以下の通り:
opt : 1(固定)
mode : 4(固定)
mode_opt : 初期の背景色(バッファはこの値で初期化される)
x_size : グラフィックボックスの大きさ(ドット単位)
y_size : グラフィックボックスの大きさ(ドット単位)
x_pos : グラフィックボックスの位置(ドット単位)
y_pos : グラフィックボックスの位置(ドット単位)
*/
p = LIB_GBOX_BUF32(graphicbox);
/* 頂点に一つ点を打つ */
pixel(49, 0) = 0x000000; /* 黒い点 */
/* ちょっとした演算 */
for (j = 0; j < 47; j++) {
for (i = 1; i < 99; i++)
pixel(i, j + 1) = pixel(i - 1, j) ^ pixel(i + 1, j) ^ 0xc6c6c6;
}
j = 0;
for (;;) {
/* バッファの内容を確実に画面に反映させる */
lib_flushgraphbox(0x8004, window, 18, 0, 100, 48, 0, p);
/* パラメーターの意味は以下の通り:
opt : 0x8004 or 0x8024(固定)
win : グラフィックボックスが所属するウィンドウ
x : グラフィックボックスのx_pos
y : グラフィックボックスのy_pos
sx : グラフィックボックスのx_size
sy : グラフィックボックスのy_size
skip : 0(固定)
p : LIB_GBOX_BUF32(graphbox)
なお、上記の例はグラフィックボックス全体をflushしている
*/
lib_waitsignaltime(0x0007, 0, 0, 0x80000000, 0, 0); /* 500msec.-wait */
/* 色変更 */
j = (j + 1) & 7;
for (i = 0; i < 48 * 100; i++) {
if (p[i] != 0xc6c6c6)
p[i] = colortable[j];
}
}
}