アニメーション1
HSP2の入門講座と同じ章で同じ内容の講座を行います。
今回は画像のエフェクト、アニメーションです。
アニメーションとは、言うまでもないかと思われますが、リアルタイムで動作する画像のこと。
生き物が動いていることは当たり前のように考えられますが、
固定された動くはずのない画像がどのような原理で動作するのでしょうか?
アニメーションGIFやパラパラ漫画というものをご存知である方は知ってのことでしょう。
ある間隔置きにちょっとずつ異なった複数の画像を表示していくのです。
文字を使ったアニメーションをしてみましょう。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
	color : boxf
	color 255, 255, 255 : pos 50, 400 : mes "あ、流れ星だ。"
	wait 100
	repeat 50
		redraw 0
		color : boxf
		color 255, 255 : pos cnt * 10 + 200, cnt * 2 + 30 : mes "★"
		redraw
		wait 2
	loop
…つまらないものですが、流れ星が動いています。
生き物ではなく動くはずのない文字(星)がウィンドウ内を動いています。
内部処理としては、背景を描き、文字の位置を変更して繰り返し表示しているだけですが、
見ている側としては、確かに自動的にアニメーションしています。
文字ではなく画像であっても基本は同じです。
表示場所の移動だけでなく、画像内で異なる動き(足踏み、まばたき等)をしたい場合は
位置だけでなく表示させる画像の変更も行う必要があります。
しかし、表示するアニメーション数分の画像ファイルを用意するなんてことはあまりしません。
それはアニメーションを自動的に補完してくれる…ということを言っているのではありません。
一つのキャラクタにつき一つの画像を用意するのが一般的ではないでしょうか。
コレはどういうことかと言うと、一つの画像ファイル内に複数の場面を想定して入れるのです。
前向きパターン、後ろ向きパターン、左右の横向きパターン、前向きの逆足が前のパターン…等々。
何故、このように一つの画像内に情報を詰め込むのかと言うと、
画像ファイル管理、画像内パレット管理等の使用効率がアップするからです。
メリットがあるのは分かったけど、1つの画像に複数のパターンがあると、
余計な部分(右向きパターンだけでよいのに左・前・後ろ向きパターン)も表示されてしまう…。
そのような心配をする方もいることでしょう。
しかし、基本的に読み込んだ画像をそのまま表示させることはあまり行いません。
読み込んだ画像をそのままウィンドウ内に表示させるのはイメージビューア位かも知れません。
ツールのスキン画像だろうが、ゲームのキャラクタ画像だろうが、
一旦はバッファに読み込み(メモリに展開し)、バッファからその画像の一部又は全てを表示させます。
このような手順を踏むのは、極力、ハードディスクへのアクセスを減らすのが目的です。
いつ何時画像上に別情報が表示されたり、画像が上書き更新されてしまうかわかりません。
バッファ、というのは前章で行いましたね。
まずは、このバッファ内に画像を読み込みます。
そして次に、バッファ内の画像から表示させたいウィンドウに画像の一部又は全てをコピーするのですが、
ココで注意が必要です。
HSPの仕様として、ウィンドウ又はバッファを初期化すると、
そのウィンドウ又はバッファが描画対象先の画面に切り替わります。
画像だけでなく、表示・配置させるもの全て、です。
 1
 2
 3
	mes "スクリーン(ウィンドウ)IDの0番です"
	screen 2, 400, 300
	mes "ID2を初期化すると、以後はID2となります。"
gsel ウィンドウID, アクティブスイッチ
ウィンドウID何番目のウィンドウを対象とするかを指定。
アクティブスイッチ指定ウィンドウの表示状態を制御。
GraphicsSELectの略でしょうか?描画先画面を変更する命令です。
ウィンドウIDに、変更後のウィンドウIDを指定しますが、
一度も初期化されていないウィンドウ又はバッファを指定するとエラーとなります。
アクティブスイッチは描画先と同時にウィンドウの表示状態を変更することが出来ます。
スイッチの指定による効果は次の通りです。

効果
-1描画先の変更と同時に非表示にする。
0描画先の変更のみ行う。
1描画先の変更と同時に表示する。
2描画先の変更と同時に最前面に表示する。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
	mes "1. スクリーン(ウィンドウ)IDの0番です"
	screen 2, 400, 300
	mes "2. ID2を初期化すると、以後はID2となります。"
	gsel // パラメータ省略 = gsel 0, 0
	mes "3. しかし、gsel命令で描画変更先を変更できます。"
	pos 100, 100 : mes "4. 尚、カレントポジションはウィンドウ毎に保持してます。"
	gsel 2, 2
	mes "5. なので、ID2には影響しません。"
	color 255
	mes "6. 後、color命令による色の変更も"
	gsel 0, 1
	mes "7. 影響することはないですよ。"

ウィンドウ最前面というのは、他のあらゆるウィンドウをアクティブ化しても
最前面ウィンドウが前面に表示されるということです。
最前面ウィンドウが複数あるときは、アクティブになっているウィンドウが前面に表示されます。
ウィンドウの非表示化ですが、screen命令のモードを2にした時の状態を指しています。
あくまで非表示であり、メモリから破棄されるわけではありません。
非表示中にも描画させることはできますし、後で再表示することもできます。
尚、一度初期化したウィンドウを破棄する命令は用意されていません
さて、この命令を使うことでバッファに読み込んだ後、
描画先をメインウィンドウに戻すことができるようになりました。
次が肝心の画像コピーを行う命令の紹介です。
gcopy ウィンドウID, X座標, Y座標, 横幅, 高さ
ウィンドウIDコピー元のウィンドウIDを指定。
X座標コピー元の左上を基点としたX座標を指定。
Y座標コピー元の左上を基点としたY座標を指定。
横幅コピーする画像の横幅を指定。
高さコピーする画像の高さを指定。
GraphicsCOPYの略でしょうね。
指定ウィンドウ(p1)の基点(p2,p3)から指定サイズ(p4,p5)分を、
現在の描画先ウィンドウのカレントポジションにコピーするというものです。
p2、p3パラメータの基点座標を指定しなければ、左上である(0,0)が、
p4、p5パラメータのサイズを指定しなければデフォルトでは32×32ピクセル分のみコピーします。
コピーサイズ省略時のサイズを32×32以外にするには、次章で説明する別の命令を用いる必要があります。
Windows用の画像です
上記の画像には、計12パターンが入っています。
上から順に、左向き、上向き、右向き、下向きパターン。
左から順に、右足が前、両足揃え、左足が前パターンとしていますが、
これは勝手に定めただけであり、内部のフォーマットは自分で決めてください。
この画像を使ってアニメーションさせてみましょう。
サンプルを実行するにはHSPの初期カレントフォルダにファイルを保存させてみてください。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
	buffer 1
	picload "character.bmp"
	gsel 0
	pattern = 1, 2, 1, 0 // 手足の動きパターン
	posx = 600, 550, 50, 200
	posy = 80, 450, 370, 120
	repeat 100
		redraw 0
		boxf
		count = cnt
		// 4人分の描画を行う
		repeat 4
			pos posx.cnt, posy.cnt
			gcopy 1, pattern(count \ 4) * 32, cnt * 32, 32, 32 // バッファ内の画像をコピー
			switch cnt
				case 0 : posx.cnt -= 4 : swbreak
				case 1 : posy.cnt -= 3 : swbreak
				case 2 : posx.cnt += 5 : swbreak
				case 3 : posy.cnt += 2 : swbreak
			swend
		loop
		redraw
		wait 5
	loop
さて、長くなってきましたね。
スクリプトを動かしてみると、上下左右の各方向を向いて歩いている4つのキャラクターがいます。
まず、1〜2行目で使用する画像をバッファに読み込みます。
3〜6行目は各キャラクタに関する初期化を行っています。
patternは左端から順に0、1…とした時に幾つ目のパターンかを書いただけの配列です。
1210という順は、中央・左足前・中央・右足前・(中央・左足前…)という順で動作させる為です。
posxは各キャラクタの初期X座標、posyは初期Y座標です。
7〜24行目までが有限回数のメインループです。
内訳として、チラつきを抑える為のredraw命令で仮想画面のみを書き換えるようにして、
次に背景を黒色で塗りつぶします。
ループの度に塗りつぶしを行っていますが、コレは一旦書いた前のキャラクタを消す為に行っています。
背景は初めの一回だけ行うようにするとどうなるか…。
ループの前に背景を塗りつぶすようにスクリプトを変更して試せば結果がわかることでしょう。
12行目からさらに内側ループがありますが、コレは4人分を一回の記述で動作させる為のものです。
4人だとループを使わずに書いたものとほとんど変わらないためにあまり分かりませんが、
実際のゲームでは1画面にもっと多くのキャラクタが登場することになる可能性があります。
その時に1キャラ毎に書いていたのでは応用が利かなくなってしまいます。
ループ前に、各キャラクタの画像や位置、動作、向き等を配列に入れておいて、
ループを使って一度に描画してしまいましょう。
今回の新命令がある14行目でpattern(count\4)というのがあります。
まずカッコ内のcountは、10行目で入れている外側のループカウンタのことです。
コレを4で割った余り…つまりpattern値を利用した「121012101210…」が得られるわけです。
その値に32を掛けた座標から32ピクセル分をコピーしているので、
動きとして、中央・左足前・中央・右足前…になるわけです。
p3のcnt*32は向きを指定しているもので、内側ループにより上下左右の各向きキャラを描画しています。
今回のサンプルは黒い背景で行っています。
通常は一色で塗りつぶすよりかは、背景も何らかの画像である確率の方が高いですね。
背景画像の場合も基本的にやり方は同じで、boxf命令による塗りつぶしの変わりに
キャラクタよりも先に背景画像を描画するわけですが、一点問題がありますね。
キャラクタが32×32ピクセルとなっており、形は四角ではなく余白があります。
ゲーム中、ソコはいわゆる背景が描かれる領域となりますが、
ココをある色で塗りつぶすと、背景がその色の時にしか使えません。
一色で塗りつぶす代わりに、実際に使用する背景画像を使うとしても同じ様なものです。
その背景の時にしかそのキャラクタ画像は使えないというのでは
あらゆる背景に対応した幾つものキャラクタ画像を用意することとなって、あまりに非効率です。
ココでマスクというものが必要となりますが、この内容については次章行います。