9-Laboratory

RGBA⇔float2

04.11.2009 (12:12 pm) – Filed under: temp ::

何かに需要があるかもしれないので、書く。
HLSL上でfloat2⇔float4変換を行う。float2もfloat4も要素の精度はどちらも32bit。しかしfloat4はR8G8B8A8形式に落とすと、結局8bit精度しかない。float2をいい感じにfloat4の各要素に分散させてfloat2の精度を16bitに保たせるのが目標。やりかたとしては、8bit精度での丸め誤差分をもう一つの要素に格納させる。

/**
float2をRGBAに変換する。
float2.x ⇒ float4.x,float4.y,  float2.y ⇒ float4.z,float4.w
*/
float4 F2ToRGBA( float2 value )
{
	float4 result;
	result.yw = modf( value * 256.0f, result.xz );
	result.xz *= 0.00390625f;
	return result;
}
/**
RGBAをfloat2に戻す
float4.x,float4.y ⇒ float2.x,  float4.z,float4.w ⇒ float2.y
*/
float2 RGBAToF2( float4 value )
{
	return float2(
			value.x + value.y * 0.00390625f
			value.z + value.w * 0.00390625f );
}

※float2の要素は[0.0f,1.0f]であることを仮定している。
※本当に16bit分あるのかはよくわからないけど。

#G16R16Fとか、使えるんならそれを使うに越したことないけれど。

追記:0.5fを超えると動かないみたいです。ちょっと原因がわかりません….orz

091019小言。

19.10.2009 (5:04 am) – Filed under: temp ::

61jTjn8cQHL._SS400_

スタンディングオベーション。最高だった。
アンチャーテッド 黄金刀と消えた船団」をクリアしました。
プレイする映画との触れ込みだったのだけれど、全く嘘ではなかった。とにかく手が込んでいて、やろうと思えばこのレベルのことまでもうできるようになっているのだと、感動した(どこのスタジオでもこれができるようになる必要もないと思うけれど)。あまり沢山のことは書かないようにしよう。購入を迷っているならば、何も考えずに買うべきだと思う。さすが20満点中21点を取るだけある。

○良かったところ
・物量の語意が変わるくらいの物量。
・どこまでも深い作りこみ。そんなところまで誰が見るんだろう?
・プレイ中は全くないロード。
・カットシーンとプレイの境界のあいまいさ。
・洒落た言い回し。

○悪かったところ
・若干長すぎた。初回クリアに12時間かかった。面白いけれど、長い映画だと思えば。
・視認性の問題で次何をすればいいのか迷ったところが数箇所あった。
・一回だけオートセーブ時にフリーズした。
・twitter機能が動かなかった。一回しかサインインにチャレンジしていないので、たまたまタイミングが悪かっただけかもしれない。
・平地でのジャンプがダサかった。

この手の超大作ゲームは年に一回以上やると食傷気味になりそうだけど、こーゆーのなら、毎月位のペースならやりたい。

どうでもいいが、Amazonで初回を買ったんだけれど、黄金銃ってどうやって手に入れるの?

AngelScript4

15.10.2009 (11:41 pm) – Filed under: temp ::

angelscript-88x31

今回はバインドについて。書いてみて思った以上の分量になってしまったが、一回やってしまえば後はコピペで済む問題なので気楽に読むといいと思う。

0.バインドを始める前に。
AngelCodeでは、バインドをする際、そのプロトタイプや、型は全て文字列で指定する。これによって変な問題(いつも、イライラさせてくれるtemplate絡みの問題とか。)がかなり解消されている。一方で、他のバインダではコンパイル時に判明するような非常に(程度が低いという意味での)低レベルな問題も実行時にならないとわからない。そのため、AngelScriptはバインドコードの戻り値を毎回assertにかけることを推奨している。

r = asEngine->RegisterObjectMethod("ISomeObj", "void Func( )", asFUNCTION(Bind::Func),asCALL_CDECL_OBJLAST); assert( r >= 0 );

本当にどうでもいい部分で、登録が失敗したりするのでバインドコードの後ろには必ずassertを入れたほうがいいと思う。
#ここら辺は動的言語 vs 静的言語的な宗教戦争とかでよくある、その人の嗜好に寄る部分だと思う。
#多少コンパイル時間がかかってでも、「コンパイルタイムは正義!」で、
#実行する前に問題を全部洗い出すようなものが好きな人は、
#文字列でプロトタイプを渡したり、ユーザ定義クラスの引数はvoid*だったり
#そもそもバインドされたクラスメンバの型のチェックがなかったりする
#AngelScriptは?かもしれない。
#個人的には、C++側の型チェックはルーズ、
#スクリプトの型チェックはそれなりに厳密なスタンスはすごく好み。

1.グローバル関数
何も考える必要はない。次のように行う。

r = asEngine->RegisterGlobalFunction("bool Func()", asFUNCTIONPR(Func, void, bool), asCALL_CDECL); assert( r >= 0 );

ただ型が、ユーザー定義の場合はその型がバインドされた後でないとエラーが帰るので、順番としては、クラス型の登録が全て終わったあとにやるといいかもしれない。余談だが、実はOverloadをサポートしている。次のように、引数が違う二つの関数を登録してもちゃんと動く。

r = asEngine->RegisterGlobalFunction("bool Func()", asFUNCTIONPR(Func, void, bool), asCALL_CDECL); assert( r >= 0 );
r = asEngine->RegisterGlobalFunction("bool Func(int)", asFUNCTIONPR(Func, int, bool), asCALL_CDECL); assert( r >= 0 );

#id softwareのコード規約にもあるように、個人的には、
#オーバーロードをあまり好ましいと思っていないが。

2.グローバル変数
グローバル変数に、C/C++の変数を置くことができる。

r = asEngine->RegisterGlobalProperty("int g_SomeNum", &g_SomeNum); assert( r >= 0 );

ユーザ定義型もおくことができる。

r = asEngine->RegisterGlobalProperty("SomeObj @g_SomeObj", &g_SomeObj ); assert( r >= 0 );

#このユーザ定義型の配置はあまり上手くいかない場合がある。(単なるミスかもしれないが)
#それに、個人的にあまり好きではないので私は利用していない。

3.クラス、構造体の登録
クラスのメンバ関数や、メンバ変数を登録する前に、クラス、構造体をまず型として登録する。メンバ関数、メンバ変数にその型がでてくるような場合、事前にその型を登録していないと、エラーになるため先に全てのバインドする型を登録し、その後にメンバ関数、メンバ変数をしたほうがいいと思う。クラス、構造体の登録には値型として登録する方法と、参照として登録する方法がある。

3.1 値型としてのクラス、構造体の登録
スクリプト内で値として生成できるようなクラス、構造体 ~ベクトルクラス、行列クラスなど~ は、以下のようにその型を登録する。

/*Vec2の登録*/
class Binds
{
public:
	static void Constructor(void *memory)
	{
		new(memory) Vec2();
	}
	static void Constructor2(float x, float y, Vec2 *self )
	{
		new(self) Vec2(x,y);
	}
	static void CopyConstructor(const Vec2 &other, Vec2 *self)
	{
		new(self) Vec2(other);
	}
	static void Destructor(void *memory)
	{
		((Vec2*)memory)->~Vec2();
	}
};
r = engine->RegisterObjectType("Vec2", sizeof(Vec2), asOBJ_VALUE); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("Vec2", asBEHAVE_CONSTRUCT, "void f()",
   asFUNCTION(Binds::Constructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("Vec2", asBEHAVE_CONSTRUCT,  "void f(float, float)",
    asFUNCTION(Binds::Constructor2), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("Vec2", asBEHAVE_CONSTRUCT,  "void f(const Vec2 &in)",
    asFUNCTION(Binds::CopyConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("Vec2", asBEHAVE_DESTRUCT, "void f()",
    asFUNCTION(Binds::Destructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );

最低限、オブジェクトのサイズ、コンストラクタ、デストラクタを登録しないとエラーが発生する。特にこだわりがない場合は、コピーコンストラクタなど必要なものも登録する(クラス関数を使ったのは、グローバル関数を書いてコードを汚したくないので、ローカル関数の立替として書いただけなので、特に他意はない)。このように登録した型はAngelScript内で次のように普通に生成することができる。

Vec2 value(0.0f,0.0f);

3.2 参照型としてのクラスの登録
スクリプト内では生成される事を意図しないクラス、構造体のインターフェイス系は 値型として登録せず、参照型として登録する。AddRef、Releaseが登録されていることからわかるように、参照型は、その参照カウンタを増減させる方法を提供しないとならない。これは、スクリプトとC/C++をまたぐインスタンスの管理を一元的に行うため、このようになっている。他のスクリプトが結構うやむやにしている点ではあるので、きっちりかっちり行われるのは、大変ありがたい。これでC/C++でもスクリプトでもどっちでも利用されるインスタンスに関して一元的な管理が可能になる。AddRef(),Release()の登録は、メンバ関数の登録の項でとりあげるべきだったが、参照型として登録する以上この関数がないとエラー(それも非常に判明しづらい)でスクリプトが動かないので、クラスの登録と同時にやるといいと思う。

r = asEngine->RegisterObjectType("ISomeObj", 0, asOBJ_REF); assert( r >= 0 );
r = asEngine->RegisterObjectBehaviour("ISomeObj", asBEHAVE_ADDREF, "void f()",
     asMETHOD(ISomeObj,AddRef), asCALL_THISCALL); assert( r >= 0 );
r = asEngine->RegisterObjectBehaviour("ISomeObj", asBEHAVE_RELEASE, "void f()",
     asMETHOD(ISomeObj,Release), asCALL_THISCALL); assert( r >= 0 );

asMETHODマクロはメンバ関数に対して、asFUNCTIONは普通の関数(グローバル関数、クラス関数)に対して利用する。フックとして、asFUNCTIONを利用することも可能だ。

4. プロパティの登録
プロパティは次のように登録する。

r = asEngine->RegisterObjectProperty("Vec2", "float x_", offsetof(Vec2, x_ )); assert( r >= 0 );

ここで気をつけないといけないのが、プロパティに参照型がある場合、それがNULLになっていはいけないということだ。そうでないとアクセス時(単にそれに代入されるだけでも)に、エラーが出る。NullObjectをデフォルトで設定するなどの工夫が必要だが、非常に面倒なので、このような場合、私はわざわざ追加でメンバ関数を作成している。

/*これはtex_にアクセス時にnullとなっているとエラーとなってしまうのでお勧めしない。*/
//r = asEngine->RegisterObjectProperty("SpriteRenderDesc", "ITexture@ tex_", offsetof(SpriteRenderDesc, tex_ )); assert( r >= 0 );
/*多少不恰好だが、tex_ == NULL でも大丈夫なようにこうする。*/
r = asEngine->RegisterObjectMethod("SpriteRenderDesc", "void SetTexture( ITexture@  )", asFUNCTION(Bind::SetTexture), asCALL_CDECL_OBJLAST); assert( r >= 0 );

5. クラス、構造体のメンバ関数の登録
5.1 そのままメンバ関数を登録する場合
次のようにする。

r = asEngine->RegisterObjectMethod("SceneGraph", "void AddSprite( const SpriteRenderDesc &in )", asMETHODPR(SceneGraph, AddSprite,(const SpriteRenderDesc&), void ), asCALL_THISCALL); assert( r >= 0 );

しかし仮想関数の登録で書くように、実はメンバ関数呼び出しを関数呼び出しに置換することが可能なので、使い分けるのが面倒ならメンバ関数のバインドは5.2の方法で統一しても問題ない。ただ呼び出しのオーバーヘッドが問題になるようなレベルになるなら、直接呼び出すような形にしたほうがいいと思う。

5.2 仮想関数の登録
知らないときはかなりはまったのだが、AngelScriptには、バインドしたクラスの仮想関数の呼び出し先を解決する方法はない。実行時に失敗する。ので、一度呼び出す前のフックを作成する必要がある。これは仮想関数でない場合も利用でき、何か別の処理を噛ませたり(ちゃんと解放されているかなど確認するなど)することが可能だ。

class Bind
{
public:
	static void SetTexture( ITexture* texture, SpriteRenderDesc* desc )
	{
		texture->Release();
		desc->SetTexture( texture );
	}
};
r = asEngine->RegisterObjectMethod("SpriteRenderDesc", "void SetTexture( ITexture@  )", asFUNCTION(Bind::SetTexture), asCALL_CDECL_OBJLAST); assert( r >= 0 );

#参照オブジェクトを作る時点で仮想関数があることはわかりきっていることなのに、
#いちいちフックを作るようにした意義がわからないが、多分色んな事を含めて
#こうしたほうがいいという結論に達したんだろう。多分。

6. @,+@,&in,&out,&inout
まずは &in,&out,&inoutについて。これらは、引数への修飾子(?)として用いることができる。ほとんど見たまんまだが、&inはその参照を参照のみすることを、&outはその参照を参照せず外部への戻り値として利用することを、&inoutはその両方を表現する。
そしてもうひとつ用意されているのが@。これが若干厄介だ。関数の引数として@が出てきたときの動作についてちゃんと理解していないと、バインドした関数を呼び出すたびに参照カウントが増えつづけ、解放できなくなってしまったりReleaseを呼びまくったりしてしまう。考え方としては、@がつくとカウントが増え/増えることが期待され、その参照がスクリプトからアクセスできなくなったら参照が減るということだ。たとえばC関数

void SomeFunc(ISomeObj*)

void SomeFunc(ISomeObj@)

としてバインドする。ISomeObjは参照型として登録されている。このときAS側からSomeFunc(someObj)として呼び出すとすると、C側のSomeFunc(ISomeObj*)では参照カウンタがひとつ増えている。参照カウンタの管理をCのSomeFunc()側で行っている場合は要注意だ。その場合は、一つ下げないといけない。これは5.2の例でやっているようにフックの中身でやるといい。ドキュメントにあった例題を沢山貼り付けておく。

/*"obj@ CreateObject()"として登録した場合*/
obj *CreateObject()
{
  /*(まともな実装なら)参照カウントがC/C++側もここで1になっているので問題ない*/
  return new obj();
}
/*"void StoreObject(obj@)"として登録*/
obj *o = 0;
void StoreObject(obj *newO)
{
  if( o ) o->Release();

  /*StoreObjectが呼び出された時点で、AngelScriptによって
   AddRefされているのでここでAddRefする必要はない。*/
  o = newO;
}
/*"obj@ RetrieveObject()"として登録*/
obj *RetrieveObject()
{
  /*@付きで返すためAddRefしておくことが期待される*/
  if( o ) o->AddRef();

  return o;
}
/*"void DoSomething(obj@)"として登録*/
void DoSomething(obj *o)
{
  /*@付きなので、事前にAddRef()されているのでRelease()する*/
  if( o ) o->Release();
}

そしてさらに@+というのがある。例えば次のような状況を考える。

/*"obj@ ChooseObj(obj@, obj@)"として登録*/
obj *ChooseObj(obj *a, obj *b)
{
  return some_condition ? a : b;
}

これはaかbがリークする危険がある。しかしこのような場合も

/*"obj@+ ChooseObj(obj@+, obj@+)"として登録*/
obj *ChooseObj(obj *a, obj *b)
{
  return some_condition ? a : b;
}

とすれば、AngelScriptが参照を管理してくれるらしい。ちょっと怖い上に、動作に多少のオーバーヘッドがあるようで使いたいと思えない。

7. GCについて
バインドとは少し変わってしまうが、自分で@の動作などを確認するときにGCの動作を把握していなかったためにはまったのでここにあわせて書く。GC用のインターフェイスはLuaなどと違いかなり簡素だが、利用者がガーベッジの回収も含めて全部ユーザーが指定しないといけない。GCの動作は、asEGCFlagsのasGC_ONE_STEP,asGC_FULL_CYCLE,asGC_DESTROY_GARBAGE,asGC_DETECT_GARBAGEをasIScriptEngine::GarbageCollectに指定して行う。ここで注意しないといけないのが、asGC_ONE_STEPは(単なるRCから発生したのも含む)ガーベッジの回収自体はやってくれないということだ。

/*ガーベッジの回収は行わない。*/
asEngine_->GarbageCollect( asGC_ONE_STEP );

シーン転換時、ローディング時にasGC_FULL_CYCLEで全て回収し、ちょっと暇ができた時に( asGC_FULL_CYCLE | asGC_DESTROY_GARBAGE )でガーベッジを回収し、通常は毎フレームasGC_ONE_STEPを呼ぶのがいいと思う。

8.enumについて
スクリプトのバインドでしばし話題に出るenumのバインド。残念ながらAngelScriptでは型安全に直接バインドする方法はない。intにキャストしてグローバルにconst int として配置するしかない(とても残念!)。わざわざそんなことするくらいなら、スクリプト側に最初から定義したほうが幾分か楽であるので、私はそうしている。

だいぶ長くなってしまったが、一回要領をつかめば多分大したことない。グルーコードのジェネレーターもささっと作れる人なら比較的容易に作れるだろう。ここまでのエントリでゲーム組み込み用途として必要な部分をほとんどカバーしたのではないだろうか?(Luaで同じことを達成しようとすると。。。) AngelScriptのイージーな感じが伝わっているとうれしい。

次回は、動的リロードと最後の落ち葉拾いなどをしようと思う。

091010小言。

10.10.2009 (9:44 pm) – Filed under: temp ::

何の変哲もないゴミ箱。でもごみを捨ててみると。。。。

何の変哲もない階段。でも歩いてみると。。。。

人の行動を(多分いい方向に)変えるにはその行動を面白い事に変えちゃばいいんじゃないの?これを”fun theory”と呼んで、実際に実験した様子がthefuntheory.comにあげられています。まだ数は少なくて上の二つしかないんだけれども。。11/15にはこの「the fun theory」のコンペティションがあって優勝すると、2500ユーロもらえるみたい。

thefuntheory.com

#「飽きたら終わりだろ」とか夢のないこと言わない。

091003小言。

03.10.2009 (4:57 pm) – Filed under: temp ::

happy
チラシの裏。Happy Buddhaのmqoファイルを最短距離で手に入れる方法。

1.The Stanford 3D Scanning RepositoryのHappy Buddhaの項目のhappy_recon.tar.gzを落とす。
2.http://www.bee-www.com/log.htmの6.23.05の日記にあるply2mqo2.zipを落とす。
3.ply2mqo.exeのフォルダにブッタのplyを入れて、ply2mqo happy_vrip_res2.plyと書いたバッチファイルを用意し、バッチを実行。
4.メタセコ上で面を反転させる。

AngelScript 3

29.09.2009 (12:47 am) – Filed under: temp ::

angelscript-88x31

少し間が空いてしまったのだけれど、AngelScript三回目。
今回も、言語側のことについて。クラスについて書く。

クラス宣言
C++を使ってきた人にとっては、ほとんど何も意識することなくクラスを宣言することができる。

/**/
class Hoge
{
	Hoge()
	{
		Print("Hoge::Hoge()");
	}
	~Hoge()
	{
		Print("Hoge::~Hoge()");
	}
	int value_;
}

コンストラクタ、デストラクタはC++と同じくクラス名となっている。アクセス制御子はない。全てpublicになっている。(後述するように、アクセッサは作ることができるので命名規約など作って保護することは可能。)

初期化
初期化方法には次のようなものがある。

/*デフォルトコンストラクタが呼ばれる*/
Hoge h0;
/*明示的にデフォルトコンストラクタを呼び出す場合*/
Hoge h1();
/*コンストラクタもオーバーロード可能*/
Hoge h2(10);

オブジェクトハンドル
AngelScriptは、限りなくシンタックスがC/C++に近いが、ポインタは意図的にサポートしていない。変わりにオブジェクトハンドルをサポートしている。shared_ptrに似ている。

Hoge h0;
/*h1にh0を参照させる。*/
Hoge@ h1 = h0;
/*isで、等価性を判定できる。*/
h2 is h1 ? Print("h2==h1") : Print("h2 != h1");
/*C++の参照と違い、ハンドルだけ宣言する事も可能。*/
Hoge@ h2;

ハンドルは参照先と参照元のライフタイムが異なるときに威力を発揮する。次の例では、参照元がスコープから抜けてしまっているが、スコープ外の参照に参照されているので、依然として参照元が残っている。これは参照する際、そのインスタンスのAddRefが呼ばれることで実現している。そしてこれのメソッドは、C/C++にバインドする際にカスタマイズすることもできる。またハンドルは、C/C++と通信する際もかなり重要な要素になる。この二つについては、多分バインドの回で詳しく。

Hoge@ h0;
{
	Hoge h1;
	@h0 = @h1;/*ここでHogeの参照がインクリメントされる*/
}
h0;/*これはh1を参照しており、実際にh1はまだ生きている。*/

循環参照はGCが呼ばれるか、asIScriptContextが解放されるまで解決しない。

/**/
class A
{
	A( )
	{
		Print("A::A");
	}
	~A()
	{
		Print("A::~A");
	}
	void SetB( B@ b)
	{
		@b_ = @b;
	}
	B@ b_;
}
/**/
class B
{
	B()
	{
		Print("B::B");
	}
	~B()
	{
		Print("B::~B");
	}
	void SetA( A@ a )
	{
		@a_ = @a;
	}
	A@ a_;
}
/**/
void test()
{
	Print("Start Scope");
	{
		A@ a = A();
		B@ b = B();
		a.SetB( b );
		b.SetA( a );
		/*
		残念ながらここで、a,bは解放はされない。
		GCを呼ぶか、asIScriptContextを解放しないといけない
		*/
	}
	Print("End Scope");
}

継承
継承ができる。

class Foo : Hoge
{
	Foo()
	{
		Print("Foo::Foo()");
	}
	~Foo()
	{
		Print("Foo::~Foo()");
	}
	void Func()
	{
		Print("Foo::Func()");
	}
}

継承関係にあるものはキャストを行うことができる。キャストに失敗すると例外が呼ばれる。(asEXECUTION_EXCEPTIONが帰る。)

Foo f;
Hoge@ h = f;
Foo@ f2 = cast<Foo>( h );

また、全てのメソッドはオーバーライド可能。オーバーライドの際、特別何かを宣言する必要もない。

f2.Func();/*Hoge::Func()ではなく、Foo::Func()が呼ばれる*/

インターフェイス
継承ができるのでそれでも十分だが、インターフェイスを明示的に作りたい場合はinterfaceを宣言できる。

interface IHuman
{
	void Walk();
}
class Bob : IHuman
{
	void Walk()
	{
		Print("Bob::Walk");
	}
}

オペレーター
オペーターオーバーロードは、指定の関数名をオーバーロードすることによって実現する。オーバーロードできる演算子は次のとおり。というかこぴぺ。

○単項演算子
演算子 相当する関数
- opNeg
~ opCom

○比較演算子
演算子 相当する関数
== opEquals
!= opEquals
< opCmp
<= opCmp
> opCmp
>= opCmp

○代入演算子
演算子 相当する関数
= opAssign
+= opAddAssign
-= opSubAssign
*= opMulAssign
/= opDivAssign
%= opModAssign
&= opAndAssign
|= opOrAssign
^= opXorAssign
< <= opShlAssign
>>= opShrAssign
>>>= opUShrAssign

○二項演算子
演算子 相当する関数 二項の順番が逆の時に呼ばれる関数
+ opAdd opAdd_r
- opSub opSub_r
* opMul opMul_r
/ opDiv opDiv_r
% opMod opMod_r
& opAnd opAnd_r
| opOr opOr_r
^ opXor opXor_r
< < opShl opShl_r
>> opShr opShr_r
>>> opUShr opUShr_r
次のように使う。

class Vec3
{
	Vec3( float x, float y, float z )
	{
		x_ = x;
		y_ = y;
		z_ = z;
	}
	/*+*/
	Vec3@ opAdd(const Vec3 &in rhs)
	{
		return Vec3( x_ + rhs.x_, y_ + rhs.y_, z_ + rhs.z_ );
	}
	/* += */
	Vec3@ opAddAssign( const Vec3 &in rhs )
	{
		x_ += rhs.x_;
		y_ += rhs.y_;
		z_ += rhs.z_;
		return this;
	}
	float x_;
	float y_;
	float z_;
}
void test()
{
	Vec3 v1(0.0f,1.0f,2.0f);
	Vec3 v2(1.0f,3.0f,0.0f);
	Vec3 v3 = v1 + v2;
}

プロパティアクセッサ
get_***,set_***のような関数は、***に相当するプロパティのgetter,setterになる。

class Human
{
	/*名前の設定*/
	void set_name( string name )
	{
		strValue_ = name;
	}
	/**/
	string get_name() const
	{
		/*デバッグ用にいつだってアリス*/
		return "Alice";
		//return strValue_;
	}
	string strValue_;
}
/**/
void test()
{
    Human human;
    human.name = "Bob";
    Print( human.name );
}

ふう。これでクラス周りは一通り回ったと思う。何かあったらクラス関連のことは、このエントリを適宜修正、加筆する。

今のところ、弱点らしい弱点(妙な構文や変な制限)がない気がする。マジで広める価値はあるのかもしれない。

090922小言。

22.09.2009 (6:57 pm) – Filed under: temp ::

Shapewaysは、自分で作ったモデルを実際の”モノ”にしてくれるサービス。自分で作ったモデルをSTL、VRML、Collada、X3DColladaのどれかのフォーマットにコンバートして、Shapewaysのサイトにアップ、材料を選んで入金するだけ。この4Stepであなたのモデルが実際に手に取れる形に。材質も、いろいろ選べるみたいで、ゴムのようなもの(?)から、陶磁器風(?)なものまでいろいろあるみたい。

これが実際に総計が幾らかよくわからなかったけれど、モデルデータを頒布しているサークルに宣伝すれば、何か新しいジャンルとかできそう。

AngelScript 2

22.09.2009 (4:10 pm) – Filed under: temp ::

angelscript-88x31

前回に引き続きAngelScriptを。
今回はクラスのバインドなどをするつもりだったのだけれど、調べてみると(サンプルがツンツンしている割に)かなり多機能だということが判明したので、じっくり回数を重ねていろいろ機能を見ていきたいと思う。ので今回は基本型周辺、制御文、あとクラスのかじりをやってみる。スクリプト内だけで完結する話なので、下のスクリプトを前回のサンプルのconst char* scriptString = ”ほげほげ”の”ほげほげ”の部分に置き換えると多分動く。説明よりも動くコードのほうが説得力があると思うので早速スクリプトを。

/*グローバル変数*/
int 	g_Int = 128;			/*普通にグローバル宣言*/
const 	int g_constInt = 128;	/*const指定が使える。*/
int8 	g_someInt8 = 0;			/*8bit。intも詳細に選べる*/
int16	g_someInt16 = 0;		/*16bit。*/
int		g_someInt32 = 0;		/*32bit*/
int64	g_someInt64 =0;			/*64bit*/
int		g_notInitInt;			/*初期化しなくても0に。これは単にnew int()が0だから?*/
int		g_hex	= 0xff;			/*16進数対応*/
float	g_someFloat	= 3.1415926535f; /*floatのサフィックスをつけられる!!*/
double	g_someDouble = 3.1415926535897932384626; /*floatとdoubleが別!!*/
string	g_someStr = "GlobalString";	/*インスタンスもグローバルにおくことが可能*/

/*typedefも使える。今のバージョンはプリミティブ型のみだが、将来的には対応するらしい。*/
typedef float float32;
//typedef oreoreString string; /*今のバージョンではエラー*/

/*enumが使える*/
enum SomeEnum_t
{
	SomeEnum_State1,
	SomeEnum_State2 = 3,/*明示的に数値を代入する事も可能。その他の型は不可*/
	SomeEnum_State3,
	SomeEnum_State4,	/*最後項目にも","をつけることも可能!!*/
}

/*クラス。細かいことは次回以降*/
 class Hoge
{
	/*コンストラクタがクラス名!!*/
	Hoge()
	{
		Print("Hoge::Hoge()");
	}
	/*デストラクタがある!!*/
	~Hoge()
	{
		Print("Hoge::~Hoge()");
	}
	/**/
	void Func()
	{
     	Print("DoSomething");
     	this.Func2();			/*thisがある!!*/
	}
	void Func2()
	{
		Print("Hoge::Func2()");
	}
}/*";"で閉じなくていい!!*/
Hoge g_hoge;

void hellow()
{
	Print("----Global Variable Test----");
	Print( "g_Int is " + g_Int );
	g_Int += 128; 								/*constがついていないので変更可能*/
	Print( "g_Int is " + g_Int );
	Print( "g_constInt is " + g_constInt );
	//g_constInt += 128; 						/*エラー。constは変更ができない。*/
	Print( "g_notInitInt is " + g_notInitInt );	/*初期化しないと0に。単にnew int()が0だから?*/
	Print( "g_someStr is "+g_someStr );			/*グローバルにインスタンスをおくことも可能*/

	Print("----Typedef Test----");
	float32 floatValue = 2.71828f;
	Print( "floatValue is " + floatValue );

	Print("----Enum Test----");
	int localInt1 = SomeEnum_State1; /*enumはintと互換*/
	int localInt2 = SomeEnum_State2;
	Print(""+localInt2);
	Print("");

	Print("----制御文Test----");
	bool someCondtion = false;
 	if( someCondtion )
 	{
  	}
	else
	{
		Print("if else");
  	}
  	SomeEnum_t someState = SomeEnum_State3;
  	switch( someState )
  	{
  	case SomeEnum_State1:
  		Print("State1");
  		break;
  	case SomeEnum_State2:
  		Print("State2");
  		break;
  	case SomeEnum_State3:
  		Print("State3");
  		//break; /*フォールスルーとかも可能。(微妙。。。)*/
  	default:
  		Print("Default");
  		break;
  	}
  	/*whileはもちろん do whileだってできちゃう*/
  	int whileCnt = 0;
  	while( ++whileCnt < 10 ){}
  	do{}while( --whileCnt > 0 );
  	/*breakとかcontinueとかもいけちゃう*/
  	while(true)
  	{break;}
  	for(int i=0;i<10;++i)
  	{
  		if( i % 2 != 0 )
  			continue;
  	}

  	Print("---ScopeTest---");
  	int localInt = 10;
  	{
  		int localInt = 20;
  		Print(""+localInt); /*Cと同じようにスコープをちゃんとみる。*/
  	}

	Print("----Class Test----");
	/*クラステスト。細かい話は次回以降。*/
	Hoge hoge; /*普通に生成*/
	hoge.Func();/*"."でメソッド呼び出し*/
	g_hoge = hoge; /*これをしない場合はスコープを出たら自動的に解放。上書きされたHogeは~Hogeが呼ばれる。*/

	Print("---End Hellow()----");
}

どう見てもC/C++です。
裏を返すとかなり高機能だということ。上にあるのをかいつまんで箇条書きにすると、

・数値はint,floatだけではなく、8bit整数から64bit整数、unsigned、doubleまで。
・floatのfなどのサフィックスが使える。
・16進数が使える。
・typedefが(今のところ限定的だが)使える。
・enumが使える。そしてこれは整数にキャスト可能。
・if elseはもちろんのこと、switch,while,do whileまで全部あり。break,continueもちゃんとある。
・switchのフォールスル-とかもちゃっかり動く。
・スコープの概念がちゃんとある。
・クラスをちゃんとサポート。
・コンストラクタの名前がクラス名
・デストラクタがちゃんとある。

やはり構文が近いと親しみがわきやすい。後になってC/C++に移植することになってもかなり少ない手間で移植ができるのではないでしょうか?(近すぎる分、「何かあったとき」は大変な目にあいそうな気も。。。)

今回はあまりまとまりがなかったなぁ。。。次回は(スクリプト側の)クラス周りをやろうか。

AngelScript 1

22.09.2009 (1:29 am) – Filed under: temp ::

angelscript-88x31

AngelScriptという組み込み用途のスクリプト言語を弄ってて、とても素晴らしいと思ったのだけれど、(国内では)マイナーなのかあまり目ぼしい資料が見当たらなかったので数回に分けてブログ記事にしてみる。(全ゲ連でSquirrelをやった直後にこれというのはなんともという感じだが。。。)

AngelScript(正式名称「AngelCode Scripting Library」以下AS)とは、Andreas J?nsson氏が2003年あたりから(※1) 開発している(恐らく)ゲーム用途(※2)の組み込みスクリプト。まずはフューチャーというか自分的うれしい点を列挙してみる。

・静的型付!!!
スタックでごにょごにょやならなくていい!!
・開発頻度が今超Hot!
・C/C++ライクな構文。クラスは当たり前のようにあり、名前空間(Module)がサポートされている。
・当たり前のようにintとfloatは別
・バインダが付属
・インターフェイスがC++。(Cも選べる)
・コルーチン
・スレッドセーフ
・参照カウンタと、GC。循環参照時のみGC(?)
・かなり自由な値のC/C++⇔ASのやりとり。
・デバッガ用インターフェイスが親切(??)

やはり一番目を引くのは静的に型付けできる点。他の組み込みスクリプトで有力なLuaSquirrel、そしてxtalは全て動的型付け。ObjectiveCに触れて多少は考えが変わったけれどそれでもやはりできるならば静的に型をつけてくれるとうれしい。動的なばかりに付け足されるテストや、保守に気を張らないといけないというのはどうも納得いかない。次にうれしいのが、スタックでごにょごにょやらずに直接引数を指定できる点。なんだがよくわからない(≒だんだんわからなくなってくる)スタック操作から解放される。最後にもしかしたらこれが一番重要かもしれないけれど、(他の有力なスクリプトと比較して)アップデートがかなり頻繁に行われている点。(※3)

今回はとりあえずHellowWorldまで持っていくことに。
現在最新版のAngelScript 2.17.1 (September 16th, 2009)を利用する。ダウンロード⇒パス通しは割愛するとして、ASで「Hellow world!」をする最小のコードを書いてみる。

#include <cstdio>
#include <angelscript.h>
#include "scriptstring.h" /*最初からついているAngelScriptのアドオン*/

#ifdef _DEBUG
#pragma comment(lib,"angelscriptd.lib")
#else
#pragma comment(lib,"angelscript.lib")
#endif

/**/
void MessageCallback(const asSMessageInfo *msg, void *param)
{
	const char *type = "ERR ";
	if( msg->type == asMSGTYPE_WARNING )
		type = "WARN";
	else if( msg->type == asMSGTYPE_INFORMATION )
		type = "INFO";

	printf("%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message);
}
/**/
void Print( std::string& value )
{
	puts(value.c_str());
}
/**/
int main(int argc, char **argv)
{
	int r;
	asIScriptContext *ctx = NULL;
	asIScriptEngine *engine = NULL;

	engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
	if( !engine )
		return -1;
	/*コンパイラメッセージの設定*/
	r = engine->SetMessageCallback(asFUNCTION(MessageCallback), 0, asCALL_CDECL);
	if( r < 0 )
		goto EXIT;
	/*addonにあるstringの登録。const char* は使えないため。*/
	RegisterScriptString(engine);
	/*printの設定*/
	r = engine->RegisterGlobalFunction("void Print(string &in)", asFUNCTION(Print), asCALL_CDECL);
	if( r < 0 )
		goto EXIT;
	/*スクリプト本体*/
	const char* scriptString =
		"void hellow()"
		"{"
		"      Print( \"hellow world\" );"
		"}";
	/*モジュールを作成し、スクリプトをコンパイル*/
	asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE);
	r = mod->AddScriptSection("script", scriptString, strlen(scriptString) );
	if( r < 0 )
		goto EXIT;
	r = mod->Build();
	if( r < 0 )
		goto EXIT;
	if( r < 0 )
		goto EXIT;
	/*実行するためのコンテキストを作成する*/
	ctx = engine->CreateContext();
	if( !ctx )
		goto EXIT;
	/*関数を検索する*/
	int funcId = engine->GetModule(0)->GetFunctionIdByDecl("void hellow()");
	if( funcId < 0 )
		goto EXIT;
	/*実行前にその関数を準備。これは一番最初だけやればよい。*/
	r = ctx->Prepare(funcId);
	if( r < 0 )
		goto EXIT;
	/*実行*/
	r = ctx->Execute();
	if( r != asEXECUTION_FINISHED )
		puts("失敗");
	else
		puts("成功");

EXIT:
	if( ctx )	ctx->Release();
	if(engine)	engine->Release();

	return 0;
}

これでとりあえずhellowworldができると思う。他の言語と違って面白いのが関数呼び出しがすべて文字列で完結するという点。テンプレートを使ったばかりに地獄を見るということがかなり避けられる。またバインドが失敗すると、コンパイル時に知らせてくれるのもうれしい。次回は、クラスのバインドについて触れたいと思う。

参考:
プログラム/AngelScript L.I.W.

※1 0.*系の一番最初のChangeLogが2003年になっている。
※2 こちらに利用例が掲載されている。
※3 ここをみると一月一回更新という鬼のような速度で更新されています。

090913小言。

13.09.2009 (9:28 am) – Filed under: temp ::

この記事は後で、加筆修正します。

第3回 全日本学生ゲーム開発者連合(全ゲ連) 交流会のお知らせ

本日行われる、全ゲ連交流会に、参加します。
単に聴講するだけでも価値があると思ったのですが、なんとなしに しゃべりたくなったので、しゃべります。「Squirrelを利用した幸せなゲーム開発」と釣りっぽいキャッチャーな題名で、自分はやったことないのにSquirrelを用いた俊敏なゲーム開発方法を提案します。(動的リロードの話をするだけですが。。。しかも泥臭い。)

当日受付もするそうなので、奮ってご参加ください。

追記:
当日のレポというか反省文
・朝の4時ごろにやっとスライド完成。
・13:00開始だったけれど、12:30前についた。
・ 開始は13:10位だった。
・サークル管理とか、簡単なイメージの作成方法とか面白かった。
・「squirrelのなんちゃら」開始。頭真っ白。
・・ スライドが雑すぎた。
・・細かい実装ははしょったほうがよかった。
・・間違ったソースが張られていた。混乱して、訂正できず。
・・もともとターゲットが極々ニッチであることを把握するべきだった。
・・個人的にライブラリ移植期間だったのと、サンプル的な意味としてコンソールでやったけれど、
かなりいまいちだったと思う。(直前のxtalの発表がかなりいい感じのないようだったのでそれと比べると….orz)
・・動的リロードの説明がメインだったけれど、そこははしょって、どんなことが実際にできるのかを注力するべきだった。
・・最後のスクリプトの高速化の所、つっこまれたやつだけれどもソースの移し間違いとか。ひどすぎ。
・・もっと汎用的で、変に詳細にはいらず、誰でも利用できる何かをやるべきだったとおもお。
・・まとめるとプレゼン力がなさすぎ。
・懇親会はすでにぐったりしていて、三時間近く端っこで、こっくりしているだけになってしまった。
・帰りの電車でのやってもらった「言語仕様今昔物語」は非常に面白かった。

追記2:
あとでpdfの方も修正しますが、一応ここでも書いておくか。。。
見てる人いないと思うけど。
激しく突っ込まれた問題のコード。

local a = [true, true, true];
for(i=0;i<10000;++i)
{
 a[0] = false;
 a[1] = false;
 a[2] = false;
}

local a = [];
for(i=0;i<10000;++i)
{
 a.append(false);
 a.append(false);
 a.append(false);
}

ってスライドに書いていたんだけれども、

for(i=0;i<10000;++i)
{
  local a = [true, true, true];
  a[0] = false;
  a[1] = false;
  a[2] = false;
}

for(i=0;i<10000;++i)
{
  local a = [];
  a.append(false);
  a.append(false);
  a.append(false);
}

が本来のものです……orz
二つ目めちゃくちゃ過ぎる。。。

追記3:
う~ん。twitterの投稿を流し読みしているのですが、いろいろありますね。。(汗) 俺俺実装を晒しても(しかも特定の言語)あんまり役に立たないかも。あとやっぱりしゃべりがよろしくなくて、わかりずらいというのが多かった。やはりサラッとやってしまうべき問題だったのかもしれない。

追記4:
今回の内容は、Squirrelの弱点ばかりが強調されてしまった感はある(私の紹介した/知っている方法が非常に手間がかかるという点で)。結局は、やっぱLuaですね。。詳しく知らないのですが、xtalは最初から動的リロードを前提としているらしくその点では、xtalもお勧めかもしれません。

追記5:
レポートがあげられたようです。
第3回全日本学生ゲーム開発者連合(全ゲ連)交流会-いつも心に平穏を
やはり、しゃべりがよくなかったようですね。。。。要反省。さっさと、修正作業を終わらせないと。。。