プチコン3号 15日目 スプライトを決まった経路で移動させる

SmileBASICでは、ゲーム作成に便利なスプライト用命令をいろいろ持っていますが、その中でも特徴的なのが9日目の記事でも使ったSPANIM命令です。
SPANIM命令では「座標変更」を指定することもでき、これを使うと、あらかじめ決めたコースの上をスプライトを移動させていくことができます。

今回は前回の「落下する動き」「弾む動き」をスプライトにさせるプログラムを作ってみました。
前回は円が画面の中で弾む動きをさせるためにY座標をプログラムで計算させていましたが、今回は
(1)落下の座標計算をより簡略化し、
(2)計算結果を配列に記録し、SPANIM命令で再生する
ようにしてみます。

落下する動きの計算を簡単にすると、以下のようなダイレクトモードでも動かせる程度の短いプログラムになります。
これを入力してみると、スプライトが画面上端から画面下端まで落ちる動きをします。

pc15-1.jpg
SPSET 0,226
Y=0:FOR I=1 TO 9:FOR J=1 TO 5:Y=Y+I:SPOFS 0,200,Y:VSYNC 1:NEXT:NEXT

ここで行っている処理は、スプライトを下方向へ
「最初は1ピクセルずつ、5回動かす」「次は2ピクセルずつ、5回動かす」…「9ピクセルずつ、5回動かす」
というものです。これだけでも落下するような表現になります。

前回、落下する動作とは
「移動速度が時間経過に従って直線的に増えていく」
ような動作であると説明しました。
そして、ループの中で

V[I]=V[I]+G
INC Y[I],V[I]

という計算により、VをGずつ増やし、そのVをYに加算することで落下動作を計算していました。

pc14-5.jpg

先ほど示したプログラムは、「5回ごと」に「移動幅が1ピクセル」増えますので、「G=1/5ピクセル」としたのと同じことになります。

この「移動幅」と「回数」を変化させれば、「落下速度・落下する距離」が異なるいろいろな落下を表現できます。

上で示したプログラムでは、移動幅1ピクセル~9ピクセル、同じピクセル数での移動回数5回です。
従ってトータルでの移動距離は

(1+2+3+4+5+6+7+8+9)×5 = 225

となります。
これは実は「画面の上から下まで落ちる動き」をさせる場合の移動距離

画面縦240ピクセル-スプライト縦16ピクセル=224ピクセル

にほぼ等しくなるようしたのです。

移動回数を5回ではなく4回にすると、より早く落下する動作になります。
この場合は「G=1/4ピクセル」としたことになります。
4回ずつの移動で、例えば移動幅を1ピクセル~10ピクセルに変化させた場合、移動距離は

(1+2+3+4+5+6+7+8+9+10)×4 = 220

となります。

下図に、移動幅と移動回数から移動距離を求める早見図を載せておきます。

pc15-2.jpg

移動幅をマイナスから始めると、スプライトはいったん投げ上げられてから戻ってくる、すなわちジャンプするような動きになります。
例えば下のプログラムのように、-9ピクセル~9ピクセルとすると、画面いっぱいの大ジャンプになります。
移動幅の初期値と終了値をもっと小さくして、例えば-5ピクセル~5ピクセルとすると、より小さなジャンプになります。

SPSET 0,226
Y=224:FOR I=-9 TO 9:FOR J=1 TO 5:Y=Y+I:SPOFS 0,200,Y:VSYNC 1:NEXT:NEXT

それでは、ループの中でSPOFS命令で毎回座標を指定するのではなく、先に一連の座標を配列に記録してからSPANIM命令で再生してスプライトをアニメーションさせてみます。
この方法のメリットは、座標をあらかじめ計算済みなので、動かすたびに計算し直す必要がないことです。

SPANIMの座標変更機能は、
(1)座標の列をパラメータで直接指定する
(2)座標の列を配列で渡す
(3)座標の列を格納したDATA命令の直前のラベルを指定する
という3つの方法がありますが、今回は(2)を使います。

使い方は

SPANIM 管理番号,”XY”,配列,ループ

です。
または

SPANIM 管理番号,”XY+”,配列,ループ

です。
“XY”で指定した場合は、スプライトは指定された座標へ移動しますが、”XY+”で指定した場合は、スプライトは「SPANIM命令が実行された時点の座標+指定された座標」へ移動します。
つまり、SPOFSで移動の出発点を指定すると、そこから出発してスプライトを動かすことができます。
配列は1次元配列で、

フレーム数,X座標,Y座標

の3つを連続して格納します。
なお現在のプチコン3号では、配列の中にこの3つを33組以上格納するとエラーになるようです。

さらに、SPANIMには「補間機能」があり、これを使うと少ないパラメータで長時間の動きを表現できます。

補間機能は、1フレームごとに「移動距離/フレーム数」だけ移動させることができる機能です。
たとえば、今回の落下の動きは「最初は1ピクセルずつ、5回動かす」で始まりますが、これは
「5ピクセルを5フレームで動かす」という指定の仕方ができます。

下図を見てください。左側が1フレームごとの動作を指定した場合、右側が補間機能を指定した場合です。
補間機能を使う場合は、フレーム数を負の値で指定します。
左側は時間軸に沿って、1フレームごとにY座標を1ずつ5回増やしています。
右側では、グレーになっている部分の指定は不要で、フレーム数が「-5」Y座標が「5」という指定をしています。
これで左側と同じ指定をしたことになります。
このあとの5フレームは2ピクセルずつ動いて、Y座標が15まで増えますので、この次の指定はフレーム数が「-5」Y座標が「15」という指定をすればよいのです。

pc15-3.jpg

今回は「落下運動の座標を配列に入れて、SPANIMに渡し、スプライトに落下運動をさせる」プログラムを作りました。
リストはこちらです。
pc15-4.jpg

DIM A[0]
SP_FALL 0,0,1,-4,9,-5,A OUT X,Y
SP_FALL X,Y,1,-4,4,-5,A OUT X,Y
SP_FALL X,Y,1,-2,2,-5,A OUT X,Y
PUSH A,-5:PUSH A,X:PUSH A,Y
SPSET 0,226

@LOOP
SPOFS 0,160,50
SPANIM 0,"XY+",A,1
SPANIM 0,"C",4,&HFFFFFFFF,4,&HFF808080,0
WHILE SPCHK(0) AND 1 != 0:WAIT 1:WEND
GOTO @LOOP

DEF SP_FALL X,Y,DX,VMIN,VMAX,VTIME,ANIM OUT NEWX,NEWY
K=ABS(VTIME)
FOR I=VMIN TO VMAX
PUSH ANIM,VTIME
PUSH ANIM,X
PUSH ANIM,Y
X=X+DX*K
Y=Y+I*K
NEXT
NEWX=X
NEWY=Y
END

このプログラムを動かすと、下図のように星のスプライトが上から落ちて2回弾んで止まるという動作を繰り返します。

pc15-5.jpg

プログラムの中にループがありますが、このループはアニメーションを繰り返し再生させているだけです。
落下の動きそのものは

SPOFS 0,160,50
SPANIM 0,”XY+”,A,1

だけで実現しています。

まずSPOFSで移動の開始ポイントを指定しています。
次にSPANIMでスプライトの移動開始です。配列Aに座標のデータが入っています。
ループ指定は1ですので、1回移動したら終了です。

その次の

SPANIM 0,”C”,4,&HFFFFFFFF,4,&HFF808080,0

は、スプライトの色を4フレームごとに明るくしたり暗くしたりして、チカチカ瞬くように見せています。

その次の

WHILE SPCHK(0) AND 1 != 0:WAIT 1:WEND

は、スプライトの移動が終わるまでループして待つ処理です。
今回は詳しく説明しませんが、SPCHKはSPANIMで指定した動作の状態を調べる命令です。
パラメータはスプライトの管理番号で、結果はSPANIMで指定できる様々な動作について「1(実行中)」「0(完了)」がビットパターンで得られます。
「AND 1」で座標変更動作の状態を調べています。

SPANIMに渡す座標の計算は、15行目からの

DEF SP_FALL X,Y,DX,VMIN,VMAX,VTIME,ANIM OUT NEWX,NEWY

の中で行っています。
各パラメータの意味は、

・X,Y:座標の初期値
・DX:X軸方向の移動量
・VMIN、VMAX:Y軸方向の移動量の最小値と最大値
・VTIME:同じ移動量で移動するフレーム数
・ANIM:座標を格納する配列
・NEWX、NEWY:最後の座標(ANIMの中には格納しない)

となっています。
一番最初に挙げた例「最初は1ピクセルずつ、5回動かす」「次は2ピクセルずつ、5回動かす」…「9ピクセルずつ、5回動かす」の場合は、
・VMIN=1
・VMAX=9
・VTIME=5
に相当します。

計算結果はPUSHで配列の末尾に追加しています。
PUSH命令は12日目にスタックの説明のところで解説しましたが、今回は単に配列の最後にデータを追加するために使っているだけです。

最後の座標をANIMに入れずにNEWX、NEWYで返しているのは、SP_FALLを繰り返し呼んだ場合に、動きをスムーズにつなげるためです。
SP_FALLを呼び出している2行目から4行目までは、

SP_FALL 0,0,1,-4,9,-5,A OUT X,Y
SP_FALL X,Y,1,-4,4,-5,A OUT X,Y
SP_FALL X,Y,1,-2,2,-5,A OUT X,Y

となっていますが、1つ目の呼び出しが最初の落下動作、2つ目と3つ目の呼び出しが弾む動作です。
直前の呼び出しの最後の座標X,Yを、直後の呼び出しの際に座標の初期値として渡しています。
そして、最後に

PUSH A,-5:PUSH A,X:PUSH A,Y

で座標を配列の末尾に追加しています。

コメント