セカンドライフでタッチイベントと状態遷移の落とし穴

投稿日:

突然ですが、次のプログラムを見てみてください。
stateを使った状態遷移と、touch_startを使ったプログラムとなっています。
どのように動くか想像できますでしょうか。

default {
	state_entry() {
		llOwnerSay("state 0");
	}
	touch_start(integer total_number) {
		state state1;
	}
}

state state1 {
	state_entry() {
		llOwnerSay("state 1");
		llSetTimerEvent(5.0);
	}
	timer() {
		llSetTimerEvent(0);
		state state2;
	}
}

state state2 {
	state_entry() {
		llOwnerSay("state 2");
	}
	touch_start(integer total_number) {
		state default;
	}
}

普通は、次のような動きを想像するかと思います。

1. 「state 0」と表示される
2. クリックする
3. 「state 1」と表示される
4. 5秒待つ
5. 「state 2」と表示される
6. クリックする
7. 「state 0」と表示される

実際にやってみてください。
実はうまくいかず、次のような動きをしているようにみえます。

1. 「state 0」と表示される
2. クリックする
3. 「state 1」と表示される
4. 5秒待つ
5. 「state 2」と表示される
6. クリックする
7. 何も発生しない
8. クリックする
9.「state 0」と表示される

なぜ、このような動きになるのでしょうか。

実は、「touch_end」イベントに関連しており、想像なのですが次のようになっています。
1. 「touch_start」が作られた時点で「touch_end」も内部的には作られる。
2. 「touch_start」イベントが発動する条件は、「touch_end」イベントが終わってから。

このために、上のソースコードは内部で次のような動作をしていると予想されます。

1. 「state 0」と表示される
2. マウスボタンを押して default:touch_start イベントを発生
3. 「state 1」と表示される
4. マウスボタンを離すが state1:touch_end が存在しないため無視★重要★
5. 「state 2」と表示される
6. マウスボタンを押すが touch_end が発生しないため、touch_start イベントを発生なし★重要★
7. マウスボタンを離して touch_end が発生(何も行わない)
8. マウスボタンを押して state2:touch_start イベントを発生
9. 「state 0」と表示される

では、回避策です。

【回避策1】全ての状態に、touch系のイベントを入れる

default {
	state_entry() {
		llOwnerSay("state 0");
	}
	touch_start(integer total_number) {
		state state1;
	}
}

state state1 {
	state_entry() {
		llOwnerSay("state 1");
		llSetTimerEvent(5.0);
	}
	timer() {
		llSetTimerEvent(0);
		state state2;
	}
	// ここが重要
	touch_end(integer total_number) {
	}
}

state state2 {
	state_entry() {
		llOwnerSay("state 2");
	}
	touch_start(integer total_number) {
		state default;
	}
}

【回避策2】「state1」の状態へ行くためには、touch_endを契機とさせる

default {
	state_entry() {
		llOwnerSay("state 0");
	}
	touch_end(integer total_number) {	// ここが重要
		state state1;
	}
}

state state1 {
	state_entry() {
		llOwnerSay("state 1");
		llSetTimerEvent(5.0);
	}
	timer() {
		llSetTimerEvent(0);
		state state2;
	}
}

state state2 {
	state_entry() {
		llOwnerSay("state 2");
	}
	touch_start(integer total_number) {
		state default;
	}
}

【回避策3】「state2」のタッチイベントの発生を、touch_endを契機とさせる

default {
	state_entry() {
		llOwnerSay("state 0");
	}
	touch_start(integer total_number) {
		state state1;
	}
}

state state1 {
	state_entry() {
		llOwnerSay("state 1");
		llSetTimerEvent(5.0);
	}
	timer() {
		llSetTimerEvent(0);
		state state2;
	}
}

state state2 {
	state_entry() {
		llOwnerSay("state 2");
	}
	touch_end(integer total_number) {	// ここが重要
		state default;
	}
}

以上
報告でした!


オマケ

ところで話が変わりますが、state1の状態の時に、
カーソルの形状を手のアイコンから普通のアイコンにするために、
今回は、llSetTimerEventを使いました。

実は、llSleepを使うと、アイコンが変わりません。
おそらく、state_entryを抜けたときにカーソルの形状が変わると思われます。

以下ダメな例です。

default {
	state_entry() {
		llOwnerSay("state 0");
	}
	touch_start(integer total_number) {
		state state1;
	}
}

state state1 {
	state_entry() {
		llOwnerSay("state 1");
		llSleep(5.0);
		state state2;
	}
}

state state2 {
	state_entry() {
		llOwnerSay("state 2");
	}
	touch_end(integer total_number) {
		state default;
	}
}
広告

コメントをどうぞ(承認された後に公開されます。メールアドレスの記入は自由ですが、記入した場合でも一般公開されることはありません)

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中