- SHIORI - チュートリアル

ステップ01 スプライトを表示してみよう


0.テーマ設定

 とりあえず基本からということで、まずはスプライトを表示してみましょう。
 目標は、こんな感じの絵です。

画面の左上の四角いのが表示するやつ。

 画面左上のやつが、表示させるスプライトです。 その他の部分は、何も表示していません。 VRAM上に残ったゴミが表示されている部分なので、このへんは表示のたびに内容変わるでしょうね。
 まぁ、どっちにしても、左上に出てる「- SHIORI -」の文字がミソです。

 これを実現するための概要は、以下のような感じです。

  1. まず - SHIORI - の実行に必要なファイルとディレクトリを用意します。
  2. 次に、画像を - SHIORI - に詠み込ませるための定義ファイルを書き、その画像の一部分をスプライトとして定義します。
  3. 続いて、定義したスプライトを表示するESPオブジェクトの特性を定義します。
  4. 特性を定義したら、そのオブジェクトを出現させるためのコマンドセットを記述します。
  5. 最後に、画面に表示するためのコマンドセットをステージイベントとして設定して、記述終了です。

 要するに、この画像を表示パターンとするESPオブジェクト特性を定義し、そのオブジェクトを画面に出現させる、という手順ですね。

 以下で、その詳細を解説します。


1.必要なファイルとディレクトリを準備しよう

 まずは、必要なファイルとディレクトリを用意します。 最低限必要なものは、shiori.exe。 その他のファイルやディレクトリは、自分で作ります。

 以下のような構成で、ディレクトリとファイルを用意してください。 shiori.exe は、アーカイブを展開したものからコピーしてください。 他のファイルは、テキストファイルとして空のものを作っておきます。
.hrnファイルなど、コメントが書けるものは、ちょっとしたコメントだけ書いておくと良いでしょう。

[shiori] ----- shiori.exe               - SHIORI - の実行ファイル
           |-- shiori.cfg               ファイル名定義ファイル
           |-- [sample] ----- [bmp]         ビットマップ画像とパレットファイルを格納する
                          |-- [wav]         音のファイルを格納する
                          |-- config.hrn       リソース定義ファイル
                          |-- prop.hrn        特性定義ファイル
                          |-- comset.hrn       コマンドセット定義ファイル
                          |-- event.hrn       イベント定義ファイル
                          |-- enemap.hrn       敵配置マップ定義ファイル

 この関係は全てのステージで同じなので、この状態で雛形を取っておくと、あとあと楽です。

 ディレクトリ構成を準備したら、ステージ内で使う画像や音を、[bmp]ディレクトリと[wav]ディレクトリに入れましょう。 なお、今回音は使いません。 下のような絵だけ、[bmp]ディレクトリに入れるとしましょう。

これが、今回出すスプライトの絵


2.ステージコンフィグファイル shiori.cfg を記述しよう。

 いよいよ設定を記述します。 まずは、shiori.cfg を書きます。 これは、各ステージで殆ど変わらないので、今回のように「最初」でなければ、既存のものに修正を加える方が良いです。

 記述内容は、以下のように。

#
#  <<<<<<< sample Configuration file. >>>>>>>
#

#------------< ステージ構成定義ファイル >------------
# 	このステージを定義するための定義ファイル群を
#	指定します。
#----------------------------------------------------
%CONFIGURE   sample/config.hrn		# ステージ構成定義ファイル
%PROP        sample/prop.hrn			# 敵特性定義ファイル
%COMSET      sample/comset.hrn		# コマンドセット定義ファイル
%EVENT       sample/event.hrn			# イベント定義ファイル
%ENEMAP      sample/enemap.hrn		# 敵配置マップファイル


#------------< リンク先ステージコンフィグ >------------
# 	このステージの次に繋がるステージを定義した
#	定義ファイルを指定します。
#	定義ファイルには、上から順に[00]から
#	インデックスが振られます。
#------------------------------------------------------
#           [filename]
%NEXTSTAGE	shiori.cfg				# 次のステージを定義した定義ファイル [00]

 ここは、前 - SHIORI - と違う点は無いので、とりあえず細かいところは省略します。
 % ではじまる行が、設定ファイルとして意味のある行です。

 先頭の5つで、このステージで使う*.hrn ファイルのファイル名を指定します。 なお、これは shiori.exe があるパスからの相対パスで記述します。(厳密に言えば、カレントからの相対パスですが。)

 最後に、%NEXTSTAGE で、このステージからリンクするステージのファイル名定義ファイルを指定します。 このサンプルでは使用されませんが、一応自分にループバックするようなものでも記述しておきましょう。


.リソース定義ファイルを記述しよう

 使用する .hrn のファイル群を指定したら、次はいよいよ *.hrn のファイルの中身を記述します。

 まず記述するのは、リソース定義ファイル config.hrn です。 まずはステージ内で使用する画像を指定しないことには、何も始まりません。
 まぁなんですな。 キッチンスタジオで料理人がまずやることといったら、料理の材料集めです。 材料を集めなければ料理は作れませんから。 - SHIORI - も同じようなもので、まずは材料を集めます。

 この場合は、こんな風に記述します。

# ----------------------------------------------------------------
#  チュートリアル サンプル リソース定義
# ----------------------------------------------------------------

# 描画デバイス設定
&SetLayers( 5 )


# ====================
#   ビットマップ登録
# ====================
#
# <<< スプライト >>>
&RegistBitmap	SPRITE , ( 'sample/bmp/sprite.bmp' , 255 )


# ======================
#   パレットの読み込み
# ======================
&RegistBMPPalet MAINPALETTE ( 'sample/bmp/sprite.bmp' , 0 , 255 )


# ==================
#   スプライト登録
# ==================
# <<< スプライト >>>
#
&RegistSprite	SPRITE	( 0,0 , 137-1,121-1 ) , ( 0,0 )

1行ずつ解説しましょう。

描画レイヤーの指定
&SetLayers( 5 )

 この行は、全てのステージにおいて必須なので、必ず記述します。
 このコマンドで、ステージで使用する描画レイヤーの数を指定します。 最低でも1以上の値を指定してください。 この指定が無い場合、使用するレイヤー数が0となり、描画先が無くなってしまいます。

 

画像の詠み込み
&RegistBitmapSPRITE , ( 'sample/bmp/sprite.bmp' , 255 )

 この行で、- SHIORI - に画像の読み込みを指示しています。
 このように指定することによって、「sample/bmp/sprite.bmp」を、「SPRITE」という名前で詠み込ませることができます。 ビットマップの透過色は、パレット番号「255」になります。

 この場合1行しかありませんが、使用する画像ファイルが複数ある場合は、全てのファイルについて同じように指定してください。

 なお、画像は読み込んだだけでは、画面に表示されません。 画像をメモリ内に取り込むだけです。

 

パレットの読み込み
&RegistBMPPalet MAINPALETTE ( 'sample/bmp/sprite.bmp' , 0 , 255 )

 この行で、パレットの読み込みを指示します。
 ここで読み込んだパレット情報は、すぐには画面には反映されません。 後述の「コマンドセット」で解説する &ReflectPalette コマンドを使用して、初めて画面に適用されます。

 ちなみに。
 - SHIORI - では、このように画面に反映されていない状態でメモリ上に保持されているパレット情報のことを、「オフスクリーンパレット」と呼称します。

 
スプライト定義
&RegistSprite SPRITE ( 0,0 , 137-1,121-1 ) , ( 0,0 )

 画像を画面に表示させるためには、2通りの方法があります。 ひとつは、ここで定義する「スプライト」を使用する方法。 もうひとつは、後の章で解説する「グラフィック画面」を使用する方法です。
 ここでは、スプライトに関する情報を定義します。

 スプライトは、『ビットマップ画像に対して矩形を登録する』という方法で定義します。 登録したスプライトを画面に表示するときには、『このビットマップ画像に対して、○○番目に登録されたスプライト』という形で表示する画像を指定することになります。

 この例では、SPRITEという名前で定義されたビットマップ画像に対して、(0,0)-(136,120)の範囲の矩形をスプライトとして定義します。 このスプライトが画面に表示されるときは、SPRITEとして定義したビットマップ内の、(0,0)-(136,120) の領域だけが切り取られて、画面に表示されることになります。

 最後についている (0,0) は、スプライトの中央位置指定です。 スプライトが画面に表示されるとき、ここで指定した座標位置に基づいて、指定座標からずらされた位置に表示されます。 たとえばこの場合、(x,y)の位置にこのスプライトを表示しろ、と指示すると、スプライトの左上の点が(x,y)に来るように表示されます。 もし、ここでの指定が(16,16)だった場合は、画像の左上の点が(-16,-16)になるように表示されます。


.特性ファイルを定義しよう

 画像の読み込みを指示したら、こんどは、その画像を表示パターンとして持つようなキャラクタ特性を定義します。

 「3.」までで、スプライトの定義まではできました。 こんどは、そのスプライトを表示パターンとするキャラクタ特性を定義します。

 - SHIORI - では、世界の全ては「オブジェクト(キャラクタ)」で構築されています。 従って、スプライトを定義しただけでは、画面は何も表示されません。 定義したスプライトを画像パターンとして持つような、何らかのオブジェクト(キャラクタ)を定義し、それを画面上に出現させてはじめて、画面に絵が現れます。

 キャラクタには、「敵」やら「自機」やら、いろいろ種類がありますが、ここでは「ESP(Effect SPrite)」を使うことにしましょう。

 さて。特性ファイルは以下のように定義します。

#---------------------------------------------------------------------
#  チュートリアル サンプル 特性定義
#---------------------------------------------------------------------

#----------< 一般設定 >----------
#
&SetDimension	2

#----------< キャラクタ特性定義 >----------
#
# ================================================================================
#   スプライト
# ================================================================================
&DefineESP( SPRITE , 0,0,2 ){

	# -----< 属性 >-----
	#
	&Axis			( X , Y )					# オブジェクトの座標系

	# -----< 描画レイヤー指定 >-----
	#
	&Draw( 0 ){
		# 配列指定
		DIMENSION	= 1
		VALUES		= {
			( 0 , 1 )
		}
	}


	# -----< 描画パターン指定 >-----
	#
	&Draw( 1 ){
		# 配列指定
		VALUES		= {
			( BMP( SPRITE ) , 0 , 1 )
		}
	}


	# -----< ポインタ関連 >-----
	#
	&DrawPointer	( 0 , LAYER )		# 0 番目のラインを描画レイヤーとして認識するポインタ
	&DrawPointer	( 1 , PATTERN )		# 1 番目のラインをパターンとして認識するポインタ
}

詳細な説明の前に、特性の概念説明

 特性定義では、かなり自由度の効いた設定が可能です。 しかしそのぶん、概念としては比較的複雑なことになっており、ここで全てを解説することはできません。 ここでは、キャラクタひとつを固定位置に表示するために必要な概念のみに絞って、概念の説明をします。

1.軸について

 - SHIORI - は、基本的に2次元のSTGを作るためのシステムです。 しかし、特性定義においては、出現するキャラクタが取り扱う世界の「次数」(軸の本数)を規定してはいません。 これは、2次元のSTGに出てくるキャラクタの特性が、必ずしも2次元の世界で表現し切れるとは限らないからです。 何を言ってるのか、よく分かりませんね。 ちょっと具体例を見てみましょう。

 たとえば、『グラディウス』のような作品のような場合、これは2次元のSTGであり、キャラクタが取り扱う次数も2次元(XとY)で良いです。 キャラクタの表示位置は、完全にX座標とY座標の2つの値で表現できます。実にあたりまえなハナシです。

 しかし、『レイフォース』のような場合はどうでしょうか。 これは、2次元のSTGのくせに、キャラクタの存在位置には奥行きがあります。 この場合キャラクタは、X座標とY座標に加えて、Z座標を管理する必要性が出てきます。

 では、『レイストーム』の場合はどうなるでしょうか。
 こんどは、キャラクタがポリゴンになりました。 それにともなって、キャラクタの表示にはX,Y,Zの位置座標に加え、キャラクタの回転成分(X軸まわりの回転,Y軸まわりの回転,Z軸まわりの回転)を管理する必要がでてきます。 この場合、ひとつのキャラクタが取り扱う次数は6です。

 このように、ゲーム自体が2次元のSTGとはいっても、キャラクタ自体が取り扱う次数は2とは限らないワケです。
 そこで - SHIORI - では、オブジェクトの特性として「次数」という項目を用意し、キャラクタの次数を変更することができるようにしてあります。 この「次数」属性に任意の次数を指定することにより、任意の次数で表されるキャラクタを定義することができます。

 

2.ベクタについて

 1で述べたように、キャラクタが持つ位置情報などの次数は、2次元STGだからといって、必ず2であるとは限りません。

 さて。キャラクタを画面上に出現させる時などには、出現位置を座標で指定することになります。
 2次元のキャラクタならX座標とY座標を指定して出現させます。 3次元なら、X,Y,Z座標を指定して出現させることになりますね。

 このような、座標値などの『いくつかの値が組み合わさってひとつの意味合いをなすものだけど、いくつの数値が組合わさるのかは分からない』、といったようなモノを表現するときに使用するのが「ベクタ」というものです。

 ベクタとは、『次数の数だけの数値をカンマで区切って、カッコで囲んだもの』として定義されます。 要するに、

  2次元ベクタ ( 100,150 )
  3次元ベクタ ( 123 , 456 , 789 )

こんな感じです。 まぁ、言葉で言うとワケわかりませんが、具体的に書くと、当たり前みたいなことですね。

 

3.特性配列について

 キャラクタの特性には、「特性配列」という概念があります。 これは、ベクタの集まりと考えておけば、問題ありません。
 この「特性配列」にはいろいろな使い道があるのですが、今説明するのはやめておきます。 とりあえず、「ベクタが集まったもの」と思っておいてください。

 この「特性配列」は、「ベクタが集まったもの」です。逆に言えば「ベクタが集まったもの」でしかありません。 これはつまり、そのベクタが持っている値が何を意味するかまでは定義されない、ということを意味します。 つまり、特性配列とは単に「値」を管理するためだけのものです。 その値が何を意味するのかは、別の所で定義します。

 なお、特性配列には「移動配列」「パラメータ配列」「描画配列」の3種類があります。 ここで詳細は述べませんが、文字通りのものなので、適当に推測しといてください。

 

4.配列ポインタについて

 特性配列とは、「ベクタ」の集まりであり、「ベクタ」の集まりでしか無いと述べました。
 配列ポインタというものを使うと、その「ベクタ」が、何を意味する値なのかを定義できます。

 配列ポインタとは、その名の通り、「この配列の、この値」といって、配列内のベクタを指し示すものです。 さらには「この配列の、この値は、このキャラクタにとって○○という意味合いを持つ値である」という情報をも指し示すものでもあります。

 たとえば、キャラクタの移動量を、特性配列にガンガン定義したとします。 しかしそれだけでは、配列を定義しただけにすぎません。 「その特性配列に入れた値は、キャラクタの移動量として認識されます」という定義をするのが、配列ポインタというワケです。

 

 さて。そろそろ、定義ファイルの内容を解説しましょう。

デフォルトの次数設定
&SetDimension

 キャラクタの特性を定義するにあたって、そのキャラクタが取り扱う座標軸の数は不定であると述べました。 だからといって、毎回キャラクタを定義する度に「このキャラクタは2次元です」と定義するのも面倒です。 そこで、このコマンドによって、デフォルトの次数を設定することができます。
 このコマンドを記述した位置より下で定義したオブジェクトは、特に指定しない限り「2次元のもの」として解釈されるようになります。

 

ESPの定義開始
&DefineESP{ ... }

 これで、ESPオブジェクトの特性定義をします。
 ブレース内にコマンドを羅列して、特性を記述します。

 

軸設定
&Axis( X , Y )

 キャラクタの次数は、任意で変えることができます。
 さて。 たとえば次数が3だったとき、位置意味するベクタ ( 10 , 20 , 30 ) は、何を意味するでしょうか。

 大抵の場合は、( X座標 , Y座標 , Z座標 ) なのでしょうけど、、、もしかしたら、そうではないかもしれません。 3つめの軸は、実はZ軸まわりの回転を意味するもので、『座標(10,20)で、回転角30度』を意味するベクタであるかもしれません。

 軸の本数を規定する &SetDimension コマンドでは、その軸が何を意味するかまでは定義していません。 各軸が何を意味するかを定義するには、この &Axis コマンドを使います。 (・・・てゆーか、&Axis コマンドは必須項目ですが。)

 ここでは、( X , Y ) を指定しています。 これはつまり『このキャラクタが取り扱うベクタの、最初の値は X座標 を意味して、ふたつめの値は Y座標 を意味します』ということを定義しています。

 たとえば、ここで ( Y , X ) と書くと、ふたつめの値がX座標で、ひとつめの値がY座標ということになりますね。 まぁ、普通は混乱を招くだけですが、、、縦画面のSTGを作るときなどは、そっちの方が自然かもしれないですねぇ。

 

描画配列設定(描画レイヤー番号)
&Draw( 0 ){ ... }

 ここでは、( 0 , 1 ) という値を、特性配列に定義しています。
 特性配列の定義におけるベクタ指定は、ちょっと特殊なことになっています。 ここで指定するベクタは、必ずキャラクタの値よりも1多い数の要素を持ちます。
 この場合、次数1を明示しているところで ( 0 , 1 ) といったような、2つの値を持ったベクタを指定しています。 特性配列では、最後の要素は必ず繰り返し回数を意味することになっています。 つまりこれは、「( 0 ) というベクタを1つ定義する」という意味合いになります。 もしここで、( 0 , 10 ) と定義した場合は「( 0 ) 」を連続して10回定義するという意味合いになります。

 

描画配列設定(描画パターン)
&Draw( 1 ){ ... }

 同様に、パラメータ配列を定義します。

 

描画ポインタ追加
&DrawPointer( 0 , LAYER )
&DrawPointer( 1 , PATTERN )

 配列ポインタを使って、『0番目に定義した配列の中のベクタは、描画レイヤー番号を表します』、『1番目に定義した配列の中のベクタは、描画するスプライトのパターンを表します』ということを定義します。

 ・・・さて、以上で特性定義は終了です。
 まず、キャラクタの次数を2としました。 そして、次数2の世界において、最初の軸はX座標を、ふたつめの座標がY座標を表すと規定しました。
 次に、配列オブジェクトに値を定義しました。 定義した値は、それぞれ描画レイヤーと描画パターンです。

 


.キャラクタを画面に表示させるコマンドセットを記述しよう

 キャラクタ特性を定義したら、その特性を持つキャラクタを画面に出現させましょう。
 まずは、画面に出現させるようなコマンドセットを定義します。

#---------------------------------------------------------------------
#  チュートリアル サンプル コマンドセット定義
#---------------------------------------------------------------------

#----------< 一般設定 >----------
#
&SetDimension	2

# ====================================================================
#   ステージ冒頭の初期化
# ====================================================================
#
# カウントイベントドライバ
&CountEvent( INITIALIZE_STAGE ){
	COUNT = 0
} ,APPEAR_SPRITE

# 出現コマンド
&RequestESP( APPEAR_SPRITE ){
	OBJECT			= SPRITE			# 出現させるオブジェクトの名前
	ATTRIBUTE = {
		EXIST		= TRUE
		POS			= ( 0*64 , 0*64 )
	}
} , REFLECT_PALETTE

&ReflectPalette( REFLECT_PALETTE ){
	PAGE		= MAINPALETTE
	STARTINDEX	= 0
	ENDINDEX	= 255
	TARGETPAGE	= SCREENPALET
	TARGETINDEX	= 0
}

1行ずつ解説します。

デフォルト次数の設定
&SetDimension 2

 これは、特性定義における &SetDimension と同じと考えて良いです。

 

カウント0で発動するイベント
&CountEvent( INITIALIZE_STAGE ){ ... } , APPEAR_SPRITE
&RequestESP( APPEAR_SPRITE ){ ... }

 これらのコマンドセットで、キャラクタを表示させるという指示を記述しています。
 コマンドセット INITIALIZE_STAGE が発動すると、、、 まず、&CountEvent コマンドが、カウントが0かどうかを確認します。 カウントが0でない場合は、何もしません。 カウントが0だった場合のみ、&RequestESP に繋がることになります。
 &RequestESP では、ESP として定義したオブジェクト「SPRITE」を、(0,0)の位置に出現させるように指定しています。 なお「EXIST=TRUE」は必須なおまじないなので、省略できません。 

 

パレットの定義
&ReflectPalette{ ... }

 パレットを画面に反映します。

 

以上で、コマンドセットの定義は終わりです。

INITIALIZE_STAGEは、カウント0のときのみ、機能します。 カウント0のときは、キャラクタを画面に出現させ、パレットを画面に反映します。


.コマンドセットをステージイベントとして登録しよう

 コマンドセットは、発動させなければ、その効果は発揮されません。 例えて言うなれば(ってゆーかそのままだけど)、実行されないプログラムといったところです。 誰かが発動させてやらねばなりません。

 ここでは、5.で定義した「カウント0で発動するESP出現コマンド」を、ステージイベントとして設定してみましょう。 ステージイベントとして定義すると、その定義されたコマンドセットは毎フレーム実行されることとなります。

 5.で定義したコマンドセットは、&CountEventで、カウントが0のときのみ実行されるようになっているので、毎フレーム実行がかかったとしても、RequestES`P が実行されるのは、最初の1カウントだけ、ということになります。

#---------------------------------------------------------------------
#  チュートリアル サンプル イベント定義
#---------------------------------------------------------------------

#----------< カウント駆動イベント >----------
#
&AddComSet( INITIALIZE_STAGE )

ステージイベント設定
&AddComSet( INITIALIZE_STAGE )

 毎フレーム、INITIALIZE_STAGE という名前で定義されたコマンドセットを実行するように指定します。
 INITIALIZE_STAGE では、カウントが0のときのみ &RequestESP を実行するように記述しましたので、このイベントは、ステージ開始直後に1度だけ、ESPを出現させる、という意味合いのイベントになります。  


.実行してみよう

 以上で、このサンプルにおける記述は完了です。
 実行してみましょう。

こんな絵、でましたか?
もし、左上にキャラクタが表示されなかったり、あるいはウィンドウ自体が開かなかったりした場合は、何か間違いがあるかもしれません。

 debug.log が生成されている場合は、その中身を見て間違いのあたりをつけることができます。
 もし、「ハンドルされていない云々・・・」とか「不正なメモリアクセス云々・・・」とかいうダイアログが出て強制終了された場合は、*.hrn 記述内の、算術式の記述に何か間違いがある可能性があります。 そのへんであたりをつけて探ってみてください。(必ずそうとは決して言えませんが。)


サンプルスクリプトのダウンロード