Squirrel その10

過去ログを見てたらSquirrelの話を終了していないことに気付いた。
なのでやる。今回で最後。これだけわかれば多分ゲームに必要な全ての物を用意できるものと思います。

Squirrelではクラスが使えます。メタメソッドによってC++でいうところのoperatorも使えるので、大変便利です。
特に3Dゲームを作るならベクトルやマトリクスで有効に使えるでしょう。
当然、これらはSquirrelファイル内だけではなく、C++プログラム内からも使えることが望ましい、というか使えないと困ります。
まずはクラスの定義ですが、こちらはSquirrelファイル内で行うのがよいでしょう。
言語仕様でも書きましたが、dofile()を利用すればヘッダファイルのようにインクルードすることが出来るので、よく使うクラスや関数は専用ファイルを作成してそこで定義し、必要なところでdofile()を実行すればよいでしょう。
今回は3Dベクトルが既にインクルードされている状態を想定し、以下の関数をSquirrelファイル内に作成します。

function foo_class(vVec)
{
   
print("x = " + vVec.x + "

y = " + vVec.y + "

z = " + vVec.z + "

");
   
vVec = vVec * 2.0;
   
return vVec;
}

関数としては、入力されたベクトルを表示し、そのベクトルを2倍してから返すというだけのものです。
なお、* のメタメソッドは定義済みということにしてください。
この関数をC++プログラム内から呼ぶのは以前にやった関数の呼び出しと同じ方法でいけます。
ただし、引数はクラスなので引数指定の部分が微妙に異なります。
関数名をsq_pushstring()でプッシュ、sq_get()で関数をフェッチ、sq_pushroottable()でルートテーブルをプッシュするまでは同じということです。
このあと、3DベクトルクラスCVec3インスタンスを作成しなければなりません。

sq_pushstring(v, _SC("CVec3"), -1);
if(SQ_SUCCEEDED(sq_get(v, -2))){
   
sq_pushroottable(v);
   
sq_pushfloat(v, x);
   
sq_pushfloat(v, y);
   
sq_pushfloat(v, z);
   
sq_call(v, 4, SQTrue, SQFalse);
   
sq_remove(v, -2);
}

まずクラス名をプッシュします。そして、ルートテーブルからクラスをフェッチします。。
この段階ではクラスの定義であってインスタンスではありません。インスタンスを作成する必要があります。
インスタンスを作成するにはコンストラクタを動かす必要があります。コンストラクタは普通の関数と同じように実行できます。
まず、ルートテーブルをプッシュし、そこから実際に使用する引数をプッシュします。
CVec3クラスはx,y,z3つの浮動小数点数を引数としますので、これらをプッシュします。
そして、sq_call()命令でCVec3のコンストラクタを実行します。
これでC++newと同じ事が起こります。インスタンスが作成され、そのコンストラクタが実行されます。
コンストラクタ内にprint()命令でも入れておけばコンストラクタが実行されているのがわかるかと思います。
最後のsq_remove()はフェッチしたCVec3を削除しているだけです。
コンストラクタ実行後、スタックトップ(-1)にはCVec3インスタンスが、その下のスタック(-2)にはCVec3のクラスがあるからです。
この段階でスタックトップから順番に、CVec3インスタンス、ルートテーブル、foo_classのアドレスが入っている状態になります。
即ち、関数を実行できる状態にあるというわけですので、sq_call()foo_class()を実行します。
最後にfoo_class()関数からの戻り値を取得します。戻り値もCVec3インスタンスです。

if(sq_gettype(v, -1) == OT_INSTANCE){
   
float x;

   
sq_pushstring(v, "x", -1);
   
sq_get(v, -2);
   
sq_getfloat(v, -1, &x);
   
sq_pop(v, 1);
}

スタックトップにある物がインスタンスかどうかをチェックします。OT_CLASSだとクラスの定義になってしまうのでOT_INSTANCEを使用します。
インスタンスならそのインスタンスからx,y,zの値を取得します。
上記のプログラムではxしか取得していませんが、y,zについても同様の方法で取得できます。
まず、取得したいメンバの名前をプッシュします。その中身をインスタンスから取得します。取得したら必要がないのでポップします。
取得した値は煮るなり焼くなり好きに使って、普通の関数実行と同じように終了します。
ベクトルのようによく使うクラスについては引数としての設定、戻り値の取得の関数を作成しておくと便利でしょう。メソッドでもいいかもしれませんね。

さて、最後に。
以前のトラックバックSquirrelは複雑で、プログラマが使うにはいいけど企画やデザイナが使うにはちょっと問題が多いというものがありました。
これは全くの事実で、Squirrelプログラマ以外が積極的に使うということはほぼありえないでしょう(企画がプログラマ出身なら別だが)。
しかし、これはSquirrelに限りません。Luaでやろうが、もっと簡単なスクリプトでやろうが企画やデザイナには荷が重いのはほとんどの場合同じです。
プログラム言語の要素をもったものを使う場合、複雑な部分はプログラマが、それ以外の人は単純な部分(例えば数値の変更や必要な部分への組み込み程度)を行うようにした方が効率は良くなります。
逆に複雑な部分を企画等にやらせると、後でわけのわからないプログラムをデバッグさせられることになります。
Epic Gamesの人も言っていましたが、役割分担さえきちんと出来ていればSquirrelで問題ありません。