NSDefSpline / NSSpline を使ってなめらかにホーミング

nslua.dllで使えるNSDefSplineとNSSplineは、それぞれスプライン補間曲線を作成する関数と実際の補間値を取得する命令である。
これを使えば、指定した三つ以上の点を通るなめらかな曲線を描くことができる。
例えば、画面上の点(80, 440)を出発し、25フレーム目で(80, 40)を通過、50フレーム目で(560, 440)に到着、終了とする曲線を考えてみる。
まずは、これを表にしてみる。

NSDefSplineに与えるテーブル
frame 0 25 50 {0, 25, 50}
x 80 80 560 {80, 80, 560}
y 440 40 440 {440, 40, 440}

定義

この曲線を定義するには、NSDefSplineを2回使う。

NSDefSpline(0, {0, 25, 50}, {80, 80, 560})  -- 第一引数は0〜99までのどれかを任意。第二引数はframe、第三引数はxを与える。
NSDefSpline(1, {0, 25, 50}, {440, 40, 440}) -- 第一引数は0〜99までのどれかを任意。第二引数はframe、第三引数はyを与える。
-- 第一引数はかぶらないこと。
NSExec("luasub spline")
function NSCOM_spline()
	local frame = NSPopInt()
	NSPopComma()
	local retx = NSPopIntRef()
	NSPopComma()
	local rety = NSPopIntRef()
	
	NSSetIntValue(retx, NSSpline(0, frame))
	NSSetIntValue(rety, NSSpline(1, frame))
end

一つ目のNSDefSplineで、フレーム番号から表示すべきx座標を計算する関数ができる。
同様に、二つ目のNSDefSplineで、フレーム番号から表示すべきy座標を計算する関数ができる。

mov %10,-1 ; フレーム番号とする。

*main
inc %10 ; フレーム番号を増やす
spline %10,%11,%12 ; フレーム番号に対するx, y座標を取得
drawbg
drawsp2 100,0,255,%11,%12,100,100,0 ; スプライトを画面に書きこむ。
draw ; 画面に反映
wait 40 ; 1秒間24フレームだとこれくらい待つ。
if %10 < 51 goto *main ; フレーム番号がいっぱいになるまで繰り返す。

実行例

上記の例では、このような動きになる。

フレーム番号をずらしてみる。

中間の通過点に達するまでのフレームを遅くすると、そこまでゆっくり移動し、その後は早くなる。「溜め」の演出に……使えるかな?

NSDefSpline(0, {0, 35, 50}, {80, 80, 560})
NSDefSpline(1, {0, 35, 50}, {440, 40, 440})


逆に、フレームを早くすると、早くなるが、あまり使いどころは思いつかない。

始点と終点を一致させる。

始点と終点を一致させ、通過点を複数指定すると、戻ってくるような軌道にできる。

NSDefSpline(0, {0, 25, 50, 75}, {40, 560, 560, 40})
NSDefSpline(1, {0, 25, 50, 75}, {240, 40, 440, 240})


ブーメランのような武器の演出に使える……か?

注意点

フレーム番号は、最初に指定した範囲でしか指定できない。
今回の例の場合は、frameで0〜50の値を使っているので、この範囲になる。これを逸脱すると、エラーになって止まる。
フレーム番号は、整数でなくてもよい。
NScripter側から呼び出す場合は、整数しか指定できないが、luaの側ではどんな値でも受け入れてそれなりに計算してくれる。

応用例

  • フレーム番号を与えて座標を計算するので、巻き戻しも自由自在w
  • 始点・通過点・終点の最低3点が必要だが、通過点は複数あってもよい。通過点の配置によっては、うねうねと蛇のように敵を狙う動きなどができる。
  • もし、敵を貫通するようなアニメーションがしたい場合は、敵の位置を通過点にして、加えて、その手前の点と敵の位置をつないだ直線上画面外に終点を設定する必要がある。