製作補助系スクリプト

この記事はセカンドライフ技術系 Advent Calendar2016用の2回目の記事です。
1回目ではユーザー定義関数について書きました。

今回の記事ではスクリプトアイテムを製作時に役に立つツールを紹介して行こうと思います。
効率よく物作りをしようとツール系をいろいろ作りすぎて逆に時間がかかるという事よくありますよねw
そんな思いをしながら作って実際によく使っている汎用性の高い物をいくつか紹介します。

スクリプトアイテムを作るって事はスクリプターさんだしこんな物はすぐ作れるだろ!!みたいな突込みは無しでお願いしますw

リンク番号と面番号を知りたい時に便利なスクリプト

タッチされたリンク番号や面番号で違う処理をする時や、1面だけ色を変えたい処理の時などそれらを指定してコードを書くことになると思います。
その変える番号を知りたい時、編集画面で「リンク部位を編集」や「面を選択」を選んで見てもたまに嘘付かれませんか?w

そんな時はこのスクリプトをコピーしたオブジェクトにぶち込んでやれば解決です。

default
{
    state_entry()
    {
        llSetLinkTexture(LINK_SET,"acdada97-4086-8d91-960d-f31b2c8430b2",0);
        llSetLinkTexture(LINK_SET,"ee1313b7-451f-97c8-1aff-de8f9f86b120",1);
        llSetLinkTexture(LINK_SET,"fa050b2a-ed2e-098a-9a2d-6dabec00111f",2);
        llSetLinkTexture(LINK_SET,"5b3eacc6-796e-f6db-a920-91e30693875b",3);
        llSetLinkTexture(LINK_SET,"1bec7d07-e722-305b-876c-4b706be3b461",4);
        llSetLinkTexture(LINK_SET,"236ac5d5-8881-787d-c024-e5e1335121c0",5);
        llSetLinkTexture(LINK_SET,"11c6952d-637f-febd-5e7f-60f6fee9dad5",6);
        llSetLinkTexture(LINK_SET,"27d28e09-2fb1-5ae6-0072-86836044edf4",7); 
    }
    
    touch_start(integer total_number)
    {
        llOwnerSay("Link_Num = "+(string)llDetectedLinkNumber(0)+" Face_num = "+(string)llDetectedTouchFace(0));
        list data_list = llGetLinkPrimitiveParams( llDetectedLinkNumber(0),[PRIM_POS_LOCAL,PRIM_ROT_LOCAL,PRIM_SIZE] );
        llOwnerSay("pos="+(string)llList2Vector(data_list,0));
        llOwnerSay("Rot="+(string)llList2Rot(data_list,1));
        llOwnerSay("Size="+(string)llList2Vector(data_list,2));
    }
}

f:id:tomozoolomu:20161211091241p:plain
こんな感じで面ごとに色別のテクスチャが貼り付けられます。
上に0~7まで数字が書いてあるのはこのスクリプトを入れた8面メッシュをHUDとして付けています。
色だけで面番号がわかるまでは付けてると便利です。
タッチした面のリンク番号、面番号、ローカルな座標、ローカルな回転、リンクプリムのサイズもメッセージとして出ます。

Object: Link_Num = 5 Face_num = 4
Object: pos=<0.00110, 0.44539, 0.00855>
Object: Rot=<0.00000, 0.00000, -1.00000, 0.00000>
Object: Size=<0.34836, 0.06446, 1.00000>

いらない部分は先頭に//でも入れて消せばいいです。

面を分けずにボタンを付けたい時に便利なスクリプト

f:id:tomozoolomu:20161211094600j:plain:w320
この画像の用に1枚のテクスチャにボタンがいくつも付いていてタッチされた場所によって処理を変えたい場合ありますよね。
その場合テクスチャやUVのどの部分をタッチされたか判別してそれにあった処理をすると思います。
そんな時のテンプレートスクリプトです。

integer count = 0;
vector count_1;
vector count_2;

default
{
    touch_start(integer total_number)
    {
        if(llDetectedLinkNumber(0) > 1)
        {
            llResetScript();
        }else if(llDetectedLinkNumber(0) == 0)
        {
            vector touched_uv =  llDetectedTouchUV(0);
            if(count == 0)
            {//1回目取得
                count_1 = touched_uv;
                count++;
            }else if(count == 1)
            {//2回目取得
                count_2 = touched_uv;
                string say_msg = "uv.x > "+(string)count_1.x + " && uv.x < "+ (string)count_2.x+" && "+
                                 "uv.y > "+(string)count_1.y + " && uv.y < "+ (string)count_2.y;
                llOwnerSay(say_msg);
                llResetScript();
            } 
        }
    }
}

通常プリムの板にスクリプトをぶち込んでテクスチャを張りボタン部分の「左下」「右上」の順に2回クリックします。

Object: uv.x > 0.148981 && uv.x < 0.278454 && uv.y > 0.440016 && uv.y < 0.546142

こんな感じのメッセージが出てきますのでコピペして

default
{
    touch_start(integer total_number)
    {
        vector uv =  llDetectedTouchUV(0);
        if(uv.x > 0.148981 && uv.x < 0.278454 && uv.y > 0.440016 && uv.y < 0.546142)
        {//ボタン1の処理
            
        }else if(uv.x > 0.183588 && uv.x < 0.265684 && uv.y > 0.793917 && uv.y < 0.865924)
        {//ボタン2の処理
            
        }
    }
}

こんな感じで使ってもらえばいいと思います。

※ちなみに上記のスクリプトのままでは違う面の同じUV箇所をタッチしても反応してしまいますのでリンク番号と面も一致してるか確認してから処理してください。

パーティクルやテキストの色指定に便利なスクリプト

パーティクルやフローティングテキストの色は淡い色や微妙な色を付けたい時ありますよね。
編集画面のカラーピッカーでLSLを選べばvector型の数値は出るのですが1発でコピペできないのでちょっと不便です。
そんな時に面の色をvector型で教えてくれるスクリプト

default 
{  
    touch_start(integer total_number) 
    { 
        vector color = llGetColor(llDetectedTouchFace(0));  
        llOwnerSay("Color Vector = [ "+(string)color+" ]"); 
    } 
}

箱にスクリプト突っ込んで色を変えてタッチするだけです。

Object: Color Vector = [ <0.30980, 0.55686, 0.69020> ]

こんな感じで教えてくれます。


他にも製作補助ツール的な物は沢山ありますが用途が絞られてる物が多いので汎用性の高そうなものを紹介しました。
また機会があればいろいろ紹介していきますね。

便利なスクリプトの書き方(ユーザー定義関数)

この記事はセカンドライフ技術系 Advent Calendar2016用の記事です。
www.adventar.org

去年も書こうとは思ってたのですが書けなかったので今年こそと思い数年ぶりにブログを書いてみます。

はてなブログでは簡単にLSLに色付けが出来ると知りブログの開設からやってしまいましたw

ユーザー定義関数とは

さて、タイトルのユーザー定義関数ですがざっくり言うと何度も同じ処理書くの大変だから同じ処理は一箇所でまとめてやっちまえ的な奴です!
オリジナルの関数を作って使えると思ってもらえば良いと思います。
いくつかメリットを上げるとすれば
 ・コードが短くなりメモリの節約になる。
 ・どこに何が書いてあるかわかりやすい。
 ・アップデートや修正などのメンテナンスが楽になる。

自分が書いた1年前のスクリプトを開いて どこに何が書いてあるかさっぱりわからん!なんて事ないですか?僕はよくありますw
きちんとユーザー関数を使って行けばかなりスッキリしたコードが書けるのでスクリプトがぐちゃぐちゃになってしまう人に特にお勧めします。

ユーザー定義関数には2種類あります。

第一の型

//ユーザー定義関数
say(string msg)
{
    llSay(0,msg);    
}
//メインコード
default
{
    state_entry()
    {
        say("Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        say("Touched.");
    }
}

まずユーザー定義関数には変数を入れることが出来ます。
say(string msg)の部分ですね。 sayというのはオリジナルの関数名です。好きにつけてしまって構いません。
その後の()の中ですが必要に応じて書いていきます。string,integer,float,vector,rotation,listなんでも入ります。
(string msg,integer num,list data_list) のように,で区切って記述していきましょう。
この場合は say()というオリジナル関数が呼び出されたらmsgという変数の中に入っている文字をllSayしてるだけですね。

第二の型

//ユーザー定義関数
integer get_num(integer num_1 , integer num_2)
{
    integer answer = num_1 + num_2;
    return answer;
}
//メインコード
default
{
    touch_start(integer total_number)
    {
        integer number_1 = 100;
        integer number_2 = 200;
        integer total_number = get_num(number_1,number_2);
        llSay(0,(string)total_number);
    }
}

似てるようでちょっと違いますね。
違いはユーザー定義関数の中で処理した結果が返事となって返ってくる所です。
returnの後に返したい変数を書き;で閉めてください。
その返したい型に合わせてユーザー関数の前にiniteger や stringといった型名を書けばOKです。
上のコードではnumber_1(100)とnumber_2(200)を足した数字が返ってきます。

おまけのカラー&テクスチェンジスクリプト

最後におさらいとして無理やり気味にユーザー定義関数を2種類使ったダイアログのスクリプトを作ってみます。

//カラーリスト
list coler_list = [
"Red",<1,0,0>,
"Blue",<0,0,1>,
"Green",<0,1,0>
];
//テクスチャーリスト
list texture_list = [
"tex_1","ee1313b7-451f-97c8-1aff-de8f9f86b120",
"tex_2","fa050b2a-ed2e-098a-9a2d-6dabec00111f",
"tex_3","5b3eacc6-796e-f6db-a920-91e30693875b"
];
//グローバル変数
string dialog_state;
integer dialog_channel;
integer Handle;
//ダイアログを設定
set_dialog(key id)
{
    string dialog_msg = "menu";
    list dialog_list = ["Color_chenge","Tex_change","Close"];
    if(dialog_state == "color_chenge")
    {//dialog_stateがカラーチェンジの時 それ用のリストを作る
        dialog_list = get_dialog_list(coler_list,[" ","<< Back","Close"]);
    }else if(dialog_state == "tex_chenge")
    {//dialog_stateがテクスチェンジの時 それ用のリストを作る
        dialog_list = get_dialog_list(texture_list,[" ","<< Back","Close"]);
    }
    llDialog(id,dialog_msg,dialog_list,dialog_channel);
    llSetTimerEvent(120.0);
}
//ダイアログのボタンリストを作成
list get_dialog_list(list data_list_1,list data_list_2)
{//data_list_2とdata_list_1 のボタン名だけを抜き出したリストをくっつけて返す。
    return data_list_2 + llList2ListStrided(data_list_1,0,-1,2);
}
//メインコード
default
{
    state_entry()
    {
        //ダイアログのチェンネルをランダムに指定
        dialog_channel = (integer)llFrand(20000) + 200; 
    }
    
    on_rez(integer param)
    {
        llResetScript();   
    }

    touch_start(integer total_number)
    {
        //メインメニューでダイアログを表示
        Handle = llListen(dialog_channel, "", "", "");
        dialog_state = "main_menu";
        set_dialog(llDetectedKey(0));
    }
    
    timer() 
    {
        //リッスンを停止 
        llListenRemove(Handle); 
        llSetTimerEvent(0); 
    }
    
    listen(integer channel, string name, key id, string msg) 
    {
        if(msg == "Close")
        {
            llSetTimerEvent(0.1);
        }else if(msg == " ")
        {
            set_dialog(id);
        }else if(msg == "<< Back")
        {
            //メインメニューでダイアログを表示
            dialog_state = "main_menu";
            set_dialog(id);
        }else if(msg == "Color_chenge")
        {//カラーチェンジ 
            //カラーチェンジメニューでダイアログを表示
            dialog_state = "color_chenge";
            set_dialog(id);
        }else if(msg == "Tex_change")
        {//テクスチャチェンジ
            //テクスチャチェンジメニューでダイアログを表示
            dialog_state = "tex_chenge";
            set_dialog(id);                
        }else
        {
            integer index_color = llListFindList(coler_list,[msg]);
            integer index_tex = llListFindList(texture_list,[msg]); 
            if(index_color != -1 && dialog_state == "color_chenge")
            {//カラーチェンジの色指定
                vector color = llList2Vector(coler_list,index_color+1);
                llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_COLOR,ALL_SIDES,color,1.0]);  
            }else if(index_tex != -1 && dialog_state == "tex_chenge")
            {//テクスチャチェンジのUUID指定
                key tex_uuid = llList2Key(texture_list,index_tex+1);
                llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_TEXTURE,ALL_SIDES,tex_uuid,<1,1,0>,<0,0,0>,0]);
            }
            set_dialog(id);//ダイアログを再表示
        }
    }
}

ちょと無理やり使ったのでスマートな書き方ではなくなってしまいますがご愛嬌でw
よくあるダイアログのページが分断化されていていろんな事が出来るメニュー画面。
listenイベント内でif(msg == "Color_chenge"){の所でメニューリストを作りダイアログを出してもいいのですが、ユーザー関数でダイアログ関係を一つにまとめてしまったほうがメンテナンスや仕様変更に柔軟に対応出来ると思います。

他にも便利な使い方は沢山あるのですが長くなってしまうのでこの辺でw
また何かネタがあれば書こうと思いますので何かリクエストがあればコメントなりIMなりくださいませ。