SFXVIにおけるポインタの使い道


 ポインタと聞くと身構えてしまう人もいるかと思いますが、それほど難しいことではありません。 確かに、扱いを間違えると大変なことにつながりますが、ポインタそのものの意味は単純なものです。

 ポインタは応用範囲が広く、これだけで本が書けてしまうほどなので SFXVIにおけるポインタの有効利用法を紹介したいと思います。

 ポインタの説明の前に、知っておきたいことがあります。 まずはそちらから説明しましょう。

 変数(静的変数)や関数はプログラム実行の際に、それぞれが一対一に対応したアドレスに割り当てられます。 つまり、変数(関数)名は実行時に唯一のアドレスを示すものとなります。
 例をあげてみます。int Evangelという変数がとあるプログラム中に使われていたとします。 このプログラムは実行時にメモりに読み込まれるわけですが、この時すべての変数、 関数はメモリ中のあるアドレスに置かれます。Evangelが0x200000番地に置かれたとすれば、 プログラム中で使われる変数Evangelはすべて0x200000番地の内容を表すものになります。
printf("%d",Evangel);・・・0x200000番地の内容を表示
Evangel+=20; ・・・・・・・0x200000番地の内容に20を加える
 つまり変数(関数)名の実体は実行時に決定されるアドレスということです。

 いよいよ、ポインタの説明です。ポインタというものは名前の通り「指し示すもの」のことです。
 普通の変数の場合、変数の中身はshort型の数値であったり文字データだったりします。 しかし、ポインタ変数の中身はアドレスを意味する数値です。 先ほどの説明の通り変数、関数はとあるアドレスに置かれるので、 ポインタ変数には変数や関数のメモリ上の位置を格納することが出来ます。
 これがポインタです。

 ポインタに関連する演算子は二つあります。それは「&」「*」です。

「&」アドレス演算子。変数の前にこれをつけることで、その変数のアドレスを得ることが出来ます。 ポインタに変数のアドレスを代入する時などでこの演算子を使います。

「*」間接演算子。ポインタ変数の前にこれをつけることで、 ポインタ変数の保持しているアドレスに格納されている値を参照します。

 さて、ここで注意しなければいけないのですが、ポインタ変数の保持する値は変数の 「アドレス」だけであって、そのアドレスにある変数のデータ型はわかりません。
 そこでポインタ変数定義時に、そのポインタで参照出来るデータ型を定義しなければなりません。 int型で定義されたポインタ変数が指し示すアドレスからは、int型の値しか参照出来ないのです。 データ型は同じである必要があります。


 ポインタ変数の定義、および使用例をあげてみます。

 ポインタ変数の定義方法は普通の変数の定義方法と同じです。ただし、変数の前に「*」をつけます。 通常の変数と同時に定義したり、ポインタの配列を定義することも出来ます。


int	*Daisy,hinagiku;
short	*lily;
FILE	*Nerv;
char	*Ikkokukan[6];
 ポインタ変数はアドレスを代入してはじめて意味をもちます。 アドレスを代入する方法は、説明した通り「&」を使います。 また、定義と同時にアドレスを代入することも出来ます。


	Daisy = &hinagiku;
	lily = &AiTenshi[0];
	Nerv = fopen("人類補完計画.txt","r");

char	*Ikkokukan[6]={
	"Otonashi",
	"Ichinose",
	"Nikaidou",
	"",
	"Yotsuya",
	"Godai",
	"Roppongi"
	};
 ポインタ変数Daisyについては見た通りです。int型の変数hinagikuのアドレスを、 同じくint型のポインタ変数に代入しています。
 次のlilyには配列AiTenshiの先頭要素のアドレスを代入しています。 配列の先頭アドレスに限っては&AiTenshi、AiTenshiという記述も可能です。
 次は関数の戻り値がポインタである例です。 この場合は戻り値がアドレスを意味しているのでアドレス演算子は必要ありません。
 最後のIkkokukanはchar型へのポインタの配列です。これを初期値つきで定義しています。 文字列はアドレスか、と疑問に思うかもしれません。実のところ、文字列の正体はchar型データのまとまりです。 あるアドレスから始まる連続したデータのことです。よって、そのアドレスをポインタに代入出来るというわけです。

 上の様に定義が行なわれていた場合の例をあげてみます。

	hinagiku = 19820505;
	Daisy = &hinagiku;
	i = *Daisy;
 この場合3行目で変数「i」に代入される値は、変数hinagikuの内容です。


	lily = &AiTenshi[1];
	*lily = 707;
 2行目の値は配列AiTenshi[1]に書き込まれます。


	printf("%s",Ikkokukan[i]);
 変数「i」の内容によって表示される文字列が変わります。 i = 4 の場合"Yotsuya"が表示されます。

 SFXVIにおいてポインタはどのように使われているのでしょう? またどのような使い道があるのでしょうか?

 SFXVIのAPIにおいても実は多くの関数でポインタを扱っています。 実のところ、キャラハンドラ(p1、p2など)もポインタです。 APIを利用している限りでは、そう意識することもありませんが。

 セル登録()、攻撃ポイント設定()などでは引き数に「&」をつけて呼び出しますね。 これはデータのアドレスを関数に受け渡しているということです。
 攻撃ポイント設定()と飛び道具威力設定()は性質的にほぼ同じ処理をする関数です。 攻撃情報をポインタ渡しにする場合とそうでない場合の対比が出来ます。 どちらが効率的かどうかはすぐわかると思います。 攻撃ポイント設定()の攻撃情報がポインタ渡しでなかったら大変なことになりますね。
 にもかかわらず飛び道具威力設定()がポインタ渡しでないのは、おそらく使用頻度が 低いことなどが理由なのでしょう。

 動作登録()は関数へのポインタを利用しています。 第二引き数に関数の名前を記述していたと思いますが、関数の名前=アドレスです。

 というように、 キャラ制作で特に意識してはいなくてもポインタを扱っていたというわけです。


 では、それ以外の場面で有効な使い道をあげてみましょう。

 1024キロバイトのPCGデータファイルをたくさん用意しているキャラがいくつかあるようですが、 これはあまり美しいとは言えないでしょう。合計サイズが同じなら、何回にもわけて少しずつ読み込むより、まとめて読み込んだ方が早いのは当然です。 またファイル数がいたずらに増えるだけですしメリットがありません。

 これの解決方法を説明しましょう。まず、PCGデータをひとつのファイルにまとめてセーブします。 このときPCGデータの配置には気をつけましょう。普通PCGデータは8個でひとまとめです。 PCGデータ8個が横一直線に並ぶように置き換えます。 別のPCGデータも同様に、その横に並べます。 EELやSMではPCGデータは横に16個まで置けるので、下のようになります。


	AAAAAAAABBBBBBBB
	CCCCCCCCDDDDDDDD
	EEEEEEEEFFFFFFFF
	・・・

 それぞれ、飛び道具Aで使われるPCG、飛び道具Bで使われる・・・という意味です。

 次にPCGデータを読み込む配列を用意するわけですが、配列の要素数はPCGひとつにつき128バイト、 8個で1024バイトですからそこから計算して必要な大きさにしましょう。


char	飛び道具PCG[1024*4];

 そして、ファイル・ロード()で配列に読み込みます。ここで注意する必要があります。 ファイル・ロード()で読み込めるサイズの上限は16384バイトです。 これ以上のサイズのファイルはCライブラリ関数を使うか、ファイルを分割して、 複数回にわけて読み込むことになります。

例1
ファイル・ロード("pcg2.sp",1024*4,飛び道具PCG);

例2
ファイル・ロード("pcg2.sp",1024*16,飛び道具PCG);
ファイル・ロード("pcg3.sp",1024*10,&飛び道具PCG[1024*16]);

 ここまできたら、必要に応じて新規PCG転送()を実行するだけです。 上の例でいけば飛び道具Bで使うPCGデータは飛び道具PCG[1024]の位置から始まるので、


新規PCG転送(&飛び道具PCG[1024]);
でPCG定義が可能です。


 条件に応じて攻撃情報を換えたいことというのはよくあると思います。 例えば、変身時やサブゲージ値によって換えたい、とかそういうことです。

 簡単に実現するならば、

	if(条件) 攻撃ポイント設定(&hit_A,OFF);
	else	 攻撃ポイント設定(&hit_B,OFF);
または三項演算子を使って、

	攻撃ポイント設定(条件?&hit_A:&hit_B,OFF);

これらの方法では、その攻撃ポイント設定()を実行する時に毎回条件判断を行なうことになります。 サイクルごとの状態を参照して攻撃情報を決定するというのであれば、これは有効な方法と言えますが、 普通はそこまで細かく設定することはないでしょう。 技を出す直前や、技を出した最初のサイクルなどで一回だけチェックすれば済むような場合がほとんどでしょう。
 こんなときはポインタを使うことで簡単に解決します。


HIT_INFO	*hit_tmp;

 とでもポインタをローカル変数として定義します。そして技の最初のサイクルで、 どの攻撃情報を選ぶか決定します。そして、選んだ攻撃情報のアドレスをポインタに代入します。 攻撃ポイント設定()の引き数としてポインタを渡せば完了です。


	if(条件) hit_tmp=&hit_A;
	else     hit_tmp=&hit_B;
あるいは

	hit_tmp=条件?&hit_A:&hit_B;
で、ポインタにアドレスを代入し

	攻撃ポイント設定(hit_tmp,OFF);
hit_tmpの前に「&」がつかないことに注意。 hit_tmpはポインタなので「&」をつけなくてもアドレスを得ることが出来るからです。

 ポインタの利用法は構造体や配列と組み合わせて使うなど、多岐にわたるのですが、 ここまでにしておきます。SFXVIでポインタに関するこれ以上の知識はあまり必要ないと思うので。 よほど特殊なことをしない限りは。

 ものがポインタだけに文章を書くのも大変でした。 はたしてどれくらいの人に理解してもらえるか・・・。

 要望があれば、再び取り上げてみたいと思います。


ひとつ前にもどるトップページにもどるSFXVI Worldに戻る