ペットを作るには(6)

Gonbe Shan

2008年09月25日 10:38


先日LSL Convention 2008の事務局の人(?)から、出店のお誘いがありました。
で、公開したペットスクリプトで良ければ・・・と言う事で参加する事にしました。

ただ先に公開したスクリプトだと、フォロータイプの事が書かれていないので、今回は典型的なフォロータイプのスクリプトを作成して、公開しようと思います。

両方を上手く組み合わせると、本当のペット仕様として満足できるようになるでしょう。
と言っておいてなんですが・・・この2つを組み合わせるには、かなりの工夫も必要で、難易度高いかも?
移動方式が両者では違うので、先の歩行型にフォロー機能を入れ込むのは、けっこう厄介なんですね。
でも、そこまで入れ込んだスクリプトにすると、複雑になってしまうから、今回は別々に紹介したと言うわけです。><

まあペット作りの参考になればと思います。

今回の作品は「トンボ」です。



こんなオブジェクトを作成しました。
顔の部分にメインスクリプトを入れてます。
面倒なので他のパーツにはスクリプトは入れていません。
羽根を動かせば、けっこう本物のトンボのような動きになります。

これは実は1年ほど前に、ANIMANIAでハチドリとトンボの動きを見て、「あ~。。。これなら簡単に作れるな~。。。」って思って、その後トンボを作る気が無いから、作らなかった物なんですが、仕組み的には簡単なものです。

移動にはllSetPosを使ってますから、非物理オブジェクトですみますし、トンボのような物は動きが早いので、歩行型のように1歩1歩進めるなんて事をしなくても、一気に飛ばしても不自然になりません。
ハチドリやトンボ、ハチ等の昆虫類のように動きの早いタイプには、このスクリプトで十分でしょう。

詳しくは続きを見て下さい。
スクリプトのポイントは、センサーを使ってアバターの探知を行い、アバターが見つかれば一番近いアバターの傍に飛んで行って止まると言うものです。

もしアバターが見つからなかった場合は、基点をベースに10m四方の中をランダムにブンブンと飛び回っています。
アバターが見つかると、アバターの位置を元に追跡していきます。
ただし、基点から10m以上アバターが離れると追跡を止めて、元のようにランダム飛行に戻ります。

又、アバター追跡中も、アバターが停止しているならば、アバターを基点に周囲をランダムに動いています。今回は他のパーツにスクリプトを入れていませんが、一応羽根を動かしたりする場合も考えて、メッセージリンク部分は埋め込んでいます。
動作開始の場合と停止してダイアログを出す場合に、それぞれメッセージを飛ばしています。
後はスクリプト内のコメントでも見て参考にして下さい。

手っ取り早いのは、適当なオブジェクトにスクリプトをコピーして動かしてみる事ですね。
動きを見たほうが早いでしょう。


integer handle;
vector target_pos;//移動目標地点の座標
vector now_locate;//移動範囲の基点座標
vector agent_pos;//ターゲットアバターの位置座標
integer status = 0;//現在の状態をあらわす。1=移動,9=停止
key key_id;
float strength = 1.0;//LookUp用の定数
float damping = 1.0;//同上
float tm = 2.0;//移動間隔(タイマー用)
float x_max;//移動範囲のMAX値(X座標方向)
float y_max;//移動範囲のMAX値(Y座標方向)
float x_min;//移動範囲のMIN値(X座標方向)
float y_min;//移動範囲のMIN値(Y座標方向)
list main_menu = ["Moving"];//ダイアログボタン
integer chanel=-9999;
integer target_flg;//ターゲットアバターが存在=1、無し=0

integer pos_rand(integer max, integer min){ // 乱数の発生。min~maxの範囲
    return ((integer)llFrand(max-min)+min);
}
integer calc_target(vector pos,integer flg){//移動目標地点計算関数
    if(flg==0){//ターゲットアバターが存在しない場合。
        x_max=y_max=10.0;//基点から1m~10mの範囲内で移動
        x_min=y_min=1.0;
    }else{
        x_max=y_max=1.0;//アバターから0.5m~1mの範囲内で移動
        x_min=y_min=0.5;
    }
    float x_axi = pos_rand((integer)(x_max*10.0),(integer)(x_min*10.0))/10.0;
    float y_axi = pos_rand((integer)(y_max*10.0),(integer)(y_min*10.0))/10.0;
    target_pos = pos+<x_axi,y_axi,0.0>;//移動目標座標のセット
    return (TRUE);
}
integer disp_menu(){//ダイアログ表示関数
    llDialog(key_id, "\n<< Main Menu >>\n\n", main_menu, chanel);
    return(TRUE);
}

default
{
    state_entry()
    {
        key_id = llGetOwner();//オーナーのUUIDをセーブ
        now_locate = llGetPos();//Rezポイントを基点へセット
        state set_menu;
    }
    on_rez(integer param){
        llResetScript();
    }
}

state set_menu
{
    state_entry()
    {
        llSetTimerEvent(0);//タイマーは停止
        llSetPos(now_locate);//基点へ呼び戻し。
        status = 9;
        llMessageLinked( LINK_SET, status, "Stop", NULL_KEY);//各パーツへ停止を伝達
        handle = llListen(chanel, "", key_id, "");ダイアログ用リッスンフィルタ
        disp_menu();//ダイアログの表示
    }
    touch_start( integer total_number )
    {
        if(llDetectedKey(0)==key_id){
            disp_menu();
        }           
    }
    listen(integer ch, string name, key id, string message){
        if(message == "Moving"){
            llListenRemove(handle);//リッスンの停止
            status = 1;
            llMessageLinked( LINK_SET, status, "Start", NULL_KEY);//各パーツへ活動開始を伝達
            now_locate = llGetPos();//ボタンを押した地点を基点にセット
            target_flg=0;
            state moving;
        }
    }
    on_rez(integer param){
        llResetScript();
    }
}

state moving
{
    state_entry()
    {
        llSetTimerEvent(tm);//タイマー開始
        llSensorRepeat("",NULL_KEY,AGENT,10.0,PI,3.0);//リピートセンサー開始。3秒間隔、10m以内のアバターを探知
    }
 
    touch_start( integer total_number )
    {
        if(llDetectedKey(0)==key_id){
            llSensorRemove();
            state set_menu;//オーナータッチでメニューへ
        }
    }
    sensor(integer num_detected){
        vector temp_pos = llDetectedPos(0);//見つかった一番目のアバターの位置
        if(llVecDist(now_locate,temp_pos)<=10.0){//見つかったターゲットが、移動基点から10m以内なら追跡開始
            if(target_flg==0){//ターゲット無しだった場合は
                llSetTimerEvent(0);//タイマーは停止
                target_flg=1;//ターゲットが存在
            }
            if(llVecDist(agent_pos,temp_pos)>1.0 ){//ターゲットが1m以上動いたら
                vector agent_size = llGetAgentSize(llDetectedKey(0));//ターゲットの身長
                agent_pos = <temp_pos.x,temp_pos.y,temp_pos.z+(agent_size.z/2)>;//ターゲットの高さを補正。ターゲットの中心位置に身長の半分を加算
            }
            calc_target(agent_pos,target_flg);//ターゲットアバターを中心に半径1m以内でランダムに移動
            llSetPos(target_pos+<0.0,0.0,0.5>);//移動
            llLookAt(agent_pos, strength, damping);//ターゲットの方向を向く
        }else{
            target_flg=0;//ターゲットが存在しなくなった
            llSetTimerEvent(tm);//タイマー開始
        }
    }
    no_sensor(){
        if(target_flg==1){
            target_flg=0;//ターゲットが存在しなくなった
            llSetTimerEvent(tm);//タイマー開始
        }
    }
    timer(){
        calc_target(now_locate,target_flg);//移動目標自動計算
        llLookAt(target_pos+<0.0,0.0,-0.5>, strength, damping);//移動目標を向く
        llSetPos(target_pos);//移動
    }
    on_rez(integer param){
        llResetScript();
    }
}



尚前回同様に、このスクリプトは商品等にも自由に使っていただいてかまいませんが、サポート等は行いませんし、このスクリプトにて発生した、いかなる問題に関しても、Gonbeは一切の責任は負いませんのでご了承下さい。
また、内容に関する問い合わせにも応じられません。
あくまで自己責任にてご利用下さい。

それでは、SL界に一杯可愛いペット達が生まれますように。^ ^
スクリプトのお勉強メモ