9-Laboratory

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回全日本学生ゲーム開発者連合(全ゲ連)交流会-いつも心に平穏を
やはり、しゃべりがよくなかったようですね。。。。要反省。さっさと、修正作業を終わらせないと。。。

090911小言。

11.09.2009 (3:35 am) – Filed under: temp ::

ac

今更、アサシンクリードをクリアした。
クリアまで、12日ほどかかった。

○よかったところ
・街中を飛び回るのがとにかく楽しかった。
・荒廃とした風景に味があって良かった。出たときにリアルタイムでやっていたら、感動したに違いない。
・戦闘シーンのBGMがいい感じに焦燥感を煽って素晴らしかった。
・アルタイル君に胸キュンした。

○よくなかったところ
・戦闘が単調で、作業的だった。ボスも全く同じ戦法で勝ててしまう。
・隠密行動をしないときのペナルティが無きに等しかった。
もっと隠密らないとどうも立ち行かなくなるようなシチュエーションにして欲しかった。
・「次どうすればいいのさ?」と思うところが何箇所かあった。
・普通に撒くのが至難の業だった。隠れると通常状態に戻るというのは納得するのだけれど、簡単すぎてしまう。
・市民殺しにペナルティがあるのは納得いかなかった。
・カウンター強すぎ。一回覚えてしまうと、隠密行動ガン無視、中央突破、敵皆殺しでも全く問題なくなってしまう。
・敵のAIが単調だった。番兵のAIも。
・血吹雪が、チープだった。
・発見された時の演出が大げさすぎると思う。
・ラスボス戦の演出は、ポケモンの事件を思い出した。私も、ちょっと めまい が起きた。

2がそろそろでるらしいですが、是非次は発売時にリアルタイムでやりたいですね。

090904小言。

04.09.2009 (9:51 pm) – Filed under: temp ::

wonderflに面白いのがあったので、貼り付ける。

CEDEC 三日目 俺的まとめ。

04.09.2009 (2:13 am) – Filed under: temp ::

今日はちゃんと起きれた。

二コマ目.
Squirrelを使ったゲーム開発 Part2
more »

CEDEC 二日目 俺的まとめ。

02.09.2009 (9:17 pm) – Filed under: temp ::

二十分遅れで会場入り。

三コマ目。
「スケーラブルな並列化」
more »

CEDEC 一日目 俺的まとめ。

01.09.2009 (10:43 pm) – Filed under: temp ::

たまには日誌的に書く。

朝9時に目が覚める。
どうがんばっても基調講演に間に合わないので、ゆっくり準備。
桜木町駅に11:20に着く。
パシフィコ横浜の場所を知らないので、適当においてある地図で確認。
「あ、あれが赤レンガ倉庫か。きれいだね。」
どう考えても道を間違っています。
#帰りに気がついたが、普通にパシフィコ横浜まで100円バスが出ている。
結局着いたのが11:50分。 more »