■ 新・ゲーム開発講座 |
■ へっぽこプログラミング入門♪ |
■第38夜:文字列変数 キャラの名前と言えば、たとえば
…なんてやろうものなら一部の同姓同名キャラのファンから非難轟々になりそうですので、ここはひとつ
とスクリプト上では記載しておき、表示するときに変数の内容によってキャラ名を変更できるようにしておけば便利そうです。そんな次第で、さっそく行ってみましょう。スクリプトの書式としては、以下のようなものでよいでしょう。ソースはこちらです。
|
変数は、ここでは 256 byte 固定長のバッファを16個用意して、それぞれを番号で区別するようにしたいと思います。「256 byteも要らないよ」 「16個じゃ足らない」 という方は適宜ソースを改造してくださいね♪ また変数名のアタマに \ がついていますが、これは「文字列処理系コマンド」 を表すための記号としてくににんが勝手につけた識別子です。文字列変数を使い出すと、人によっては汎用変数だけではなく \ITEM とか \CHR_NAME とか \SPEL とかいろいろ追加したいだろうと思いますので、多少の拡張性(…というほどのものでは ^^;) を考慮したかたちで実装したいと思います。 |
■スクリプトに文字列を挿入するって? ところで、文字変数をスクリプトに挿入する形で表示するというのはどういうことでしょう。本講座で作成中のシステムでは、バッファに読み込んだスクリプト上をポインタ *TEXT が順次なぞるように1文字ずつたどりながら表示していきます。ここに後から余計なテキスト(文字変数)を挿入する訳ですが、さすがに文字変数の記述を発見する毎にいちいちスクリプトを直接書き換えていたのでは面倒です。 このような場合、スクリプト本体を書きかえるようなことは一切しないで、*TEXT を瞬間的にダミーバッファ上で走らせることで文字変数の挿入を実現できます。第28夜のラベルジャンプと似たような処理で、文字変数を格納した配列上に *TEXT を飛ばし、文字変数の終端に達したところで元のスクリプト位置に *TEXT を戻してやるのです。この間、テキストインタプリタの本体ループは *TEXT がどこを走っていようが一切感知することなく、ウェイトをとりながら単純に文字表示を続けています。つまりプレーヤーから見た場合、表示しているのがナマのスクリプト部分でも文字変数の部分でも、画面上の見え方は一緒ということになります。 |
|
今回の処理では、この *TEXT を戻すというところがミソと言えるでしょう。別に難しい処理ではありませんが、そのために内部コマンドとして \EOS (=End of String)というのを用意しました。文字列変数の最後に \EOS の文字列を記述しておき、これを見つけたところで *TEXT を戻す処理をしよう、という算段です。文字列の終端はC言語のキマリで16進数の 0x0 になっているのでここをチェックすれば良いようにも思えますが、ここでは明示的に \EOS を終端マーカーとして使います。 |
■文字列変数の実装 ではさっそく参りましょう。文字列変数はイベントフラグと同じ扱いでフラグ構造体 Mode_stat のメンバとして以下のように実装します(main.h) |
#define EVENT_FLAG_MAX 256 struct _Str256 {
struct _Mode_stat {
extern _Mode_stat Mode_stat; |
わざわざ _Str256 などという構造体を定義しているのは、単純に str[16][256] などとしてしまうと、実際にメモリ上で連続した256byteの領域が確保される保証がないためです。そのためここではまず 256 byte の配列を構造体として宣言して、それを16個ぶん配列として確保する方法をとっています。 |
■#set_str コマンドの実装(文字変数に値をセット) 結構ながくなりそうですので、ここではまず文字列変数をセットする #set_str コマンドを先に解説して、その後 \STR * と \EOS の実装について解説することにします。 #set_str コマンドについては、従来のコマンド追加と要領はまったく同じです。処理の振り分けを行っている Command_call() に、以下のように処理関数を呼ぶ記述を追加します。 |
void Command_call() {
|
個別処理関数 Com_set_str() は、新設した Text_Str_01.cpp
に記述することにします。内容は特に難しいことはありません。コマンドパラメータを解釈して、変数に文字列をコピーしているだけです。注意すべきことは、文字列をコピーした後、終端マーカーとして
\EOS を書きこんでおくことでしょう。文字列の切り出し処理は TextEngine.cpp
に記述してある便利関数を呼んでいます。特に解説はしませんがダブルクォーテーテション「"」で囲まれた文字列の切りだし方なんかが書いてありますので興味の有る方は読んでみてください。 変数に文字列をコピーする部分を別に分けたことにはあまり深い意味はありません。後日、汎用文字変数の \STR * 以外に文字変数の種類を増やしたくなった場合、多少リストが見やすくなるかな…という程度のものです(^^;)。 |
int _set_str( int num, char *str
) //わざわざ分割記述にしたのは将来の拡張を考えてのことです(^^)
int Com_set_str()
} |
■文字列変数 \STR * の処理部分の実装 次に、文字列変数の表示部分の実装です。今回は \ を識別子として処理を分岐しますので、制御文字のチェックを行っている Check_control_chr() に \ の処理を追加します。なおリスト中で \\ と表記してあるのは、\ がエスケープ文字を表す予約語となっているためです(このへんの事情はVCのマニュアルを見てください)。とりあえずこの関数の中では文字変数処理を行う関数 String_call() を呼んでいるだけです。 |
int Check_control_chr() {
|
文字変数の処理を担当する String_call() は、コマンド処理関数 Command_call() と内容的にはほとんど同じ構成になっています。\ をみつけてこの関数に処理が移ったとき、テキストポインタ *TEXT は文字列変数(文字列コマンド)名のアタマ=\ そのものを指しています。そこで TEXT++ して \ を飛ばし、それに続く文字列部分(=変数名/コマンド名)を切り出します。そして切り出した変数名/コマンド名を strcmp() で評価し、該当する個々の処理関数を呼び出します。 今回のソースでは汎用文字列変数 \STR * と 終端処理コマンド \EOS しか実装していませんが、将来ここに別の文字列変数を追加したい場合は Command_csll() を参考にして増設していってください。 |
void String_call() {
} |
■文字列処理(Text_Str_01.cpp) 個別の文字列変数処理については、新規に Text_Str_01.cpp というソースに記述することにします。まず、*TEXT を一時的に飛ばしてしかる後に元に戻す…という処理を行うためには、もとのアドレスを記録しておく変数が必要になります。そこでグローバル変数として *TEXT_ORG を用意します。 \STR * の処理は Str_str() で行っています。やっていることは至極単純で、変数の番号を解析して *TEXT の内容を *TEXT_ORG に保存、その後文字列変数の先頭に飛ばしているだけです。リスト中 Mode_stat.str[num].str -1 という記述がありますが、これは Str_str() の処理が終わってTextEngine のメインループに戻った時、*TEXT が1文字分インクリメントされるのを先取りしたものです。とにかく、これで *TEXT は文字列変数領域の上を走るようになります。 |
unsigned char *TEXT_ORG; //*TEXT の保存用 int Str_str()
} |
また \EOS の処理は、そのまんま *TEXT の値を書き戻すだけです。あれれ、たとえばスクリプトに \STR 02 などと書いてあったら、この \STR 02 の文字数分だけ調整する必要があるんじゃないの…と思った方もいるかも知れませんが、心配は無用です。String_call() や Str_str() でテキスト解釈している間にポインタ *TEXT はインクリメントされて、ちゃんと辻褄は合っているのです(^^) |
int Str_eos() {
|
■実行させてみると… ではコンパイルしてサンプルスクリプトを走らせてみましょう。今回はちょっと猟奇サスペンス調に味付けしたスクリプトです(爆 ^^;)。ああ、ドロ○ジョ様ごめんなさい…♪ |
|
ではまた次回…♪(^^;) |