9Bのスプライト機能は、プログラムを書くときに画像を簡単に取り扱えるようにするための仕組みの一つです。
具体的にどんなことをするのかというと、紙に描いた絵をチョキチョキと・・・要するに次の図のような感じです。
幼児相手に手っ取り早く人形劇ごっこをするときなんかに、こんなことをしますね。厚紙に絵を描いて切り抜き、割り箸にでも貼り付けて、劇をやるのに具合よくしているわけです。
9BのSPRITEというコマンドもこれと同じで、紙に描かれた絵を切り抜く、つまり座標で範囲を指定して画像を切り出し、割り箸・・・はさすがにありませんが、その代わりに番号を付けてプログラムで扱いやすくします。
スプライトとは、この切り抜いた絵のこと、またはこのような技術全般のことを言います。
スプライトを使う前にもう一つ知っておきたいことは、9Bで使う3つの画面 − 表画面、裏画面、テクスチャ画面 − についてです。
今度は、劇場をイメージしてください。
少し変わった構造の劇場で、舞台が前後半分ずつに分かれています。間は幕で仕切られているので、お客さんに見えるのは前半分だけです。
表の役者がポーズを決めている間に、舞台の後ろ半分には同じ役の役者がスタンバって次のポーズを作ります。
後ろの役者全員の位置が決まったら、舞台の前半分をさっと一息に消します。さっきまで後ろに隠れていた舞台裏が、一気に表に現れます。
この動作が瞬時に連続して行われると、お客さんの目には役者が移動したりポーズを変えているように見えるという仕掛けです。
さて、すでにお気づきと思いますが、この舞台の前半分(常にお客さんに見える方)が「表画面」、後ろ半分(役者がこっそりスタンバる方)が「裏画面」です。
「テクスチャ画面」は、役者が本番前の準備をする「楽屋」にあたります。
では、「役者」は? ・・・ 役者にあたるのは最初に説明した「スプライト」です。
(ここでは説明しませんが、背景の書き割りにあたるBGというものもあります)。
そして、役者の位置を決める演出家の役目が、プログラマであるあなたになりますね。
もちろん、現実の舞台でこんなことをしていてはとても芝居が成立しませんが、コンピュータではこのような方式で処理すると、高速に描画できたり、プログラムを書く方にとってもプログラムを単純化できる利点があります。
では、実際にコマンドを使ってみましょう。
まず、9B リファレンスのサンプル用画像 9B_REF.BMP のUFOをスプライトにして、画面中央に表示してみます。
※ここから先は 9B リファレンスのサンプル用画像、9B_REF.BMP を使用して説明します。お持ちでない場合は、ここをクリックしてダウンロードしてください。また、このファイルは、F1:\944BASIC\ にコピーしておいてください。
主な手順は次のとおりです。
1. テクスチャ画面に、9B_REF.BMP を読み込む
2. SPRITEコマンドを使って、
a. UFOの部分を切り抜き
b. 裏画面に配置する
3. REFRESHコマンドを使って、裏画面を表画面にする
この手順をプログラムにすると、次のようなコードになります。
※ ここから先のサンプルコードでは、BITMAP コマンドでファイルを絶対パス(F1:〜の形式)で指定しています。9Bのプログラミング作法としては相対パス(ファイル名単独)が望ましいのですが、サンプルとしては絶対パスにしておいた方が確実(クリップボード実行も可)なため、下記のように表記しています。
[サンプルコード 1]
REM 944BASIC 'スプライト サンプル1'
SETPAGE 2
BITMAP 'F1:\944BASIC\9B_REF.BMP'
SPRITE 1,1,1, 112,112, 96,0, 16,16
REFRESH 0,0, 0,0, 240,240
KEYWAIT
END
このコードの主な手順について、説明します。
1. テクスチャ画面に、9B_REF.BMP を読み込む
画像の読み込みや描画処理をテクスチャ画面で行うには、SETPAGE 2 を実行します。するとそこから先の画面描画に関係するコマンド(BITMAP,JPEG,LINE,BOXなど)は、テクスチャ画面で実行されます。(元の表画面で描画するように戻すには、SETPAGE 0を実行します)。
上のコードでは、SETPAGE 2 の次の行にある BITMAP '9B_REF.BMP' で、テクスチャ画面に画像を読み込んでいます。
これで、楽屋に役者たちを呼ぶことができました。
2. SPRITEコマンドを使って、a.UFOの部分を切り抜き、b.裏画面に配置する
SPRITE コマンドには、2つの働きがあります。
1つは楽屋にいる役者を指名(=画像の範囲を指定)して役名(=番号)を割り当てる働き。
もう1つは、役者を舞台裏のどの位置にスタンバイさせるかを指定(=表示位置を座標で指定)する働きです。
それもあってか、SPRITEコマンドのパラメータはいくぶん多めです。
SPRITE 1,1,1, 112,112, 96,0, 16,16
最初の2つの 1,1 は、スプライト番号を指定しています。「役名(=番号)は 1 だ」と楽屋の役者たちに説明しているような具合です。
それに続く3番目の 1 は、表示モードです。1 の場合は表示、0 だと表示を消します。5 を指定すると拡大縮小モードになります(拡大縮小モードについては、ここでは説明しません)。
次の 112,112 は、裏画面の座標位置です。舞台裏で「立ち位置はここね」と役者に指示していることになります。
続く 96,0, 16,16 は、テクスチャ画像のどこから、どの程度切り抜くかを指定する数値です。始めの 96,0 は切り出しを開始する位置(この場合、9B_REF.BMP の UFO のあるあたり)で、続く 16,16 はその開始位置から切り取る範囲 (16x16ドット) です。
この部分は役者の指名にあたります。楽屋をぐるっと見回して、「え〜っと、そこのおまえ。この役ね」と言い渡すような具合です。
--
SPRITEコマンドのパラメータの意味は、おおよそつかんでいただけたでしょうか?
実際のプログラムの動きとしては、このコマンドを実行すると、裏画面の座標 (112,112) に UFO の画像(16x16)を配置したことになります。この場合、スプライト(UFO画像)の左上角が (112,112) に位置します。
3. REFRESHコマンドを使って、裏画面を表画面にする
ここまでの作業で、役者を舞台裏に配置することはできました。しかし、このままではお客さんの目には見えません。舞台裏をさっと表に出す必要があります。
これを行うのが、REFRESH コマンドです。
REFRESH 0,0, 0,0, 240,240
最初の 0,0 は、REFRESH する範囲の開始位置(座標)です。
次の 0,0 は、裏画面の座標位置なのですが、基本的に最初の2つの数字に合わせておけば問題ありません。
最後の 240,240 は、REFRESH する範囲です。
この場合、0,0 位置から 240x240 ドットの範囲を REFRESH することになります。縦型のザウルスで画面全体をREFRESHするなら 240,320 を指定することになりますね。
なお、今回の例では実のところ、スプライトの表示位置 (112,112) から 16x16 ドットだけ、つまりUFOのあるところだけを REFRESH すれば間に合います。その場合は:
REFRESH 112,112, 112,112, 16,16
となります。
REFRESH する範囲は狭い方が処理が高速になるため、速度にシビアなプログラムの場合は、できるだけREFRESHする範囲を無駄なくするように心がけるとよいでしょう。
スプライトで画像を容易に扱えるとなると、ゲーム的なものを作りたくなるのが人情です。まずは、矢印キーを押した方向にキャラクターを動かすプログラムを考えてみましょう。
最初にスプライトを使わずに、文字を使ってプログラムしてみます。
※ここから下のコードでは、矢印キーを押し続けてキャラクタが画面の端を超えると見えなくなります。本来なら画面の端を越えないように処理するのですが、コードをシンプルにするため端折りました。
[サンプルコード 2]
REM 944BASIC 'キャラクタ移動例'
C='☆' :REM キャラクタ
X=112 :REM 初期表示位置 X
Y=112 :REM 初期表示位置 Y
MV=4 :REM 移動ドット数
*LOOP
SYNC
GCURSOR X,Y :REM 現在の表示位置
PRINT ' ' :REM 表示を消す
PX=PADX :REM 横矢印キーの方向
PY=PADY :REM 縦矢印キーの方向
X=X+PX*MV :REM 現在地+方向x移動距離
Y=Y+PY*MV :REM 現在地+方向x移動距離
GCURSOR X,Y :REM 新しい表示位置
PRINT C :REM キャラクタを表示
GOTO *LOOP
ここで行っているのは、
1. 現在位置のキャラを消して、
2. 位置を変え、
3. キャラを再び書く
という処理です。
この処理を繰り返し高速に行うと、人間の目には移動しているように見えるわけです。
では、次にスプライトを使って同じような処理を行ってみましょう。
[サンプルコード 3]
REM 944BASIC 'スプライト サンプル 2'
COLORBG 0,0,0: CLS :REM 画面を黒く
X=112 :REM 初期表示位置 X
Y=112 :REM 初期表示位置 Y
MV=4 :REM 移動ドット数
REM スプライトの定義
SETPAGE 2
BITMAP 'F1:\944BASIC\9B_REF.BMP'
SPRITE 1,1,1, 112,112, 96,0, 16,16
*LOOP
SYNC
PX=PADX :REM 横矢印キーの方向
PY=PADY :REM 縦矢印キーの方向
X=X+PX*MV :REM 現在地+方向x移動距離
Y=Y+PY*MV :REM 現在地+方向x移動距離
REM SPRITEを裏画面に配置
SPRITE 1,1,1, X,Y
REM リフレッシュ(240X240)
REFRESH 0,0,0,0,240,240
GOTO *LOOP
やっていることはほとんど同じですが、キャラクタを消す処理がなくなっています。REFRESHコマンドで以前の画面は自動的に消去されるためです。
*LOOP内のSPRITEコマンドでは、画像の切り出し範囲を指定するパラメータが省略されていることにも注意してください。
画像切り出しの定義(楽屋での役者の指名)は1度だけ行っておけば、後は省略してもかまいません。すでに役者は指名してあるので、立ち位置を指定するだけでよいのです。
さて、今回のプログラムですが、文字だけの表現よりもやはり遅くなります。高速化するには、REFRESHする範囲を狭くするなどの工夫が必要です。
MV=4の数値をもう少し大きくしても、うわべの速度を上げることができます。
次の例は、UFOが落下物にあたらないようによけるだけの代物です。ゲームというよりは、複数のキャラクタを扱う例を示すのが目的のプログラムです。
これもスプライトを使った例(サンプルコード 4)と、文字を使った例(サンプルコード 5)の両方を挙げておきます。ただし、このレベルではスプライトを使用したプログラムは、文字だけで作ったプログラムに速度で負けているようですが・・・。
その反面、スプライトを使ったプログラムでは、キャラクタを消す処理が不要であること、スプライトどうしの衝突を検知する関数が使えることから、*LOOP内のコードがすっきりしています。
また、じゃまキャラのスプライト定義に、FORループを使用しているところに注意してください。9B_REF.BMPの上辺は16ドット間隔で規則正しく模様が配置されているため、いちいち座標を測定せずに、スプライト定義を機械的に行わせることができます。
[サンプルコード 4]
REM 944BASIC 'スプライト サンプル 3'
COLORBG 0,0,0
CLS
X=112 :REM 初期表示位置 X
Y=200 :REM 初期表示位置 Y
MV=6 :REM 移動ドット数(速度)
SETPAGE 2
BITMAP 'F1:\944BASIC\9B_REF.BMP'
REM 自キャラ
SPRITE 1,1,1, 112,112, 96,4, 16,10
REM じゃまキャラ用変数
J=5 :REM じゃまキャラ数
DIM JX(J) :REM じゃまキャラのX座標
DIM JY(J) :REM じゃまキャラのY座標
DIM JV(J) :REM じゃまキャラの速度
REM じゃまキャラのスプライト定義
FOR I=0 TO J
SPRITE 2+I,2+I,0, 0,0, 16*(I%5),0,16,16
JX(I)=RNDRG(16,222) :REM X座標
JY(I)=RNDRG(16,80) :REM Y座標
JV(I)=RNDRG(8,20) :REM 速度
NEXT I
*LOOP
SYNC
PX=PADX :REM 横矢印キーの方向
PY=PADY :REM 縦矢印キーの方向
X=X+PX*MV :REM 現在地+方向x移動距離
Y=Y+PY*MV :REM 現在地+方向x移動距離
REM 自キャラ配置
SPRITE 1,1,1, X,Y
REM じゃまキャラ配置
FOR I=0 TO J
JY(I)=JY(I)+JV(I)
REM 下限を越えたら再初期化
IF JY(I)>240 THEN JX(I)=RNDRG(16,222):JY(I)=16:JV(I)=RNDRG(12,20)
SPRITE 2+I,2+I,1,JX(I),JY(I)
NEXT I
REM リフレッシュ
REFRESH 0,0,0,0,240,240
REM スプライトの衝突判定
IF SPCOLLISION(1,2,2+J)<>-1 THEN GOTO *GAMEOVER
GOTO *LOOP
*GAMEOVER
MESSAGE 'OUCH! ・・・ GAME OVER'
END
[サンプルコード 5]
REM 944BASIC '複数キャラを動かす例'
C='☆' :REM 自キャラ
X=112 :REM 初期表示位置 X
Y=200 :REM 初期表示位置 Y
MV=4 :REM 移動ドット数
REM じゃまキャラ用変数
DIM JC(4)='●','◆','■','▲','★'
DIM JX(4) :REM じゃまキャラのX座標
DIM JY(4) :REM じゃまキャラのY座標
DIM JV(4) :REM じゃまキャラの速度
FOR I=0 TO 4
JX(I)=RNDRG(16,222)
JY(I)=RNDRG(16,80)
JV(I)=RNDRG(8,16)
NEXT I
*LOOP
SYNC
PX=PADX :REM 横矢印キーの方向
PY=PADY :REM 縦矢印キーの方向
GCURSOR X,Y
PRINT ' '
X=X+PX*MV :REM 現在地+方向x移動距離
Y=Y+PY*MV :REM 現在地+方向x移動距離
GCURSOR X,Y
PRINT C
REM じゃまキャラ表示
FOR I=0 TO 4
REM 前の座標のキャラを消す
GCURSOR JX(I),JY(I)
PRINT ' '
REM 下に着いたら再初期化
IF JY(I)>240 THEN JX(I)=RNDRG(16,222):JY(I)=16:JV(I)=RNDRG(12,20)
REM 衝突判定
IF (ABS(X-JX(I))>=0)*(ABS(X-JX(I))<=12)*(ABS(Y-JY(I))>=0)*(ABS(Y-JY(I))<=12) THEN GOTO *GAMEOVER
JY(I)=JY(I)+JV(I)
REM じゃまキャラ表示
GCURSOR JX(I),JY(I)
PRINT JC(I)
NEXT I
GOTO *LOOP
*GAMEOVER
MESSAGE 'OUCH! ・・・ GAME OVER'
END
あたり判定とかはテキトーです。あくまでスプライトのキホンを示すだけですので、あしからず。
ここで扱っているのはごく初歩的な内容ですが、グラフィックを扱うプログラム開発の手助けになれば何よりです。スプライトが使えるようになると表現力が格段に向上するので、ビジュアルにこだわりたい方はぜひ挑戦してみてください。
機会があれば、スプライトの応用編もやってみたいですね。
--
このページの文章やプログラムに誤りや問題点がある場合は、遠慮なくご指摘ください。
最後に一応、断り書きを。
[お断り]
このページに掲載したプログラムを使用したことで、どのような損害または障害が発生したとしても、筆者は一切その責任を負いません。
ご使用は、あくまで自己責任で行ってください。
また、ここに挙げたプログラムは、いっさい断りなく自由に、転用/加工してもらってかまいません。ただし、その転用/加工の結果で何が起きようと、筆者は何も責任を負わないものとします。