2008年09月07日
ペットを作るには(5)
そして、説明用のサンプルスクリプトを入れたものを見て、なんと!満足してしまったようです。^ ^;
まあ、一般の人から見ると、サンプル用のスクリプトレベルの動きでも、「凄いー!」と言う感じなんでしょうね。
そこで、この記事の締めくくりと言う意味で、歩行型のサンプル・スクリプトを公開してしまいます。^ ^
まず次のような感じのオブジェクトを作ってください。

4速歩行のペットになります。
SSの物はサンプル用に通常プリムだけで、適当に作りました。
ネコなのか熊なのか???
まあ、頭と胴体と足があれば何でも良いです。
ちなみに2速歩行でも構いません。
気をつけて欲しいのは、後で示すサンプルスクリプトの場合、ルートオブジェクトの向きによって、正しい姿勢で動かない場合があります。
これは方向を向かせるのに、llLookAt()と言う関数を使っているからです。この関数の場合、正面がZ軸方向になります。
分かりにくいので、簡単に正面を正しく向かせる方法を書いておきます。
まず作成で三角錐等を地面に出して下さい。下の絵のように、とがった方を上に向けて出てくると思います。
写真は分かりやすいように、各面に色をつけてあります。

このとがった方角(=Z軸方向)が、このオブジェクトの正面になります。
そこで、水平方向にオブジェクトの正面が向くように、Y軸を横に90度回転させます。

これでとがった先が水平方向を向きました。
この状態で、プリムの形状を球にでも変えてしまえば良いです。
このプリムをルートオブジェクトにしておけば、常に青い面を上にしてとがった先の方向に移動してくれます。
実際に試して見て下さい。
ちなみにサンプルでは顔の球をルートプリムに指定していますが、別に胴体でも構いません。
ルートプリムに後から記述する、メイン・スクリプトを入れてくれれば良いです。
で、サンプル・スクリプトを入れたペットは、次のような動きになります。
ランダムに歩いたり、走ったりして勝手に動き回ります。小さめに作ると、けっこう可愛いです。^ ^
スクリプトに関しては続きをご覧下さい。
実際の商品では、もっと色々なチェックや、細かい動きの制御までしています。
もし商品等へ適用する場合は、各自で十分なテストを行い、問題の無いように確認した上でお使い下さい。
当方では一切責任は負いかねますので、あらかじめご了承の程お願いいたします。
このペットには3つのスクリプトを使っています。
まずはルートプリムに入れるスクリプトです。
これがペットを動かしているメインのスクリプトです。
手足が無くても動き回ります。
基本的にはllSetPosと言う命令で動いています。従ってオブジェクトは非物理です。
では以下にメインスクリプトをそのまま記載します。
****************************************************************************************************
// Main V1.0 2008/09/7
integer handle;
vector destination;//移動目標地点の座標兼個別移動時の基点座標
vector now_locate;//移動範囲の基点座標
integer status = 0;//現在の状態をあらわす。1=移動,9=停止
key key_id;
integer k;//パーツを動かす為の掛け声。1,2,1,2とメッセージを送る。
integer sp;//移動歩数
float sp_const=0.2;//移動スピード用変数歩幅を現す。
float strength = 1.0;//LookAT用の定数
float damping = 1.0;//同上
integer t;//タイマー用のカウンタ
float tm = 1.0;//移動間隔(タイマー用)
float x_max=5.0;//移動範囲のMAX値(X座標方向)
float y_max=5.0;//移動範囲のMAX値(Y座標方向)
float x_min=0.1;//移動範囲のMIN値(X座標方向)
float y_min=0.1;//移動範囲のMIN値(Y座標方向)
list main_menu = ["Moving"];//ダイアログボタン
integer chanel=-9999;
float x_add;//1歩毎の移動座標増分変数
float y_add;//1歩毎の移動座標増分変数
float z_add=0;//歩行型なので高さの移動は0固定
integer r_cnst=0;//歩く=0、走る=2。パーツへのメッセージでIndexに対応
integer pos_rand(integer max, integer min){ // 乱数の発生。min~maxの範囲
return ((integer)llFrand(max-min)+min);
}
integer calc_target(){//移動目標地点計算関数
float x_axi;//計算用ワーク
float y_axi;//計算用ワーク
vector now_destination=destination;//今居る場所の座標をセーブ
x_axi = pos_rand((integer)(x_max*10.0),(integer)(x_min*10.0))/10.0;//移動先の相対増分X座標求める。基点は設置基準点になる。
y_axi = pos_rand((integer)(y_max*10.0),(integer)(y_min*10.0))/10.0;//移動先の相対増分Y座標求める。基点は設置基準点になる。
destination = now_locate+<x_axi,y_axi,0.0>;//設置基準点に対する移動先座標をセット
sp = llFloor(llVecDist(now_destination, destination)/sp_const);
//現在地と次の移動地点間の距離(m)を求め、歩幅で除算して、移動に必要な歩数を求める。
if(sp==0){//spが0だと次の式でエラーになるから補正
sp=1;
}
x_add = (destination.x - now_destination.x)/sp;//1歩毎のX座標増分
y_add = (destination.y - now_destination.y)/sp;//1歩毎のY座標増分
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 = destination = llGetPos();//Rezポイントを基点へセット
state set_menu;
}
on_rez(integer param){
llResetScript();
}
}
state set_menu
{
state_entry()
{
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();
}
}
link_message(integer sender_num, integer st, string msg, key id){
if(st == 1){
k=0;
state move_set;
}
}
listen(integer ch, string name, key id, string message){
if(message == "Moving"){
llListenRemove(handle);
now_locate = llGetPos();//ボタンを押した地点を基点にセット
status = 1;
llMessageLinked( LINK_SET, status, "Start", NULL_KEY);//各パーツへ活動開始を伝達
}else{
disp_menu();
}
}
on_rez(integer param){
llResetScript();
}
}
state moving
{
state_entry()
{
t=0;//タイマー用カウンタの初期化
destination = llGetPos();//現在地を移動の起点にセット
llSetTimerEvent(tm);//タイマーセット
}
touch_start( integer total_number )
{
if(llDetectedKey(0)==key_id){
llSetTimerEvent(0);
state set_menu;
}
}
timer(){
if(++t>sp){//タイマーで移動した歩数tが、予定歩数を超えているか?
state move_set;//移動目標へ到達した場合、再度目標設定へ。
}else{//各パーツを交互に同期を取って動かすための掛け声セット。
if(k==1){
k=2;
}else{
k=1;
}
destination = destination+<x_add,y_add,z_add>;//次の一歩で進む先の座標をセット
llMessageLinked( LINK_SET, 51, (string)(k+r_cnst), NULL_KEY);
//各パーツへ掛け声を伝達。歩くの場合はk+0、走るならk+2
llSetPos(destination);//一歩進む。
}
}
on_rez(integer param){
llResetScript();
}
}
state move_set
{
state_entry()
{
llSetTimerEvent(0);//タイマー停止
if(pos_rand(3,1)==2){//乱数で「歩く」と「走る」の切り替え
sp_const=0.5;//歩幅を50cmにセット
r_cnst=2;//各パーツへの伝達用
tm=0.5;//歩く時の間隔
}else{
sp_const=0.2;//歩幅を20cmにセット
r_cnst=0;//各パーツへの伝達用
tm=1.0;//歩く時の間隔
}
calc_target();// 移動目標計算関数呼び出し
llLookAt(destination, strength, damping);//移動目標へ体の向きを向ける
state moving;
}
touch_start( integer total_number )
{
if(llDetectedKey(0)==key_id){
state set_menu;
}
}
on_rez(integer param){
llResetScript();
}
}
****************************************************************************************************
コメントが入ってますから、特に説明も必要無いと思います。
ポイントはcalc_target()の中にあります。
乱数を用いて、移動基点から任意のX,Yの相対座標を求めています。
その座標地点が移動目標になりますが、そのままllSetPosで移動すると、瞬間で移動してしまいます。
そこで、現在地点と移動目標地点間の距離を求め(llVecDistで求めている)、その距離を歩幅で割って、移動地点へ到達するために必用な歩数を計算しています。
そして、1歩毎に変動するX座標とY座標の変動値を求めておいて、後はタイマーを使って一歩一歩llSetPosで必用歩数分進んでいるのです。
このまま入れてくれれば、最初にダイアログが出ますので、「Moving」のボタンを押してください。
5m四方の範囲をランダムに歩いたり走ったりして動き回ります。
タッチすると停止してダイアログが表示されます。
次に4本の足に入れるスクリプトが2つあります。
1つ目は足の位置を調整するスクリプトです。
****************************************************************************************************
list wing_pos = [<0.20079, 0.05560, -0.08351>,<0.20078, 0.05560, -0.07229>,<0.20078, 0.05560, -0.10229>,<0.20079, 0.05560, -0.14829>,<0.17493, 0.05560, -0.03829>];
default
{
link_message(integer sender_num, integer st, string msg, key id){
if(st == 51){
llSetPrimitiveParams([PRIM_POSITION, llList2Vector(wing_pos,(integer)msg)]);
}else if(st==9){
llSetPrimitiveParams([PRIM_POSITION, llList2Vector(wing_pos,0)]);
}
}
on_rez(integer param)
{
llResetScript();
}
}
****************************************************************************************************
最初の部分のwing_pos と言うリストに、足の位置座標が直に書かれています。
0番目が停止した時の足の位置
1番目と2番目が歩行時の足の位置で、メインから1,2、1,2と合図によって切り替わります。
3番目と4番目が走る時の足の位置で、メインから3,4、3,4と合図が送られてきます。
この座標は実際の作成したペットの足毎に値を調べて書き換えてください。
このまま使うととんでもない場所にパーツが飛んでしまいますから。
次に足の角度調整のスクリプトです。
****************************************************************************************************
list wing_rot = [<0.00000, 0.00000, 0.00000, 1.00000>,<0.00002, -0.13919, 0.00001, 0.99027>,<0.00001, 0.11319, 0.00002, 0.99357>,<0.00001, 0.34201, 0.00002, 0.93970>,<0.00002, -0.48482, 0.00001, 0.87461>];
default
{
link_message(integer sender_num, integer st, string msg, key id){
if(st == 51){
llSetLocalRot(llList2Rot(wing_rot,(integer)msg));
}else if(st==9){
llSetLocalRot(llList2Rot(wing_rot,0));
}
}
on_rez(integer param)
{
llResetScript();
}
}
****************************************************************************************************
これも同様に、wing_rot と言うリストの中に、足の回転角度座標が書いてあります。
位置と同様に、
0番目が停止した時の足の位置
1番目と2番目が歩行時の足の位置で、メインから1,2、1,2と合図によって切り替わります。
3番目と4番目が走る時の足の位置で、メインから3,4、3,4と合図が送られてきます。
になっています。
こちらもリスト内の座標は、各足毎に実際に調べて書き換えてください。
問題は、リストの中の位置座標と回転角度座標の求め方ですが、Gonbeの場合はパーツが少ないので、非常に原始的な作業で調べてスクリプトの中に書き込んでいます。
具体的には、各足に以下のスクリプトを入れておき、ポーズを作るたびに、各足にタッチして座標を表示させて転記してます。
****************************************************************************************************
default
{
touch_start(integer total_number)
{
llOwnerSay("Pos="+(string)llGetLocalPos()+"Rot="+(string)llGetLocalRot());
}
}
****************************************************************************************************
これも単純なスクリプトです。
上のようなポーズを作り、そこで各足にタッチして、座標を表示させてメモしておき、スクリプトの中に記述してます。
もし、パーツが多いとか、面倒だーと思う人は、自分でもっと簡単なツールを作れば良いと思います。
尚、このスクリプトは商品等にも自由に使っていただいてかまいませんが、先に書きましたとおり一切のサポート等は行いませんし、このスクリプトにて発生した、いかなる問題に関しても、Gonbeは一切の責任は負いませんのでご了承下さい。
また、内容に関する問い合わせにも応じられません。
あくまで自己責任にてご利用下さい。
それでは、SL界に一杯可愛いペット達が生まれますように。^ ^
llAddToLandPassList
【注意】Bulk Permissions機能は使っては駄目!
HTTP-INを使ってみた。
オブジェクトへの指示
ペットを作るには(6)