超初心者向けFXGAゲーム制作日誌
第五日以前を読む

PC−FXのトップページに戻る
「PC−FXGAとは?」を見る
「「GMAKER」とは?」を見る
「「んーにゅー」とは?」を見る

第六日「BG表示、そして再びスプライトの巻」

さて、眠り込んで体力を回復した徹夜君は再びBG表示の修羅場へと立ち返ったのであった。まずは前日発見した「libm.txt」を読んでみる。ここにはいくつかの関数の説明があるが、とりあえず「KING(HuC6272のニックネーム)」から始まる一連の関数群を見ていこう。
さてさて、分かったような分からないような。徹夜君はやむなくダメもとでプログラムを打ってみることにした。今度は改造ではなく一応自分でプログラムを作ることになる。
まず、プログラムを打つに当たってやらねばならないのは「フローチャート」という奴である。要するに動作の流れを書いた手順書みたいなもので、プログラムの設計図とも呼ばれる。単にBG面を表示するだけとは言え、レッキとしたプログラムである。大まかな流れを考えておこう。まず徹夜君は漠然とこう考えた。
開始→画像読み込み→画像表示→終了
「なんだ、これだけか」と言いたいところだが、どんなプログラムでも初めは大まかで、こんな感じらしい。ところで実際に各サンプルを見れば分かるが、プログラムの初めに「初期化」と呼ばれる一連の作業を必ず行っている。それに気がついた徹夜君は一応、
開始→各初期化→画像読み込み→画像表示→終了
と書き加えておいた。各初期化作業は各サンプルにあるのでそれをそのまんまコピーして使うことにする。画像読み込み作業は前日目をつけた「READBG()」をそのまんま拝借する。画像表示も「KING〜」グループの関数をあれこれ並べれば何とかなるだろ、てな感じである(どこが自作プログラムなんじゃ)。 さて、ここで徹夜君はハタと気がついた。そう、肝心のBG用画像ファイルが無いのである。せっかくフルカラーも扱えるBG表示なんだから、いい絵を使いたいものである。
「そうだ!AVIファイルからかっぱらおう!」
突然何を言い出すかと思うだろうが、これは附属のコンバーターツール「MPCONV2」を使ってやろうと言う魂胆なのである。「MPCONV2」は主にAVIファイルをFX用MIXファイルに変換するのに使われるのだが、良く見ると動画の1コマをBMPファイルに変換することも可能になっているのである。AVIファイルは雑誌の付録CD−ROMなんかには結構入っており、大概フルカラーになっている。徹夜君はそこに目を付けた訳だ。
さっそく徹夜君は適当に雑誌の付録から映画の紹介ムービーを拝借し、その一コマを「MPCONV2」でBMPファイルに変換して保存した。個人的に好きなC・イーストウッドの顔が映っていたので「east.bmp」と名づけて保存する。一応エディターで確認すると256×256サイズの1677万色。
当然ながらBMPファイルのままではFXGAで使用できない。これをさらにコンバートするわけだが、これはbinディレクトリ内のB2K.EXEというツールがやってくれる。これの説明はdocディレクトリ内にあるので、良く読んでおこう。とりあえずここでは、
>b2k -16m east.bmp
とコマンドを打ち込む。これで256×256サイズの1678万色の画像ファイル「east.bin」が生成される。
さて、材料も揃った事だし、実際にプログラムを作ってみよう。 徹夜君はまず単純にこう打ってみた。
void main(){
KINGInit(); /*Huc6272を初期化*/
KINGCls(KING_BG0); /*BG0面を消去。*/
ReadBG("east.bin"); /*画像データ読み込み*/
    KINGScreen(0);        /*画面を16M色に指定*/
   KINGCylinder(KING_BG0, 1);  /*良くわかんないけど使う*/
   KINGChangeSize0(DISP_NORMAL);/*画面サイズを256×256に*/
   KINGDispOn(); /*BG画像を表示*/
      }
多くの方はお分かりだろうが、これで動くわけはない。徹夜君もなんとなく分かってはいたのだが、コンパイラにかけたら見事に「あれが無い、これが無い」「文法間違い」と大量のエラーメッセージが画面を埋め尽くしてしまった。ここは無理せず「BULLY OFF」のBG処理部分をコピーしてしまう方が良かろう、というわけで次のようになった。
void main(){
DevInit();/*デバイス全体を初期化*/
init_nt(); /*HuC6261を初期化*/
KINGInit();
KINGCls(KING_BG0);
dispoff(bg0);/*N鉄により画面消去*/
ReadBG("dokyusei.bin"); /* 背景データの読み込み */
/*以下、背景データの表示 */
KINGScreen(0);
KINGCylinder(KING_BG0, 1);
KINGChangeSize0(DISP_NORMAL);
 KINGDispOn();
dispon(bg0); /*N鉄により画面表示*/
}
こうやってみてようやく徹夜君は理解した。そう、結局画像を最終的に表示しているのは「N鉄(=HuC6261)」なのである。「KING」の方はあくまでBG面処理を「担当」しているに過ぎない。
では「N鉄」関係を書き込んだところで完成かというと、んなわきゃない。第一、ここに並んでる関数の処理がこのままではどこにも書かれていない。この中で使っているReadBG()は「BULLY OFF」の「fileio.c」に書かれているのでそれを拝借して付け加えよう。なお、実際にReadBG()を見ると分かるが、readerr()とopenerr()という関数が内部で使われているので、これもプログラムに書いておく必要がある。
また、C言語には付き物のインクルードするファイルの指定も冒頭でしておかねばならない。これも「BULLY OFF」の各ソースコードの冒頭をそのまんまコピーしてしまおう。もちろん不必要なものはカットしていいが、どれが必要かというのは初めのうちはなかなか分からない。とりあえず「KING〜」関係は「libm.h」、「N鉄」関係は「v93.h」「nt.h」、ファイル関係は「pio.h」といったヘッダファイルがあるとだけ言っておこう。足りないものがあったらコンパイル時に「こら、この関数の処理はどこに書いてあんねん」と英語でイチャモンをつけてくるので分かる。まあ徹夜君も苦労した所なので、自作する人もせいぜい苦労してもらいたい(笑)。
問題は関数だけでは無い。関数中で使用している定数(たとえばBG_SIZEとかね)をちゃんと宣言しておかねばならない。「BULLY OFF」では「#define BG_SIZE(262144L)」などと宣言されている。「なんだ?この262144って数字は…?」首をかしげた徹夜君だったが、良く考えればわかりますね。そー、画像のサイズである。ファイルマネージャー(Win95ならエクスプローラー)等で確認すれば分かるが「BULLY OFF」で使用している各背景画像ファイル「〜.bin」はきっちり262144バイトなのである。それでは、と「east.bin」のサイズを調べると、122880バイトであった。つまりBG_SIZEをこの数字に変えておけば良いわけだ。
ゴチャゴチャ書いたが、要するに全ソースコードはこうなった。ちょっと長いがそのまま引用しちゃおう。
//BG表示実験用プログラム「bg.c」//
#include <libm.h>
#include <system.h>
#include <v93.h>
#include <nt.h>
#include <pio.h>
#include "vardecl.h" 
/*上のこれ、「BULLY OFF」のいろんな定義をしてるファイルで背景関係も含まれている。面倒なのでそのまま持ってきてる*/
/*以下はおまじないみたいなものと思ってそのまま引っ張ってきた*/
#ifndef LOCAL
#define LOCAL
#endif
#define BG_SIZE (122880L)
/*以下、各関数の定義*/
void ReadBG(char *fname);
  void openerr(char *fname);
void readerr(char *fname);
void main(void);
unsigned short filereadbuf[122880 / 2];
void main(){
DevInit(); /*全デバイス初期化*/
init_nt();  /*N鉄初期化*/
KINGInit(); /*KING初期化*/
KINGCls(KING_BG0); /*BG面消去*/
dispoff(bg0); /*画像非表示*/
ReadBG("east.bin"); /* 背景データの読み込み */
/* 以下、背景データの表示 */
KINGScreen(0); /*1678万色指定*/
KINGCylinder(KING_BG0, 1); /*茶筒モード指定*/
KINGChangeSize0(DISP_NORMAL); /*画面サイズを256×256に*/
KINGDispOn(); /*KINGの内容を表示*/
dispon(bg0); /*それを受けてN鉄が画像表示*/
}
/*以下、ファイル読み込み関係*/
void
ReadBG(char *fname)
{
int fh;  pio_putstr("BG data loading ...\n");
if ((fh = pio_fopen(fname, "rb")) < 0) {
openerr(fname); /*画像ファイルのオープンに失敗*/
}
/* 一時バッファに読み込む */
if (pio_fread(fh, BG_SIZE, filereadbuf) < BG_SIZE) {
readerr(fname); /*ファイル内容読み込みに失敗*/
}
/* ファイルのクローズ */
pio_fclose(fh);
pio_putstr("done.\n"); /*終わったことをパソコン画面で伝える*/
KINGDispOff();
irq_disable();
KINGTrans(KING_BG0, filereadbuf, 2);
irq_enable();
}
/*ファイル読み込みエラー*/
LOCAL volatile void
readerr(char *fname)
{
pio_putstr("Fail ");
pio_putstr(fname);
pio_putstr(" read.\n");
_exit(); /*あーあ。失敗した、って訳で終了*/
}
/*ファイルオープンエラー*/
LOCAL volatile void
openerr(char *fname)
{
pio_putstr("Fail ");
pio_putstr(fname);
pio_putstr(" open.\n");
_exit(); /*あーあ。失敗した、って訳で終了*/
}
どこで何をしてるのか分かるよう、ゴチャゴチャとコメントを書き込んでいるが、はっきり言ってこれはやり過ぎである。マネしないように。
さてコードを書いたらいきなりコンパイルしても良いのであるが、ここでサンプルの改造時を思い出そう。あれではバッチファイルで一挙に実行ファイルを作っていた。徹夜君は「fxcc」だの「fxlk」だのといったややこしい作業が苦手なので「どうせなら簡単に済ませたい」とバッチファイルを作ってみることにした。
「ASL」サンプルに含まれていた「mk.bat」などをのぞき込んでこれを書き換え「bg.bat」とでも名づけてみよう。
// bg.bat //
fxcc -c -Ia:\fxga\include bg.c
fxlk @linkbg
詳しくはfxcc.txtやfxlk.txtでも見てくれれば良いが、「-I」以下はインクルードするヘッダファイル等の場所を示しており「@linkbg」はリンクするファイルをいっしょくたに書いたインダイレクトファイルのお名前である。
当然ながらこの「linkbg」なるファイルを作らなきゃならない。これも面倒だからサンプルにあった「linkfile」なんかを改造しよう。
// linkbg //
-v -o bg
/zseg+vlib_zero+farl_zero+asl_zero:100,,7e00
a:/fxga/lib/_startup.o
a:/fxga/lib/_stack.o
bg.o
a:/fxga/lib/libm.a
a:/fxga/lib/common.a
a:/fxga/lib/vpsglib.a
a:/fxga/lib/libasl.a
a:/fxga/lib/libfarl.a
a:/fxga/lib/libcmn73.a
a:/fxga/lib/piolib.a
a:/fxga/lib/vlib.a
最初のほうの「zseg」とかよく意味の分からぬ部分もあり、はっきり言って無駄そうなものもあるように見受けるが、とにかくこのまんまやってしまう。ダメでもともと。ちなみに_startup.oと_stack.oは絶対に必要である。
さて、材料がひととおりそろったところでこれらを同じディレクトリに詰め込んで、
>bg
と打ち込む。すると「bg.bat」が実行され、コンパイラ「fxcc.exe」が「bg.c」の解析・翻訳を始める。しばらくしてリンカ「fxlk.exe」が起動し、うまくいけば「bg.ex」なる実行ファイルが完成するはずだ。
「おおっ!出来たぞ!」
徹夜君は喜びの声を上げた!どうやら「bg.ex」が生成されたようだ。さっそくデバッガで実行してみよう。
>fxdb ybg;rg
例によってガタガタ…っと文字列が表示される。TVに初期画面が出たところでRUN押し。パソコンのDOS画面に画像データを読み込んでる旨が伝えられ、「done」と出て終了。すると!
「おおっ!イーストウッドが!」
シワクチャのC・イーストウッドのご尊顔がフルカラーでTV画面に表示された!成功だ!
「おー、やっぱフルカラー画像は美しかー」 また出身地不明の言葉をつぶやきつつ、徹夜君は例によって力尽き、深い眠りに入ったのでありました。

・・・以下、作者のつぶやき。
「ぐわああああ、ただのBG表示実験の記事のはずが、えれぇ長くなっちまった…これじゃFXGAプログラミング奮戦記そのまんまじゃないか…ってもともとそうか。まぁ実際にはこんなもんじゃすまない凄まじい悪戦苦闘があったのだが、退屈なだけだろうからカットしている。」
「あとちょっと補足。掲載プログラムを試すのは勝手だけど画像は自分で作れよ!それと「bg.c」の画像表示部分はwhile(1){}なんかで囲って繰り返し動作にした方が無難かも。何か画面が一瞬表示されて消えたことがあるので。うまくいくこともあるんだけど。」
「さらに補足。BG表示のもっと簡単なプログラムは「んーにゅー」に載っている。基本的には同じだけど少し手間が省けている。」
「…次回予告にあった「再びスプライト」はどうなったって?ハハハハハ…(汗)今回が長過ぎたんで次回へ先送りだ!」
と、いうわけで次回予告 「簡易スプライトに挑む!キャラを動かせるか?の巻」
第七日「簡易スプライトに挑む!キャラを動かせるか?の巻」
さて、スプライト表示もこなしBG表示も成功させ、
「もはや我が前に敵はない。ワッハッハッハ!」
と勝手に得意満面になった徹夜君は、
「では、いよいよゲーム製作の前哨戦!スプライトをパッドで操ることにチャレンジしよう!」
と再びスプライトに挑んで行くのであった。
さて、FXGAにおけるスプライト表示はASLというライブラリを使うのが最も簡単である。このASLという奴に関してはdocディレクトリ内の「C6273_s1.wri」「C6273_s2.wri」さらには「libasl.txt」という文書に詳細が書かれている。「C6273」という名前でお分かりのようにFXGAのみに搭載されているチップがこのスプライト表示を担当しているので、FX本体と同じスプライト機能である訳ではない。また説明にある通り、学習用を念頭に作られたためFXGAのスプライト能力を最大限に引き出すわけではない。もっとも初心者にはかえって好都合というものだ。
徹夜君はとりあえず「C6273_s〜」の文書を読んでみた。半分以上意味不明であったが、ともかく以下のような事を理解した。
◎スプライト画像データはいったん「テクスチャバッファ」に格納される(前にスプライトのサンプルいじりでやってましたね)
◎その「テクスチャバッファ」から画像情報が「ディスプレイバッファ」に書き込まれ、スプライトが描画される。
◎その際、テクスチャバッファにデータをどう渡すかを「SCT=Sprite Control Table」なるものが決定している。言ってみれば「スプライト命令文」みたいな物らしい。こいつ自身がテクスチャバッファ上に置かれている。
まあ、こんな所である。最低限この程度の認識がないと、いくら簡単と言っても「ASL」を扱うのは難しい。で、大事なのは「SCT」なるテーブルである。「ASL」というのはこのSCTを簡単に作ってくれるライブラリだと思ってもらいたい。効率のいいプログラムを打ちたければSCTを直接いじった方が良いらしいが、いきなり徹夜君にンなことができるわきゃない。
ではここで以前にも使用したASLのサンプルの一つ、「spzoom.c」の中身を覗いてみよう。そのまんま引用するのも何なので(何か著作権も気になるし)、どこが何やってるのか「libasl.txt」を参考にコメントをつけてみる。
static char SccsId[] = "@(#)spzoom.c 1.6 10/2/95";
/*
* Copyright (c) KUBOTA Corporation and HUDSON SOFT,
* 1994, 1995 All Rights Reserved
*
* This software comprises unpublished confidential information of
* KUBOTA Corp. and HUDSON SOFT. It may not be used, copied or made
* available to anyone, except in accordance with the license under
* which it is furnished.←分かるだろうけど、以上は著作権明記ね。
*/
#define ASL_USE_MACRO /*ASLの各関数をマクロ化する。出来るだけそうしろと書いてある。*/
#include <farl.h> /*ASLはFARLというライブラリも使用してるのでインクルードする*/
#include <asl.h> /*当然のインクルードですね*/
void
main()
{
ASLSct sct;
ASLSource src;
int i, scale;
FX73init(); /*HuC6273初期化*/
FX73_cmap_i5c4(); /*カラーマップ*/
FarlInit(); /*FARL関係初期化*/
/*以下、構造体で定義されている諸メンバー。後文参照*/
    src.bank = 0;/*スプライトのデータが置かれてるテクスチャバッファ番号*/
src.x_pos = 0;/*スプライトパターンの左上頂点X座標*/
src.y_pos = 0;/*同Y座標*/
src.x_size = 256;/*スプライトパターンのX方向サイズ*/
src.y_size = 256;/*同Y方向サイズ*/
src.x_hot = 128;/*スプライトパターンにいろいろやらせる時の中心X座標*/
src.y_hot = 128;/*同Y座標。共に左上頂点からの相対座標*/
ASLSetSpriteWindow( 0, 0, 255, 239 );/*スプライトを表示する範囲を設定。*/
for (;;) {
for ( i = 0; i < 720; i++ ) {
    scale = (int)( ( ( i < 360 ) ? i :
( 720 - i ) ) * 128 / 360 );
if ( scale < 3 ) scale = 3;/*ここらはズームする値を決めている。*/
ASLMakeZoomSprite( &src, 128, 120, scale, &sct );/*拡大縮小させるSCT生成*/
ASLSetSpriteTable( 1, 0, 1, &sct );/*生成されたSCTをテクスチャバッファに書く*/
ASLExecuteSprite( 1, 0, 1 );/*SCTを実行する*/
ASLSync();/*スプライト描画動作が終わるのを待つ*/
FarlSwapWindow();/*スワップさせる*/
ASLWaitForSwap();/*スワップ終了を待つ*/
}
}
}
ここではとりあえず「spzoom.c」を例にしたが、他のサンプルも驚くほど良く似た構造をしている。この例の真ん中、for文辺りを除いた部分はほぼ同じである。ここで「ASLMakeZoomSprite」となってる所が各サンプルでそれぞれ違っているが、要するにここでどういうスプライト描画を行うか決定するSCTを生成してるって訳だ。
このプログラムの後半ではまずASLMakeZoomSprite( &src, 128, 120, scale, &sct );という関数でSCTを生成する。「libasl.txt」に詳しいが、この()内は「「&src(ポインタと言う奴。わかんない人はC言語入門書を読もう!って言いながら作者もいまだに良く分からない)」の位置にあるスプライトパターンを、ディスプレイバッファ(TV画面と読み変えちゃってもかまわない)の(128,120)の座標に、scaleで示す拡大率で描画することになっていて、「&SCT」で示すテクスチャバッファの位置に書き込まれるSCTですけん」ということを示している(分かったような分からないような)。
SCTを生成したら次はASLSetSpriteTable(1,0,1,&sct)なる関数でそのSCTを実際にテクスチャバッファに書き込む。()内は(書き込むテクスチャバッファのバンク(テクスチャバッファ内を区切ったブロックのこと。256×256サイズ)番号,実行する先頭SCTのバンク内番号,書き込むSCTの数,SCTの格納されるバッファへのポインタ)という内容を示している。この場合「テクスチャバッファ・バンク1、バンク内0番に、1つのSCTを書き込む。場所は&SCTで参照するよん」という意味である。
そして次のASLExecuteSprite( 1, 0, 1 );は実際にスプライト表示を実行する。()内は(バンク番号、先頭バンク内番号、実行するSCTの数)となっており、この場合「バンク1のバンク内0番から始まるSCTを、1つだけ実行しまっせ」と言ってるワケ。
その下は画面表示関係で、まあいつもそのまんま使ってる、という程度の理解でかまわない。
ところで順序が逆のようでもあるが、「src.〜」という一連のメンバー(構造体を構成する変数…分からん人はしつこいようだがC言語入門を読め)がやはり気になるところ。これに関しては文章よりも絵でイメージを理解してもらおう。テクスチャバッファ内にスプライトデータがこんな格好で格納されているとイメージできる。
で、他のプログラムではこれがどうなっているか見てみると、例えば「sprot.c」では以下のようになっている。これもイメージ図を掲示しよう。
テクスチャバッファイメージ src.bank = 0;
src.x_pos = 192;
src.y_pos = 128;
src.x_size = 64;
src.y_size = 64;
src.x_hot = 32;
src.y_hot = 32;
お分かりだろうか。「sprot.c」は「spzoom.c」と同じスプライトパターンを使用しながら、その一部だけを使用している訳だ。以前サンプルを試したとき、全体像が出たり一部が出たりしたカラクリがここにある。
「ほーおー、結構簡単じゃん」
徹夜君が思わずそう言っちゃったのには一応理由がある。徹夜君は以前98用簡易ゲーム作成言語(ま、実名挙げても構わんでしょ。アスキーで出してた「マルチゲームScripter」って奴です。なかなかの優れ物でした)を少しばかりいじったことがあったのだが、これで98(当然スプライト機能なんぞない)上でスプライトもどきを表現するカラクリがこれとソックリなのだ。おかげでスンナリ理解することが出来たのだった。
「じゃあ、とりあえず単純にスプライトを表示するだけのプログラムを作ってみるぜ!」
徹夜君がすぐ思い立ったのも道理で、これ実に簡単なのである。前掲の「spzoom.c」のズーム関係の部分を削除して、ASLMakeSimpleSprite()という関数を導入する。この関数の()内は(スプライトパターンの位置を示すポインタ,描画する位置のX座標,そのY座標,SCTのポインタ)という構成になっている。徹夜君はとにかく単純にテクスチャバッファに格納されたスプライトパターンを、そのまんま画面に表示するプログラムを打ってみた。以下のようになる。
//TANJUN.C//
#include <farl.h>
#include <asl.h>
void
main()
{   ASLSct sct;
ASLSource   src;
FX73init();
FX73_cmap_i5c4();
FarlInit();
    src.bank = 0;
src.x_pos = 0;
src.y_pos = 0;
src.x_size = 256;
src.y_size = 256;
src.x_hot = 0;
src.y_hot = 0;
ASLSetSpriteWindow( 0, 0, 255, 239 );
ASLMakeSimpleSprite( &src, 0, 0, &sct );
ASLSetSpriteTable( 1, 0, 1, &sct );
ASLExecuteSprite( 1, 0, 1 );
ASLSync();
FarlSwapWindow();
ASLWaitForSwap();
}
とまあ、こんな具合である。さっそくコンパイル・リンクして実行ファイルを作ってしまおう。前にやったようにaslディレクトリ内の「mk.bat」を使うのが手軽である。
>mk tanjun
これでミスさえなければ、あっという間に「tanjun.ex」が完成する。
「ではでは、さっそくデバッガで…あっ!画像をテクスチャバッファに入れとかなきゃならないんだっけ」 そう、さすがに徹夜君も気がついた。このプログラムではスプライトデータをテクスチャバッファに格納する部分が一切ない。これはサンプル同様に「texload.ex」などを利用してスプライトデータをテクスチャバッファにロードするわけだ。まずは「asl」ディレクトリ内にある「texload.ex」を実行してサンプルのスプライトデータを覗いてみよう。
>fxdb ytexload;rg
スプライトデータのロードが終了して所で、強制終了し、改めて
>fxdb ytanjun;rg
と実行する。やがておなじみの初期画面が。さっそくRUNを押すと・・・
「へっ?」
一瞬にして画面は真っ暗に。何も起こった様子はない。
「ど、どーゆーこっちゃねん!」
慌てる徹夜君であったが、C言語の上でミスがあったとは思えない(だいいち、ミスがあったら実行ファイルが出来てる訳がない)。もう一回試してみるが結果は同じ。
「もうっいやっ!FXGAなんてだいっきらい!もう、あたし寝ちゃう!」
とうとう出身地ばかりか性別まで不明になってきた徹夜君は、そのままふて寝しちゃうのでありました。

・・・以下、またしても作者のつぶやき。
「えー、分かる方は最後何が起こったかお分かりですね。分かんない方は次回掲載までの宿題ね。考えて置きましょう。何?お前は分かってんのかって?ハッハッハ、当然じゃないか。昔これで三日も悩んだんだぞぉ」
「・・・最近、次回予告と内容に齟齬がある?うーん。最近内容が濃くて文章が長いもんで。え?そうじゃなくてC言語コード載せてるからだろって?そういやそうかな(笑)」
では当てにならない次回予告「簡易スプライトに挑む!キャラを動かせるか?の巻・後編」
第八日「簡易スプライトに挑戦!キャラを動かせるか?(中編・・・あれ後編じゃなかったか?)」
さて、目覚めた徹夜君は前日のトラブルの原因を探るべく、「TANJUN.C」を読み直した。
「待てよ…何か足りないような…」
さっそく他の「ASL」サンプルのコードを当たってみる。いくつか見ていくうちに、徹夜君もようやく気が付いた。
「ああっ、どれもfor文使って繰り返し処理やってやがる!」
そうなのだ。どのプログラムも必ず「for(;;){}」という部分がある。ここには永久に繰り返される処理が書かれている訳だが、その中身は、というと
SCTの生成→SCTのテクスチャバッファへの書き込み→SCTの実行→画面上の処理
と、なっている。つまり、これら一連の処理がグルグルと繰り返され、その結果、画面にスプライトが表示されているらしい。前日の徹夜君のプログラムでは繰り返し処理が行われていないため、画面表示処理は一度しか実行されなかったわけ。単純に考えるとTV画面は1秒間に30フレームを表示しているから、徹夜君が指定したスプライト表示は30分の1秒だけ行われたようである。目に見えるわけが無い。
「それでは、肝心の部分をfor文で繰り返しにしますか」
その結果、以下のようになる。
//TANJUN.C//
#define ASL_USE_MACRO
#include <farl.h>
#include <asl.h>
void
main()
{
ASLSct sct;
ASLSource src;
FX73init();
FX73_cmap_i5c4();
FarlInit();
src.bank = 0;
src.x_pos = 0;
src.y_pos = 0;
src.x_size = 256;
src.y_size = 256;
src.x_hot = 0;
src.y_hot = 0;
ASLSetSpriteWindow( 0, 0, 255, 239 );
for(;;){
ASLMakeSimpleSprite( &src, 0, 0, &sct );
ASLSetSpriteTable( 1, 0, 1, &sct );
ASLExecuteSprite( 1, 0, 1 );
ASLSync();
FarlSwapWindow();
ASLWaitForSwap();
}
}
さっそくコンパイルしてリンクして、「tanjun.ex」を再製造する。出来上がったらさっそく実行だ。前日同様、「texload.ex」を先に実行してテクスチャバッファにデータを読み込んだ後、
>fxdb ytanjun;rg
初期画面でrunを押すと…出ました!ASLサンプルで使われていたスプライト画像の全景がそのまま表示される。全く動かないから、どの部分が各サンプルで使われていたのか、一目瞭然だ。画面全体が64×64の絵に分割されており(つまり16個の絵がある)、切手、パイナップル、汽車、なぜか禁煙マークなど、バラエティに富んでいる。それにしてもどこから持ってきた画像なんだろうか。
さて、こうして見ることでHuC6273のスプライト機能の仕組みが漠然とではあるが、分かってくる。
「では理解してるかどうか、実験してみよう」
徹夜君は次に「画像データの一部分を単純にTVのど真ん中に表示するプログラム」作りに取り掛かった。徹夜君の理解によれば、前掲のプログラムの「src〜」の部分を書き換えると画像のどの部分をスプライトにするか決定され、ASLMakeSimpleSprite()の中身を書き換えると表示位置が変化する。全部表示するのもそろそろ億劫なので書き換えた部分のみを紹介しよう。
src.bank = 0;
src.x_pos = 128;/*元の画像データのどこを使うか左上座標を指定*/
src.y_pos = 192;
src.x_size = 64;/*使うスプライトの縦横サイズを指定*/
src.y_size = 64;
src.x_hot = 0;/*特殊処理の際の基準となる座標。ここでは使わない*/
src.y_hot = 0;
ASLMakeSimpleSprite( &src, 128, 128, &sct );
/*画像のどの位置に表示するのか座標を指定*/
こうした変更を前のプログラムに加えて、コンパイルし出来たファイルを実行してみる。うまく行けば画像データ全体のうち右下方にあった汽車の絵だけが暗闇の中に表示されるはずだ。
「あれ、真ん中になっとらんぞ」
TV画面が256×240と判断して(128,128)あたりが真ん中だろうと判断して指定したのだが、汽車の絵はやや右下方にずれこんだ。良く考えれば当然で、あくまで指定したスプライト画像の左上が(128,128)になるだけの話なのである。
とまあ、このように元のプログラムの「src.〜」とASLMakeSimpleSprite()をいじれば、表示する画像と表示位置をあれこれ操作できる。いろいろやってみるうちに仕掛けがどんどん分かってくるはずだ。
そんなイロイロを試していた徹夜君であるが、さすがにバージョンアップをしてみたくなる。
「スプライトを同時に二つ以上出すことだって、当然できるだろ?」
そりゃそうである。出来なきゃゲームなんぞ出来るわけが無い。どうすりゃ良いか、というと早い話がSCTを二つ以上作っちまえばいいわけである。単純に表示するだけなら「src.〜」のメンバーどもとASLMakeSimpleSprite()を二種類以上作ってSCTを複数生成し、テクスチャバッファに書き込む時と実行する時に、その個数をちゃんと指定してあげれば良いのである。論ずるより書くが易し。以下にそのプログラムを掲げよう。
//FUKUSU.C//
#define ASL_USE_MACRO
#include <farl.h>
#include <asl.h>
void
main()
{
ASLSct kisya,donguri;
ASLSource src;
FX73init();
FX73_cmap_i5c4();
FarlInit();
ASLSetSpriteWindow( 0, 0, 255, 239 );
for(;;){
src.bank = 0;
src.x_pos = 128;
src.y_pos = 192;
src.x_size = 64;
src.y_size = 64;
src.x_hot = 0;
src.y_hot = 0;
ASLMakeSimpleSprite( &src, 128, 128, &kisya );/*作ったSCTを「汽車」と命名*/
src.bank = 0;
src.x_pos = 0;
src.y_pos = 64;
src.x_size = 64;
src.y_size = 64;
src.x_hot = 0;
src.y_hot = 0;
ASLMakeSimpleSprite( &src, 50, 50, &donguri );/*SCTを「どんぐり」と命名*/
ASLSetSpriteTable( 1, 0, 2, &kisya);/*実行する先頭のSCT名だけを指定してます*/
ASLExecuteSprite( 1, 0, 2);
ASLSync();
FarlSwapWindow();
ASLWaitForSwap();
}
}
ここでまた詳細を説明するのもいい加減うっとうしいので省くが、「src.〜」とASLMakeSprite()を数字を変えて二回書き、SCTを二つ作っていることだけは理解してもらいたい。そしてASLSetSpriteTable()とASLExecuteSprite()内のSCT数の数字を「2」に変えていることも見逃してはいけない。aslのサンプルにある「texload.ex」を実行して画像データを読み込んだ後、このプログラムを実行すると、画面内二個所に「どんぐり」と「汽車」の絵が表示されるはずだ。
このプログラムの内容が理解できれば、もっとたくさんのスプライト表示も可能だ。ここで徹夜君はゲーム画面のお約束、「同じ画像の画面内敷き詰め」に挑戦することにしてみた。
RPGなんかのフィールド画面で気づいた人も多いだろう。あれ、一枚のデカい絵で描いてませんよね。よーく見ると同じようなブロックの組み合わせで出来ている。「RPGツクール」なんかやったことのある人はおなじみだと思う。この基本がこれから徹夜君がやろうとしている事である。
こうした処理をおこなう際、絶対に理解しておかなければならないのが「配列」という考え方だ。詳しくはC言語入門を、といつもの台詞になってしまうが、少し理解し易いように順を追ったプログラムにしてみよう。
まず横一列に同じ画像を敷き詰めるプログラムを紹介しよう。
//ITIRETU.C//
#define ASL_USE_MACRO
#include <farl.h>
#include <asl.h>
void
main()
{
int x;/*繰り返し処理で使う変数を定義*/
ASLSct sct[4];/*sct[]という配列を指定*/
ASLSource src;
FX73init();
FX73_cmap_i5c4();
FarlInit();
ASLSetSpriteWindow( 0, 0, 255, 239 );
for(;;){
src.bank = 0;
src.x_pos = 0;
src.y_pos = 0;
src.x_size = 64;
src.y_size = 64;
src.x_hot = 0;
src.y_hot = 0;
for(x=0;x<4;x++){
ASLMakeSimpleSprite( &src, x*64, 0, &sct[x] );
}/*xに1を足しつつ4回同じ処理を行う*/
ASLSetSpriteTable( 1, 0, 4, &sct[0] );/*先頭はsct[0]である*/
ASLExecuteSprite( 1, 0, 4 );
ASLSync();
FarlSwapWindow();
ASLWaitForSwap();
}
}
てな感じである。ここでは「src.〜」メンバーは一度しか書かず、ASLMakeSimpleSprite()を「x」という変数を変えつつ4回繰り返すことで「同じ画像で表示位置の違うSCT」を4つ生成している。例によって「texload.ex」で画像データを読み込んでからこのプログラムを実行すると、画面上部に同じ画像が4つ、一列に並んで表示されるはずだ。
C言語初心者が(徹夜君もそうなわけだが)一番理解に時間がかかりそうなのが、ここに登場する「sct[]」という配列変数だろう。詳しい説明はC言語入門書に任せるが、ここではどうしても多くなってしまうSCTの表記を簡単にすますテクニックという程度に考えてもらいたい。
上のプログラムが理解できたら、今度は画面全体の敷き詰めに入ろう。上の一列の処理を縦方向に何回か繰り返せば良い訳で、sct[][]という「二次元配列」という奴を使う。
//SIKITUME.C//
#define ASL_USE_MACRO
#include <farl.h>
#include <asl.h>
void
main()
{
int x;
int y;/*繰り返し処理で使う変数を定義*/
ASLSct sct[4][4];/*sct[][]という二次元配列を指定*/
ASLSource src;
FX73init();
FX73_cmap_i5c4();
FarlInit();
ASLSetSpriteWindow( 0, 0, 255, 239 );
for(;;){
src.bank = 0;
src.x_pos = 0;
src.y_pos = 0;
src.x_size = 64;
src.y_size = 64;
src.x_hot = 0;
src.y_hot = 0;
for(x=0;x<4;x++){
for(y=0;y<4;y++){
  ASLMakeSimpleSprite( &src, x*64, y*64, &sct[x][y] );
}
}
ASLSetSpriteTable( 1, 0, 16, &sct[0][0] );/*先頭はsct[0][0]である*/
ASLExecuteSprite( 1, 0, 16 );
ASLSync();
FarlSwapWindow();
ASLWaitForSwap();
}
}
先ほどの一列表示の場合とどこが違うか、よーく見比べて欲しい。今回は二重に繰り返し処理が行われている。よくわかんない人は実際に目で記述を追って(x、yに1を足しつつね)理解するようにして欲しい。コンパイルに成功後に実行してみると、北京辺りにある建物がTV画面にぎっしり敷き詰められるはずだ。
以上のプログラムを何とかこなしてきた徹夜君は、けっこうFXGAのスプライト表示にも慣れてきたようである。
「さぁ〜て、ではいよいよ、スプライトキャラを動かすぞ!ただ単に表示するのはもう卒業じゃ!」
と、徹夜君は勢いこんで新たなプログラムを打ち出そうとした。が、その時である。
「待てよ…そもそもパッド操作って、どうプログラムすりゃいいのだ?」
さぁ大変である。aslのサンプルには当然のごとくパッド操作など入っていない。と、いうことは?パッドを使っているサンプルというと…
「ぐああああああ!また「BULLY OFF」を見なきゃならんのかいな!」
以前BG表示の際の悪夢が思い起こされる。
「…もう寝るべ、寝るべ」
明日やれることは今日はやらない、がモットーの徹夜君はあっさりと寝てしまうのでありました。

次回予告「簡易スプライトに挑戦!キャラを動かせるか?(後編)」