この前の週末に ASUKA DJ RELAY というイベントがあって、DJ さんがリレー形式でクラブ雷神で曲をかけるというイベントがありました。で、そのときの模様を動画でとって Yaz さんの Sunrise で編集してみました。
今、新しいダンス玉のテスト中で、今回のダンス玉の特徴は「時間差ダンス」です。
同期(シンクロ)ダンスはよくありますが、0.5秒とかわざとずらしてダンスを開始させるものを試しています。
今回の動画でもその「時間差ダンス」をみることができます。
時間差ダンスの仕組みはとても簡単ですが、Mono のおかげでちょっと気がラクになったかも・・・です。人数分の llStartAnimation の命令をちょっとずつ時間をずらして行うわけですから、軽い処理とはいえません。
しばらくクラブ雷神で様子をみながらテストを繰り返すことになると思います。
もちろん、時間差から同期の切り替えはボタンでオッケーです。
あ~、エンディングの日付・・・おもいっきり「未来」になってるし、、、開催されたのは9月13日でした。。。
2008年9月17日水曜日
Sunrise - 時間差ダンス
2008年8月31日日曜日
新しい関数 llGetAgentLanguage
Second Life で外人の方とのコミュニケーションは珍しくありませんよね。
ダンスクラブに来られた外人の方にダンスの開始方法を教えるのも、、、まぁ、英語ならなんとかチャットで教えてあげていました。
サーバー 1.24 でいくつかの新しい関数が追加されていますが、llGetAgentLanguage という関数は、その Avatar の Agent が使っているビューワーの言語のバージョンを取得する関数です。これを使うと、スクリプトのメモリが許せば、日本語、英語、がんばれば他の外国語のメッセージをスクリプト内に保存しておいて、Agent が利用しているビューワーの言語情報によって切り替えることが可能ですね。
MLDU(ダンス玉) でいうと DDManager みたいなダイアログの操作でストリング(文字列)とリストを多用していると、メモリの問題から1つのスクリプト内で処理するのはつらいのですが、個々のアバターのパーミッションを管理して、ダンスの開始、停止を行うスクリプトなどは、複数の言語メッセージを保管しておいて、llGetAgentLanguage の情報からメッセージを切り替えることが可能になりますね。
こんな感じです。
string msg1 = "こんにちは!";
string msg1e = "Hello!!";
if (llGetAgentLanguage(id)=="ja"){
llInstantMessage(id, msg1);
} else llInstantMessage(id, msg1e);
日本語以外だったら英語、という単純なものですが、ダンスの停止方法などをビューワーの言語設定にあわせて切り替えて表示させるには重宝しますね。
あたりまえですが、引数にはアバターの Key が必要です。ですから、Detected 系の関数をつかってあらかじめ対象となるアバターの Key を取得して llGetAgentLanguage に渡すことになりますね。戻り値は ja や en-us といった文字列になります。
list を使って利用する方法もありますね。
list msgList01 = ["ja","こんにちは!!","en-us","Hello!!","fr","Salut!!!"];
default
{
state_entry()
{
llSay(0, "Hello, Avatar!");
}
touch_start(integer total_number)
{
integer i = 0;
for(; i<total_number; ++i) {
string lang = llGetAgentLanguage(llDetectedKey(i));
integer x = llListFindList(msgList01,[lang]);
if(x==-1) x=2; //言語対応してない場合のデフォルトを指定します。
llInstantMessage(llDetectedKey(i), llList2String(msgList01,x+1));
}
}
}
ちょっとしたメッセージだと、この方が list に言語と対応するメッセージを追加するだけでよく、 if 文を変更せずに済むので楽なのかもしれませんね。
ちょっとした小ネタでした~。
ダンスクラブに来られた外人の方にダンスの開始方法を教えるのも、、、まぁ、英語ならなんとかチャットで教えてあげていました。
サーバー 1.24 でいくつかの新しい関数が追加されていますが、llGetAgentLanguage という関数は、その Avatar の Agent が使っているビューワーの言語のバージョンを取得する関数です。これを使うと、スクリプトのメモリが許せば、日本語、英語、がんばれば他の外国語のメッセージをスクリプト内に保存しておいて、Agent が利用しているビューワーの言語情報によって切り替えることが可能ですね。
MLDU(ダンス玉) でいうと DDManager みたいなダイアログの操作でストリング(文字列)とリストを多用していると、メモリの問題から1つのスクリプト内で処理するのはつらいのですが、個々のアバターのパーミッションを管理して、ダンスの開始、停止を行うスクリプトなどは、複数の言語メッセージを保管しておいて、llGetAgentLanguage の情報からメッセージを切り替えることが可能になりますね。
こんな感じです。
string msg1 = "こんにちは!";
string msg1e = "Hello!!";
if (llGetAgentLanguage(id)=="ja"){
llInstantMessage(id, msg1);
} else llInstantMessage(id, msg1e);
日本語以外だったら英語、という単純なものですが、ダンスの停止方法などをビューワーの言語設定にあわせて切り替えて表示させるには重宝しますね。
あたりまえですが、引数にはアバターの Key が必要です。ですから、Detected 系の関数をつかってあらかじめ対象となるアバターの Key を取得して llGetAgentLanguage に渡すことになりますね。戻り値は ja や en-us といった文字列になります。
list を使って利用する方法もありますね。
list msgList01 = ["ja","こんにちは!!","en-us","Hello!!","fr","Salut!!!"];
default
{
state_entry()
{
llSay(0, "Hello, Avatar!");
}
touch_start(integer total_number)
{
integer i = 0;
for(; i<total_number; ++i) {
string lang = llGetAgentLanguage(llDetectedKey(i));
integer x = llListFindList(msgList01,[lang]);
if(x==-1) x=2; //言語対応してない場合のデフォルトを指定します。
llInstantMessage(llDetectedKey(i), llList2String(msgList01,x+1));
}
}
}
ちょっとしたメッセージだと、この方が list に言語と対応するメッセージを追加するだけでよく、 if 文を変更せずに済むので楽なのかもしれませんね。
ちょっとした小ネタでした~。
2008年8月23日土曜日
Mono がやってきます
Linden の Official Blog に「Mono Launch」という記事がついに投稿されましたね。
Linden Official Blog - Mono Launch
20日から 1.24 サーバーの展開が始まって、その 1.24 サーバーには Mono が含まれている、ということらしいです。
サーバーが 1.24 になっただけでは Mono は使えず、スクリプト作成者はクライアントビューワーの 1.21 RC が必要になるということです。1.21 RC のビューワーではスクリプトエディタの下に [Mono] というチェックボックスが追加されるので、それをチェックして保存、コンパイルで Mono 対応に移行完了、ということらしいです。逆に [Mono] にチェックをいれなければ、従来のまま動くようです。
ちなみに サーバー 1.24 のリリースノートによれば、ビューワーの 1.21 RC は 8月25日の週にリリースする予定ですね。
Mono はスクリプトを動かすための新しいエンジンで、従来のものと並存して提供されるとのことです。すべてのスクリプトを変換する必要はなく、また、LSL の書き方が変わるわけではありません。ちょっと安心ですね。
この Mono ですが、この新しい実行環境はスクリプトの「実行スピード」と「メモリー管理」が現行のスクリプト エンジンよりも強化されている点が特徴です。
この 1ヶ月ほど、ダンス玉 (次の MLDU) に機能をどんどん追加していったらメモリーがかなり厳しくなって、スクリプトを分けないと 10人以上踊れない、、、という状況になっていてのこの記事だったので、早速ベータ グリッドにいって試してみました。メモリーに関していえば、これまで 1つのスクリプトは 16KB しかメモリーを使えませんでしたが、Mono では 64 KB まで拡張されていることは知っていました。
今の環境だと、一番メモリーがくるしい DDManager (Dance Slot と Dialog 管理スクリプト) は残りのメモリーが 1.1K しかありません。これ、リストの操作で空のリストを使う方法じゃないと、エラーでスクリプトが止まる状態だったりします、、、
ベータ グリッドにアクセスするには Preview Grid Viewers でダウンロードできる 1.21 ベータをインストールして、1.24 サーバーになっている Sandbox のリージョンにいくと Mono のテストが可能です。
早速、スクリプトエディタを開いてみると、ありました、[Mono] のチェックボックスが。
MLDU ダンス玉のスクリプトの [Mono] をすべてチェックして、メモリーの状況を確認してみました。
1.1 KB しか余裕がなかった DDManager は 29.5KB(!!!) までフリーメモリーが増えました。(^^)v
リストやデータを保持するための単位必要メモリーの容量が大幅に変わっていなければ、これでかなり余裕 (というか、16KB より大きいしw) ができたことになりますね。
あと、やはり実行速度も速くなっています。メモリーの状況を表す上の画面の Text の表示や、MLDU 初期化時のアニメーションチェックなど、今のものよりも速くなっていますね。
ということで、スクリプト分割大改造せずに Mono を待つことにしました。(笑
Linden Official Blog - Mono Launch
20日から 1.24 サーバーの展開が始まって、その 1.24 サーバーには Mono が含まれている、ということらしいです。
サーバーが 1.24 になっただけでは Mono は使えず、スクリプト作成者はクライアントビューワーの 1.21 RC が必要になるということです。1.21 RC のビューワーではスクリプトエディタの下に [Mono] というチェックボックスが追加されるので、それをチェックして保存、コンパイルで Mono 対応に移行完了、ということらしいです。逆に [Mono] にチェックをいれなければ、従来のまま動くようです。
ちなみに サーバー 1.24 のリリースノートによれば、ビューワーの 1.21 RC は 8月25日の週にリリースする予定ですね。
Mono はスクリプトを動かすための新しいエンジンで、従来のものと並存して提供されるとのことです。すべてのスクリプトを変換する必要はなく、また、LSL の書き方が変わるわけではありません。ちょっと安心ですね。
この Mono ですが、この新しい実行環境はスクリプトの「実行スピード」と「メモリー管理」が現行のスクリプト エンジンよりも強化されている点が特徴です。
この 1ヶ月ほど、ダンス玉 (次の MLDU) に機能をどんどん追加していったらメモリーがかなり厳しくなって、スクリプトを分けないと 10人以上踊れない、、、という状況になっていてのこの記事だったので、早速ベータ グリッドにいって試してみました。メモリーに関していえば、これまで 1つのスクリプトは 16KB しかメモリーを使えませんでしたが、Mono では 64 KB まで拡張されていることは知っていました。
今の環境だと、一番メモリーがくるしい DDManager (Dance Slot と Dialog 管理スクリプト) は残りのメモリーが 1.1K しかありません。これ、リストの操作で空のリストを使う方法じゃないと、エラーでスクリプトが止まる状態だったりします、、、
ベータ グリッドにアクセスするには Preview Grid Viewers でダウンロードできる 1.21 ベータをインストールして、1.24 サーバーになっている Sandbox のリージョンにいくと Mono のテストが可能です。
早速、スクリプトエディタを開いてみると、ありました、[Mono] のチェックボックスが。
MLDU ダンス玉のスクリプトの [Mono] をすべてチェックして、メモリーの状況を確認してみました。
1.1 KB しか余裕がなかった DDManager は 29.5KB(!!!) までフリーメモリーが増えました。(^^)v
リストやデータを保持するための単位必要メモリーの容量が大幅に変わっていなければ、これでかなり余裕 (というか、16KB より大きいしw) ができたことになりますね。
あと、やはり実行速度も速くなっています。メモリーの状況を表す上の画面の Text の表示や、MLDU 初期化時のアニメーションチェックなど、今のものよりも速くなっていますね。
ということで、スクリプト分割大改造せずに Mono を待つことにしました。(笑
2008年8月17日日曜日
YazMania Videos Clip
NMR10~、遅ればせながら参加できました。
ファンの人からの「この曲が好き、なぜなら、、、」みたいなやり方って、ほんと楽しかったし。
それぞれが、それぞれの思いあるから、「へぇ~、そうなんだー」みたいなのがあって、新鮮でした。
Yaz Yaz で素人なりにビデオつくってきて良かったなぁ~、と思いました。それぞれの思いがある曲もみんな違うし、それぞれの考え方や印象もみんな違う。それって、いろいろな思いをいだかせることができる才能のある人がなせる業w
すごいなぁ、、、Yaz さんって、いろいろな人にいろいろな感情・体験を持たせているんだぁ、、、と改めて実感しました。
で。。。。
こんなん、作ってみました~。
High Quality な、、、ですでに動画を見てる人は問題なさげ~、ですがw
[追記] サービス終了にて削除しました。
これからも多くのファンを楽しませてください~w
ファンの人からの「この曲が好き、なぜなら、、、」みたいなやり方って、ほんと楽しかったし。
それぞれが、それぞれの思いあるから、「へぇ~、そうなんだー」みたいなのがあって、新鮮でした。
Yaz Yaz で素人なりにビデオつくってきて良かったなぁ~、と思いました。それぞれの思いがある曲もみんな違うし、それぞれの考え方や印象もみんな違う。それって、いろいろな思いをいだかせることができる才能のある人がなせる業w
すごいなぁ、、、Yaz さんって、いろいろな人にいろいろな感情・体験を持たせているんだぁ、、、と改めて実感しました。
で。。。。
こんなん、作ってみました~。
High Quality な、、、ですでに動画を見てる人は問題なさげ~、ですがw
[追記] サービス終了にて削除しました。
これからも多くのファンを楽しませてください~w
2008年8月12日火曜日
llDialog 再び
造形のセンスまったくなしなので、クールな HUD を作ってスクリプトを埋めこむ、、、なんてことができないので、どうしても llDialog を使うことになってしまいます。
llDialog で表示するダイアログには12個のボタンしか表示できない、ボタン内に表示できる文字数に制限がある、同じ場所から出てくるので「複数ダイアログ表示」できないことはないのですが、あまり使い勝手が良くないなど、制約が多いのですが、、、これを使うしかないわけで(笑
そこそこ多くのダイアログを作ってみて、気がついた点などを再度まとめてみようと思いました。
1) 複数の人がダイアログを押して、それを処理するような状況になるべくしないほうがいいかも
用途にもよりますが、複数の人がダイアログを押すような状況を仮定してスクリプトを組まないほうが懸命のようです。できないことはないのですが、if 文を駆使して、そのロジックをいろいろと悩むよりも、「前の人の処理が終わるまで待ってもらう」としたほうが当然のことながらスクリプトがシンプルになります。
[追記] ダンス玉(つまりアニメーション操作)など Permission を必要とするものなどは、touch されたら、アバターの Key を他のスクリプトに渡し、そちらで処理する方法があります。この場合は、複数の人が1つのプリムをタッチしても、個別に処理するスクリプトの数の最大に達するまで処理をさせることが可能になります。1つの Prim には複数のスクリプトを入れることが可能なので、touch を管理するスクリプトを親として、詳細の処理を子スクリプトに渡す、、、ということができます。
2) state を使ったほうがラクかも
これも人によりけりでしょうが、私の場合は、タッチされたら(もしくは、なんらかのイベントが発生してダイアログを呼ぶようなことになったら)、そのアバターの key を拾って state を変える方法を使うことが多くなりました。メリットは llListenRemove を使わなくても state が変わることによって listeners を溜めなくてすむこと(64 だったかな?、Remove せずにそのくらい溜まるとエラーになります)、他の人が touch したときの処理をシンプルにできること、だと思っています。また、state_entry, state_exit を使った初期化処理/終了処理が可能でスクリプトが見やすくなることもあげられます。デメリットは必ず llSetTimerEvent を使ってタイムアウトを検知させること。これをやらないでダイアログの [無視] ボタンを押されたら元の state に戻って来れません。
(以下は可読性を高めるためにグローバル変数をあまり使ってません)
key toucher = NULL_KEY;
list buttons = ["button1", "button2", "button3"];
default {
state_entry() {
toucher = NULL_KEY;
}
touch_start(integer detected_num) {
toucher = llDetectedKey(0); //一番最初にタッチいた Avatar の Key を取得
state dialog; //ダイアログ・ステートに移動
}
}
state dialog {
state_entry() {
integer hDialog = llListen(-5555, "", toucher, ""); //-5555チャンネルで、タッチした Avatar の声だけ聞く設定
llListenControl(hDialog, TRUE); //リスナーを追加(要は聞き始める、ということ)
llDialog(toucher, "好きなボタンを押してください。", buttons, -5555); //ダイアログを表示。返信は -5555 チャンネル
llSetTimerEvent(30.0); //30秒でタイムアウトにさせるため
}
touch_start(integer detected_num) { //ダイアログ処理中にタッチした Avatar への処理
integer i = 0;
for(; i<detected_num; i++) { //タッチした人分の処理をする for ループ
llInstantMessage(llDetectedKey(i), "ダイアログ使用中です。しばらくまってください。");
}
}
timer() {
llInstantMessage(toucher, "タイムアウトになりました。");
llSetTimerEvent(0.0);
state default;
}
listen(integer channel, string name, key id, string message) {
if (llListFindList(buttons, [message]) != -1) { //一応のチェック処理。この程度なら必要ないですが・・・
llSetTimerEvent(0.0);
if (message == "button1") {
xxxxxxxx;
} else if (message == "button2") {
yyyyyyyy;
} else if (message == "button3") {
zzzzzzzz;
}
state default;
}
}
state_exit() {
toucher = NULL_KEY;
llSetTimerEvent(0.0); //しつこいですが、、、過去に timer が戻らなかったことがあったので・・・
}
}
3) ダイナミックにボタンを変えてみましょう
「前ページ」「次ページ」ボタンで複数ダイアログを使いこなすのもおもしろいですが、操作する側にとっては望まない複数ダイアログは結構迷惑、、、だったりします。
たとえば、ダンスを踊る、止める、みたいな場合、最初のころ [踊る] [止める] のボタンを用意していましたが、よくよく考えると踊っている時は [踊る] ボタンを押すわけもなく、だったら [踊る] のボタンを [止める] にすればいいわけです。このため、状態を保持するフラグを使用しなくてはなりませんが、ボタン使用の節約には効果大です。
この場合は、フラグを元にボタンのリストに対して llListFindList を使い、該当する List 要素の順番をとり、llListReplaceList で置き換える、ということをやります。
意外に、、、この手の「対称な意味を持つ」ボタンって、、、多かったりします。
以下の複数ページダイアログの例の「ノートカードが変更されたら Close ボタンを RESET ボタンに変更する」などもこの考えでボタンを変更しています。
4) 複数ページダイアログの例
以前にご紹介している ネトラジ・チェンジャーは複数ページダイアログでネトラジ局をボタンにしています。その方法をご紹介します。
以下のスクリプトの前段階処理で、ノートカードからネトラジ局の情報を読み込んで contentList という List に入れています。
ダイアログの一番下の段は [< Prev] [Close] [Next>] という3つのボタンがくるので、1つのダイアログには9つのラジオ局のボタンが表示できます。その9つの部分を変えていくわけですね。
現在のページ数を currentPage として、0ページ目(そう、この世界ってはじめはゼロのほうが都合がいいです)の場合は 0 番から8番まで、1ページ目の場合は 9番から17番、、、、としていきます。
最後のページは List の項目数を 9 で割った商になります。たとえば0ページから「前ページ」を押されたら、最大ページに移る、、、みたいなこともあらかじめ最後のページ数をとっておいて、もし、「前ページ」がマイナスになるのであれば、最大ページをいれる、次ページが最大ページを超えるようであれば、0ページを入れる、とすれば、複数ページのラウンドロビンが可能ですよね。
以下は NRC のソースの default ステートを除いたそのものです。オレンジにした部分がページとボタン操作になります。
state wait {
state_entry()
{
llMessageLinked(LINK_THIS,myNumber,"wait",gToucher);
currentPage = 0;
buttonList = [];
gToucher = "";
integer handle = llListen(gChan,"","","");
llListenControl(handle,TRUE);
}
link_message(integer sender_number, integer number, string message, key id)
{
if (message == "start") {
if (number == myNumber) {
gToucher = id;
currentPage = 0;
state active;
}
}
if (message == "contentChanged")
{ // ノートカードが変更されたら Close ボタンを RESET ボタンに変更する
fixedList = llListReplaceList(fixedList,["RESET"],llListFindList(fixedList,["Close"]),llListFindList(fixedList,["Close"]));
}
}
listen(integer channel, string name, key id, string message)
{
if (llStringTrim(llToUpper(message),STRING_TRIM)=="HIDE TEXT") {
llMessageLinked(LINK_THIS,myNumber,"HIDE TEXT","");
} else if (llStringTrim(llToUpper(message),STRING_TRIM)=="SHOW TEXT") {
llMessageLinked(LINK_THIS,myNumber,"SHOW TEXT","");
}
}
}
state active {
state_entry()
{
integer handle = llListen(gChan,"",gToucher,"");
llListenControl(handle,TRUE);
buttonList = fixedList + llList2List(contentList,currentPage*9,(currentPage+1)*9-1);
gDialogMsg2 = "登録数 "+(string)gCount+", "+"ダイアログチャンネル: "+(string)gChan;
gDialogMsg3 = "オブジェクト上の文字を消したい場合は"+"\n"+"/"+(string)gChan+" hide text をダイアログを閉じてから行ってください。";
dMsg = gDialogMsg+"("+(string)(currentPage+1)+"/"+(string)(maxPage+1)+")"+"\n"+gDialogMsg2+"\n"+gDialogMsg3;
llDialog(gToucher,dMsg,buttonList,gChan);
llSetTimerEvent(0.0);
llSetTimerEvent(gInterval);
}
state_exit()
{
llSetTimerEvent(0.);
}
timer()
{
if (llKey2Name(gToucher) != "") {
llInstantMessage(gToucher,gMsgTimeout);
}
state wait;
}
listen(integer channel, string name, key id, string message)
{
if (llListFindList(fixedList,[message])!=-1) {
buttonList = [];
if (message == "Next >"){
if (currentPage+1 > maxPage) currentPage=0;
else currentPage = currentPage+1;
} else if (message == "< Prev") {
if (currentPage-1 < 0) currentPage=maxPage;
else currentPage = currentPage-1;
} else if (message == "Close") {
state wait;
} else if (message = "RESET") {
llResetOtherScript("dialogMgr");
state wait;
}
buttonList = fixedList + llList2List(contentList,currentPage*9,(currentPage+1)*9-1);
gDialogMsg2 = "登録曲数 "+(string)gCount+", "+"ダイアログチャンネル: "+(string)gChan;
dMsg = gDialogMsg+"("+(string)(currentPage+1)+"/"+(string)(maxPage+1)+")"+"\n"+gDialogMsg2+"\n"+gDialogMsg3;
llDialog(gToucher,dMsg,buttonList,gChan);
llSetTimerEvent(0.0);
llSetTimerEvent(gInterval);
} else if (llListFindList(contentList,[message])!=-1) {
llSetParcelMusicURL(llList2String(urlList,llListFindList(contentList,[message])));
llSetText(message,<1,1,1>,0.8);
state wait;
}
}
単純といえば単純ですが、、、なんらかのご参考になれば幸いです。
[追記]
最初にものすごく悩んで、今は普通にやっていることを書き忘れました。
llDialog が使用するチャンネルについてです。
5) llDialog が利用するチャンネルは決めうちにしないほうが良い場合が多い
ダンスボールや、音楽玉、ビデオ玉を作り始めのころに、ダイアログの混線の問題がありました。
たとえば、自分の作ったダンスボールを複数 Rez したとき、llDialog が利用するチャンネルを決めうちにしている場合は、それら Rez したダンス玉を交互に操作するとダイアログ・チャンネルの混線が発生します。
llDialog って、結局は llSay でいうことを代わりにやってくれているようなものなので、それぞれのダイアログの返答がどのダイアログからきているかを判定するもの(値)がないんです。
よって、それぞれのダイアログを判定する方法は、llDialog で使用するチャンネルを変える事になります。
私は llFrand を使って、ランダムにダイアログのためのチャンネルを Rez や初期化時に生成して利用するようになりました。
絶対同じものにならない、、、というわけではありませんが、同じオブジェクトを複数 Rez しても、それぞれのダイアログが利用するチャンネルが違う確率が高いので、この方法をいつも無意識に使うようになりました。
以下のようなユーザー関数を使って、ランダムに生成されたチャンネルを使います。
これは 1000 から 9999 までのランダムな数字を作る例です。
integer randChan() {
return 1000 + (integer)llFrand(8999);
}
llFrand(8999) によって、0 から 8999 までの間の数字がつくられます。 0 が作られたら、それに 1000 を足したもの、つまり 1000 が返され、8999 だったらそれ1000を足した 9999 が返されます。
もちろん、これをマイナスの値にしてあげれば、さらに良くなりますね。
llDialog で表示するダイアログには12個のボタンしか表示できない、ボタン内に表示できる文字数に制限がある、同じ場所から出てくるので「複数ダイアログ表示」できないことはないのですが、あまり使い勝手が良くないなど、制約が多いのですが、、、これを使うしかないわけで(笑
そこそこ多くのダイアログを作ってみて、気がついた点などを再度まとめてみようと思いました。
1) 複数の人がダイアログを押して、それを処理するような状況になるべくしないほうがいいかも
用途にもよりますが、複数の人がダイアログを押すような状況を仮定してスクリプトを組まないほうが懸命のようです。できないことはないのですが、if 文を駆使して、そのロジックをいろいろと悩むよりも、「前の人の処理が終わるまで待ってもらう」としたほうが当然のことながらスクリプトがシンプルになります。
[追記] ダンス玉(つまりアニメーション操作)など Permission を必要とするものなどは、touch されたら、アバターの Key を他のスクリプトに渡し、そちらで処理する方法があります。この場合は、複数の人が1つのプリムをタッチしても、個別に処理するスクリプトの数の最大に達するまで処理をさせることが可能になります。1つの Prim には複数のスクリプトを入れることが可能なので、touch を管理するスクリプトを親として、詳細の処理を子スクリプトに渡す、、、ということができます。
2) state を使ったほうがラクかも
これも人によりけりでしょうが、私の場合は、タッチされたら(もしくは、なんらかのイベントが発生してダイアログを呼ぶようなことになったら)、そのアバターの key を拾って state を変える方法を使うことが多くなりました。メリットは llListenRemove を使わなくても state が変わることによって listeners を溜めなくてすむこと(64 だったかな?、Remove せずにそのくらい溜まるとエラーになります)、他の人が touch したときの処理をシンプルにできること、だと思っています。また、state_entry, state_exit を使った初期化処理/終了処理が可能でスクリプトが見やすくなることもあげられます。デメリットは必ず llSetTimerEvent を使ってタイムアウトを検知させること。これをやらないでダイアログの [無視] ボタンを押されたら元の state に戻って来れません。
(以下は可読性を高めるためにグローバル変数をあまり使ってません)
key toucher = NULL_KEY;
list buttons = ["button1", "button2", "button3"];
default {
state_entry() {
toucher = NULL_KEY;
}
touch_start(integer detected_num) {
toucher = llDetectedKey(0); //一番最初にタッチいた Avatar の Key を取得
state dialog; //ダイアログ・ステートに移動
}
}
state dialog {
state_entry() {
integer hDialog = llListen(-5555, "", toucher, ""); //-5555チャンネルで、タッチした Avatar の声だけ聞く設定
llListenControl(hDialog, TRUE); //リスナーを追加(要は聞き始める、ということ)
llDialog(toucher, "好きなボタンを押してください。", buttons, -5555); //ダイアログを表示。返信は -5555 チャンネル
llSetTimerEvent(30.0); //30秒でタイムアウトにさせるため
}
touch_start(integer detected_num) { //ダイアログ処理中にタッチした Avatar への処理
integer i = 0;
for(; i<detected_num; i++) { //タッチした人分の処理をする for ループ
llInstantMessage(llDetectedKey(i), "ダイアログ使用中です。しばらくまってください。");
}
}
timer() {
llInstantMessage(toucher, "タイムアウトになりました。");
llSetTimerEvent(0.0);
state default;
}
listen(integer channel, string name, key id, string message) {
if (llListFindList(buttons, [message]) != -1) { //一応のチェック処理。この程度なら必要ないですが・・・
llSetTimerEvent(0.0);
if (message == "button1") {
xxxxxxxx;
} else if (message == "button2") {
yyyyyyyy;
} else if (message == "button3") {
zzzzzzzz;
}
state default;
}
}
state_exit() {
toucher = NULL_KEY;
llSetTimerEvent(0.0); //しつこいですが、、、過去に timer が戻らなかったことがあったので・・・
}
}
3) ダイナミックにボタンを変えてみましょう
「前ページ」「次ページ」ボタンで複数ダイアログを使いこなすのもおもしろいですが、操作する側にとっては望まない複数ダイアログは結構迷惑、、、だったりします。
たとえば、ダンスを踊る、止める、みたいな場合、最初のころ [踊る] [止める] のボタンを用意していましたが、よくよく考えると踊っている時は [踊る] ボタンを押すわけもなく、だったら [踊る] のボタンを [止める] にすればいいわけです。このため、状態を保持するフラグを使用しなくてはなりませんが、ボタン使用の節約には効果大です。
この場合は、フラグを元にボタンのリストに対して llListFindList を使い、該当する List 要素の順番をとり、llListReplaceList で置き換える、ということをやります。
意外に、、、この手の「対称な意味を持つ」ボタンって、、、多かったりします。
以下の複数ページダイアログの例の「ノートカードが変更されたら Close ボタンを RESET ボタンに変更する」などもこの考えでボタンを変更しています。
4) 複数ページダイアログの例
以前にご紹介している ネトラジ・チェンジャーは複数ページダイアログでネトラジ局をボタンにしています。その方法をご紹介します。
以下のスクリプトの前段階処理で、ノートカードからネトラジ局の情報を読み込んで contentList という List に入れています。
ダイアログの一番下の段は [< Prev] [Close] [Next>] という3つのボタンがくるので、1つのダイアログには9つのラジオ局のボタンが表示できます。その9つの部分を変えていくわけですね。
現在のページ数を currentPage として、0ページ目(そう、この世界ってはじめはゼロのほうが都合がいいです)の場合は 0 番から8番まで、1ページ目の場合は 9番から17番、、、、としていきます。
最後のページは List の項目数を 9 で割った商になります。たとえば0ページから「前ページ」を押されたら、最大ページに移る、、、みたいなこともあらかじめ最後のページ数をとっておいて、もし、「前ページ」がマイナスになるのであれば、最大ページをいれる、次ページが最大ページを超えるようであれば、0ページを入れる、とすれば、複数ページのラウンドロビンが可能ですよね。
以下は NRC のソースの default ステートを除いたそのものです。オレンジにした部分がページとボタン操作になります。
state wait {
state_entry()
{
llMessageLinked(LINK_THIS,myNumber,"wait",gToucher);
currentPage = 0;
buttonList = [];
gToucher = "";
integer handle = llListen(gChan,"","","");
llListenControl(handle,TRUE);
}
link_message(integer sender_number, integer number, string message, key id)
{
if (message == "start") {
if (number == myNumber) {
gToucher = id;
currentPage = 0;
state active;
}
}
if (message == "contentChanged")
{ // ノートカードが変更されたら Close ボタンを RESET ボタンに変更する
fixedList = llListReplaceList(fixedList,["RESET"],llListFindList(fixedList,["Close"]),llListFindList(fixedList,["Close"]));
}
}
listen(integer channel, string name, key id, string message)
{
if (llStringTrim(llToUpper(message),STRING_TRIM)=="HIDE TEXT") {
llMessageLinked(LINK_THIS,myNumber,"HIDE TEXT","");
} else if (llStringTrim(llToUpper(message),STRING_TRIM)=="SHOW TEXT") {
llMessageLinked(LINK_THIS,myNumber,"SHOW TEXT","");
}
}
}
state active {
state_entry()
{
integer handle = llListen(gChan,"",gToucher,"");
llListenControl(handle,TRUE);
buttonList = fixedList + llList2List(contentList,currentPage*9,(currentPage+1)*9-1);
gDialogMsg2 = "登録数 "+(string)gCount+", "+"ダイアログチャンネル: "+(string)gChan;
gDialogMsg3 = "オブジェクト上の文字を消したい場合は"+"\n"+"/"+(string)gChan+" hide text をダイアログを閉じてから行ってください。";
dMsg = gDialogMsg+"("+(string)(currentPage+1)+"/"+(string)(maxPage+1)+")"+"\n"+gDialogMsg2+"\n"+gDialogMsg3;
llDialog(gToucher,dMsg,buttonList,gChan);
llSetTimerEvent(0.0);
llSetTimerEvent(gInterval);
}
state_exit()
{
llSetTimerEvent(0.);
}
timer()
{
if (llKey2Name(gToucher) != "") {
llInstantMessage(gToucher,gMsgTimeout);
}
state wait;
}
listen(integer channel, string name, key id, string message)
{
if (llListFindList(fixedList,[message])!=-1) {
buttonList = [];
if (message == "Next >"){
if (currentPage+1 > maxPage) currentPage=0;
else currentPage = currentPage+1;
} else if (message == "< Prev") {
if (currentPage-1 < 0) currentPage=maxPage;
else currentPage = currentPage-1;
} else if (message == "Close") {
state wait;
} else if (message = "RESET") {
llResetOtherScript("dialogMgr");
state wait;
}
buttonList = fixedList + llList2List(contentList,currentPage*9,(currentPage+1)*9-1);
gDialogMsg2 = "登録曲数 "+(string)gCount+", "+"ダイアログチャンネル: "+(string)gChan;
dMsg = gDialogMsg+"("+(string)(currentPage+1)+"/"+(string)(maxPage+1)+")"+"\n"+gDialogMsg2+"\n"+gDialogMsg3;
llDialog(gToucher,dMsg,buttonList,gChan);
llSetTimerEvent(0.0);
llSetTimerEvent(gInterval);
} else if (llListFindList(contentList,[message])!=-1) {
llSetParcelMusicURL(llList2String(urlList,llListFindList(contentList,[message])));
llSetText(message,<1,1,1>,0.8);
state wait;
}
}
単純といえば単純ですが、、、なんらかのご参考になれば幸いです。
[追記]
最初にものすごく悩んで、今は普通にやっていることを書き忘れました。
llDialog が使用するチャンネルについてです。
5) llDialog が利用するチャンネルは決めうちにしないほうが良い場合が多い
ダンスボールや、音楽玉、ビデオ玉を作り始めのころに、ダイアログの混線の問題がありました。
たとえば、自分の作ったダンスボールを複数 Rez したとき、llDialog が利用するチャンネルを決めうちにしている場合は、それら Rez したダンス玉を交互に操作するとダイアログ・チャンネルの混線が発生します。
llDialog って、結局は llSay でいうことを代わりにやってくれているようなものなので、それぞれのダイアログの返答がどのダイアログからきているかを判定するもの(値)がないんです。
よって、それぞれのダイアログを判定する方法は、llDialog で使用するチャンネルを変える事になります。
私は llFrand を使って、ランダムにダイアログのためのチャンネルを Rez や初期化時に生成して利用するようになりました。
絶対同じものにならない、、、というわけではありませんが、同じオブジェクトを複数 Rez しても、それぞれのダイアログが利用するチャンネルが違う確率が高いので、この方法をいつも無意識に使うようになりました。
以下のようなユーザー関数を使って、ランダムに生成されたチャンネルを使います。
これは 1000 から 9999 までのランダムな数字を作る例です。
integer randChan() {
return 1000 + (integer)llFrand(8999);
}
llFrand(8999) によって、0 から 8999 までの間の数字がつくられます。 0 が作られたら、それに 1000 を足したもの、つまり 1000 が返され、8999 だったらそれ1000を足した 9999 が返されます。
もちろん、これをマイナスの値にしてあげれば、さらに良くなりますね。
2008年7月6日日曜日
Electrogram NITORO Black Side
ビデオ、つくっちゃいました~。
Video: Electrogram NITORO Black Side
KOBIKI-Z, The Black Stripes のみなさま、楽曲を使用しないでごめんなさい~。
ライブの模様を・・・ということでおゆるしください。
また、特設タワー崩壊のシーンもいれてみました。(事情をしらない人はかなり驚いたようです)
Yaz さんの曲で Live の模様の動画つくったの、、、N.M.R.7 ぶり?
[追記...] GION Experiment があったじゃない!(笑
みりおんきっし~ず
Video: Electrogram NITORO Black Side
KOBIKI-Z, The Black Stripes のみなさま、楽曲を使用しないでごめんなさい~。
ライブの模様を・・・ということでおゆるしください。
また、特設タワー崩壊のシーンもいれてみました。(事情をしらない人はかなり驚いたようです)
Yaz さんの曲で Live の模様の動画つくったの、、、N.M.R.7 ぶり?
[追記...] GION Experiment があったじゃない!(笑
みりおんきっし~ず
High Quality な動画を楽しむ その2
エレクトログラム、最高でした。
残念ながら1日目は参加できませんでしが、今日の2日目。
われらが Yaz さんはもちろんですが、、、KOBIKI-Z 最高でした、、、てか反則w
「おもい・・」「おもい・・・」「どげんかせんと・・・」
ほんと、面白かったぁ。
ビデオは撮ってみました。でも、楽曲が Yaz さんのものしか手元にないので、、、、ビデオ編集悩みそうです。
で、前回から引き続いて High Quality な動画その2は Instant Sorrow でw
[該当の動画サービスが終了予定なので以下は削除いたしました。YouTube でお楽しみください。]
動画エリアの下のほうにマウスをおくとコントロールがでるので、再生ボタンをぽっちっとな、、してください。
(注)この動画を見るには専用プラグインのインストールが必要です。1-2分程度で終了します。
[追記] Firefox3 だとちょっとうまくいかないようです。FireFox3 の方は今しばらくお待ちください。。。
[追記] Firefox3 でもOKになりました。
ビデオおたのしみにぃ~w
残念ながら1日目は参加できませんでしが、今日の2日目。
われらが Yaz さんはもちろんですが、、、KOBIKI-Z 最高でした、、、てか反則w
「おもい・・」「おもい・・・」「どげんかせんと・・・」
ほんと、面白かったぁ。
ビデオは撮ってみました。でも、楽曲が Yaz さんのものしか手元にないので、、、、ビデオ編集悩みそうです。
で、前回から引き続いて High Quality な動画その2は Instant Sorrow でw
[該当の動画サービスが終了予定なので以下は削除いたしました。YouTube でお楽しみください。]
(注)この動画を見るには専用プラグインのインストールが必要です。1-2分程度で終了します。
[追記] Firefox3 だとちょっとうまくいかないようです。FireFox3 の方は今しばらくお待ちください。。。
[追記] Firefox3 でもOKになりました。
ビデオおたのしみにぃ~w
2008年7月3日木曜日
HighQualityな動画を楽しむ
YouTube も高画質モードがでてきて、動画共有もますます楽しくなってきました。
で、ちょっと DVD クォリティまで可能な方法をためしてみました。
その試したサービスは 1.4Mbps のビットレートが再生時に可能、、、となっているので、かなり Second Life を実際みているクォリティがでると思いますし、、、地デジみたいに結構みたくないものまで見えるかもしれません。(笑
[残念ながら、YouTubeを除くサービスが終了・終了予定のため削除しました]
ということで、、、前に作ったビデオをアップしたのがこれ。GION の YazYaz な曲とビデオを使ってみました。
このビデオはフラッシュみたいに専用のプラグインが必要です。最近 GYAO とかでも使っているのでそんなに特殊なものではないと思います。
Windows, Mac, Linux でも OK で、ブラウザは IE, Firefox, Safari, Opera でも動くと聞いています。 そのビデオを見たい人は「続きを読む」をクリックしてください。 どうでしょw
かなりきれいにビデオを見ることができるような気がします。
ちなみに以下はこれまで使って、投稿してきた動画共有サイトの Embed リンクです。
YouTube
ますます、動画の世界が楽しくなってきましたね。
2008年6月21日土曜日
新しいデフォルトアバター
なかなかインできない日々が続いておりますが、Second Life Official ブログをみていたら、デフォルトアバターが変更されたんですね。
デフォルトアバター一覧はこちら
http://wiki.secondlife.com/wiki/Default_Avatar_Designers
こんなのとか、こんなのとか。



男性アバターはこんなのとか。



人間型じゃないアバターも。


Library から使えないかなぁ、、、
デフォルトアバター一覧はこちら
http://wiki.secondlife.com/wiki/Default_Avatar_Designers
こんなのとか、こんなのとか。
男性アバターはこんなのとか。
人間型じゃないアバターも。
Library から使えないかなぁ、、、
2008年6月13日金曜日
Race がまた・・・開催されます
Race 計測スクリプト、、、前回の SL Japan GP Steam Glider では IKAROS サーバーが動かなくて・・・
理由は「乗り物」(ビークル)にアバターが乗っていたせいでした。IKAROS はアバターが「素」のまま飛ぶので、ゲートを通過したとき llDetectedKey() でアバターの Key を採る方法でよかったのですが、乗り物にのるとアバターは乗り物にリンクされた状態になるようで、llDetectedKey() ではアバターの Key を取得することができなかったんですよね~。
なので、llDetectedOwner() でオブジェクトのオーナーKey をとってあげればよかったわけです。
前回は計測サーバーがまったく反応しないでごめんなさいです、、、。
6月22日(日)に SL Japan GP が開催、、、って、あちゃーw 計測スクリプトまだ完成してないしw 基本的なところはクリアしてるんですけど、、、ごめーん LIN ちゃん。
前回の Steam Glider の模様
って、思えば、レース系のイベントを撮影・編集した動画が増えましたw
Wing of IKAROS
SUZAKU GP
なんか、IKAROS のときに作った計測スクリプトって、今みても、、、、おもしろいw
この手の計測スクリプトは List 関数をいかにうまく使うかなんですよね。自分で見ても、よくまぁ、こんなに List 系関数を使ってる、、、と思いました。
簡単なのは llHTTPRequest で Web サーバーにデータを POST して、結果だけもらうのがいいんですが、こだわったのは「SLの中だけでやる」だったんです。
途中挫折しそうになるくらい処理のおもーいスクリプトにしたりしたのですが、結果的には List 系関数の多用で「そこそこ」動くようになったんです。
おおよそ普段使わないような List 関数をレース計測サーバーでは使ってます。
llListSort は当たり前としても、llCSV2List, llList2List, llList2ListStrided, llDumpList2String, llDeleteSubList.... このあたりを多用してます。
for 文でまわすと「ピッタッ」と止まるような処理でも、List 操作だと比較的高速に処理してくれるんですよね~。
機会をみてご紹介したいなぁ、、、と思ってます。
理由は「乗り物」(ビークル)にアバターが乗っていたせいでした。IKAROS はアバターが「素」のまま飛ぶので、ゲートを通過したとき llDetectedKey() でアバターの Key を採る方法でよかったのですが、乗り物にのるとアバターは乗り物にリンクされた状態になるようで、llDetectedKey() ではアバターの Key を取得することができなかったんですよね~。
なので、llDetectedOwner() でオブジェクトのオーナーKey をとってあげればよかったわけです。
前回は計測サーバーがまったく反応しないでごめんなさいです、、、。
6月22日(日)に SL Japan GP が開催、、、って、あちゃーw 計測スクリプトまだ完成してないしw 基本的なところはクリアしてるんですけど、、、ごめーん LIN ちゃん。
前回の Steam Glider の模様
って、思えば、レース系のイベントを撮影・編集した動画が増えましたw
Wing of IKAROS
SUZAKU GP
なんか、IKAROS のときに作った計測スクリプトって、今みても、、、、おもしろいw
この手の計測スクリプトは List 関数をいかにうまく使うかなんですよね。自分で見ても、よくまぁ、こんなに List 系関数を使ってる、、、と思いました。
簡単なのは llHTTPRequest で Web サーバーにデータを POST して、結果だけもらうのがいいんですが、こだわったのは「SLの中だけでやる」だったんです。
途中挫折しそうになるくらい処理のおもーいスクリプトにしたりしたのですが、結果的には List 系関数の多用で「そこそこ」動くようになったんです。
おおよそ普段使わないような List 関数をレース計測サーバーでは使ってます。
llListSort は当たり前としても、llCSV2List, llList2List, llList2ListStrided, llDumpList2String, llDeleteSubList.... このあたりを多用してます。
for 文でまわすと「ピッタッ」と止まるような処理でも、List 操作だと比較的高速に処理してくれるんですよね~。
機会をみてご紹介したいなぁ、、、と思ってます。
登録:
投稿 (Atom)