LSLって何?

LSL(the scripting language of Second Life)とは、
secondlifeのスクリプトで利用される言語です。
それをちょっとやってみようかと。健忘録も兼ねまして。
エレベータ(もどき?)を作ってみる
ただ移動するだけならば飛んだりsitで瞬間移動したりすればいいのですが、
そこをあえて時間を掛けて移動したいということでエレベータ(見たいな物)を
つくってみました。

同じ場所を行ったりきたりするスクリプトです。
いつもどおり難しいことはできませんので
プリムにsitした状態でtouchするとプリムが目的の場所まで
連れて行ってくれます。
「まっすぐ進むだけだとつまらない」というご意見を受け、
ちょっとだけジグザグに移動します。
※sitせずにクリックするとプリムだけ
どっかに行ってしまいます。

以下、そのスクリプトの紹介


//1階にいるか2階にいるかを判別します。
integer updownflg = 0;

default
{
state_entry()
{
llSetText("sit & touch bar!", <1,0,0>, 1.0);
}

touch(integer total_number)
{
//もし1階にいたら・・・
if(updownflg == 0){
//go to 2nd floor
//現在の場所を取得
vector foo = llGetLocalPos();

//for文のための変数
integer x;
//oku ni 5m
//0.5mずつ10回移動する→5m(一気に移動すると瞬間移動になってしまうため)
for (x = 0; x < 10; x++)
{
//現在位置に対して、ベクターでx軸に0.5m足す。
//ベクターの単位はmのようです。
//赤い軸がX軸、矢印が向いているほうに数字が増えていく
llSetPos(foo + <0.5, 0, 0>);
//移動後の位置を取得し、再度変数に格納する
foo = llGetLocalPos();
}

//同様にY軸の移動、とZ軸の移動を行う
//hidari ni 10m
for (x = 0; x < 20; x++)
{
//緑がY軸
llSetPos(foo + <0, 0.5, 0>);
foo = llGetLocalPos();
}
//ue ni 8m
for (x = 0; x < 16; x++)
{
llSetPos(foo + <0, 0, 0.5>);//青がZ軸
foo = llGetLocalPos();
}

//移動が終わったらフラグを2階に設定
updownflg = 1;

} else {
//2階にいる場合は1階に移動する
//手順は、きたときと逆にするだけ
//go to 1st floor
vector foo = llGetLocalPos();
integer x;

//ue ni -8m
for (x = 0; x < 16; x++)
{
llSetPos(foo + <0, 0, -0.5>);
foo = llGetLocalPos();
}

//hidari ni -10m
for (x = 0; x < 20; x++)
{
llSetPos(foo + <0, -0.5, 0>);
foo = llGetLocalPos();
}

//oku ni -5m
for (x = 0; x < 10; x++)
{
llSetPos(foo + <-0.5, 0, 0>);
foo = llGetLocalPos();
}

updownflg = 0;

}

}
}



もしかして、これだと移動中にプリムをクリックすると
さらにそこから移動が加算されるかも・・・
テストしていませんので、利用される方は移動中判定フラグ等を
入れてみてください。って無責任。
| 土地建物 | 00:32 | comments(0) | trackbacks(0)
翻訳ツールを作ってみる
以前ブログに書いたHTTPREQUESTの応用で
翻訳ツールを作成してみました。

とはいっても、lslで翻訳エンジンを作るというのは
あまりにも難易度が高いため(LSL以外の仕組みでも無理ですが・・・W)
googleの翻訳サービスを利用しています。

まず、前提条件として
・phpの実行可能なサーバを持っていること
・そのサーバでfsockopenが利用可能なこと
となります。
これは別にphpでなくてもいいのですが、
今回のサンプルがphpを利用している関係です。

仕組みの概略としては、

1、チャットのメッセージをlistenで拾う
2、拾ったメッセージをhttprequestで自分のサーバに設置したphpのプログラムに引き渡す。
3、phpのプログラムでgoogleの翻訳サービスを呼び出して翻訳をしてもらう
4、phpが受け取った翻訳結果をhttpresponseとしてlslでうけとる
5、lslで受け取った邦訳結果をllSayで表示する


という流れになってます。

ここで紹介するスクリプトは、なるべく単純なものにしています。
phpのプログラムに変更を加えることで、特定文字列は個別に翻訳するといったこともできます。

■lsl
httprequestのサーバはご自分で用意してものに置き換えてください。
このスクリプトを適用なプリムに貼り付けて、HUDに設定するなどしてご利用ください。


key toucher;
integer LHandle;
integer activeflg;
key requestid;

default
{
//REZしたタイミングでスクリプトをリセットすることで
//オーナーを正しく判別しています。
//自分で作った場合は関係ないのですが、
//人にあげたりしたときのための対応です。
on_rez(integer reset)
{
llResetScript();
}

state_entry()
{
//llListenでチャットメッセージを拾います
//llListenはsimに与える負荷が高くなるそうなので
//かならずフィルター処理をします
//この場合、チャンネルは0ですべてを拾っていますが
//llGetOwnerで持ち主からのメッセージを受け取るように
//フィルタリングしています
LHandle = llListen(0,"",llGetOwner(),"");
//一旦リスナーを無効にしておきます
llListenControl(LHandle, FALSE);
//翻訳機能が無効である事をllSetTextデ表示します
llSetText("stop", <255,255,255>, 1.0);
activeflg = 0;
}

//プリムをタッチすることで翻訳機能の有効/無効を切り替えます
touch(integer total_number)
{
//タッチできるのをオーナーだけに限定します
toucher = llDetectedKey(0);
if(toucher == llGetOwner())
{
//フラグを確認して、有効にするか無効にするかを判断します
if(activeflg == 0){
activeflg = 1;
//リスナーを有効にします
llListenControl(LHandle, TRUE);
llSetText("active", <255,255,255>, 1.0);
} else {
activeflg = 0;
//リスナーを無効にします
llListenControl(LHandle, FALSE);
llSetText("stop", <255,255,255>, 1.0);
}
}
}

//このブロックでは、チャットメッセージを拾った時の処理を記述します。
listen(integer channel, string name, key id, string message)
{
//llHTTPRequestでphpを呼びます
//念のためurlエンコードをしています
requestid = llHTTPRequest("http://ご自分のドメイン/trans_j2e.php?word="
+ llEscapeURL(message),[HTTP_METHOD,"GET"],"");
}

//HTTPRequestの結果を受け取るブロックです
http_response(key request_id, integer status, list metadata, string body)
{
if (request_id == requestid)
{
//翻訳結果を画面に表示させます
//自分以外のエージェントからも見えるようにチャンネルは0を使います
llSay(0,body);
} else {
llSay(0,(string)status+" error");
}
}

}



■PHPのプログラム(trans_j2e.php)


<?php
/*
ご自分で用意されたサーバに設置してください。
PHPが実行可能である必要があります。
また、fsockopenが実行可能な設定に鳴っている必要があります。
*/

//変換処理先
//http://www.google.co.jp/language_tools?hl=ja

$word = urlencode($_GET['word']);
$requrl = "translate.google.com";
$reqmethod = "get";
$reqpath = "translate_t";

//言語変換の設定パラメータ
//hlとlangpairを変更する事で他の言語への変換が可能
$reqdata = "text=" . $word ."&hl=ja&langpair=ja|en";

//リクエストの送信と受信
$fp = fsockopen($requrl, 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)¥n";
} else {
$out = $reqmethod." /".$reqpath."?".$reqdata." HTTP/1.1¥r¥n";
$out .= "Host: ".$reqhost."¥r¥n";
$out .= "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja-jp; rv:1.8.1) Gecko/20061010 Firefox/2.0¥r¥n";
$out .= "Connection: Close¥r¥n¥r¥n";

fwrite($fp, $out);
while (!feof($fp)) {
$res .= fgets($fp, 128);
}
fclose($fp);
}

//レスポンスから変換後の文字列のみを抽出
$mystring = $res;
$findme = '<div id=result_box dir=ltr>';
$pos = strpos($mystring, $findme);
if ($pos === false) {
echo "transelation error";
} else {
$step1 = substr($mystring,$pos + 27);
}
$mystring = $step1;
$findme = '</div>';
$pos = strpos($mystring, $findme);
if ($pos === false) {
echo "transelation error";
} else {
$step2 = substr($mystring,0,$pos);
}

//翻訳後の文字列を出力
echo $step2;
?>
| Communications | 21:09 | comments(16) | trackbacks(0)
パーティクルで産卵
アバターから卵を産ませたかったのですが
プリムで作成しても産卵にはならない・・。
ということで、パーティクルを利用してその方法を模索してみました。

結果的にはそこそこそれっぽく見えるんじゃないかと
いうものができました。(←自己満足)
ついでに、パーティクルについてもいくつか学べたことが
あるのでまとめて見ます。

以下は、ポロッと1つだけパーティクルが発生する例です。


default {
state_entry() {
llParticleSystem( [
PSYS_PART_START_SCALE, <0.3,0.3,0.0>,
PSYS_PART_END_SCALE, <0.1,0.1,0.0>,
PSYS_PART_START_COLOR, <1.0,1.0,1.0>,
PSYS_PART_END_COLOR, <1.0,1.0,1.0>,
PSYS_PART_START_ALPHA, 1.0,
PSYS_PART_END_ALPHA, 1.0,
PSYS_SRC_BURST_PART_COUNT, 1,
PSYS_SRC_BURST_RATE, 7.0,
PSYS_SRC_TEXTURE, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
PSYS_PART_MAX_AGE, 2.0,
PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_DROP,
PSYS_SRC_ACCEL, <0.0,0.0,-5.0>,
PSYS_SRC_BURST_SPEED_MIN, 2.3,
PSYS_SRC_BURST_SPEED_MAX, 5.3,
PSYS_PART_FLAGS,
( PSYS_PART_EMISSIVE_MASK |
PSYS_PART_WIND_MASK |
PSYS_PART_FOLLOW_VELOCITY_MASK |
PSYS_PART_INTERP_COLOR_MASK |
PSYS_PART_INTERP_SCALE_MASK )
] );
}
}


設定値の説明を簡単に。

//粒の発生時のサイズと消失時のサイズの指定
PSYS_PART_START_SCALE, <0.3,0.3,0.0>,
PSYS_PART_END_SCALE, <0.1,0.1,0.0>,

///粒の発生時の色と消失時の色の指定
PSYS_PART_START_COLOR, <1.0,1.0,1.0>,
PSYS_PART_END_COLOR, <1.0,1.0,1.0>,

///粒の発生時の透明度と消失時の透明度の指定
PSYS_PART_START_ALPHA, 1.0,
PSYS_PART_END_ALPHA, 1.0,
※1.0で不透明のようです。

//粒の同時発生数と発生間隔(秒?)
PSYS_SRC_BURST_PART_COUNT, 1,
PSYS_SRC_BURST_RATE, 7.0,

//粒に貼り付けるテクスチャの指定
PSYS_SRC_TEXTURE, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
xxxxには、貼り付けたいテクスチャのUUIDを入れてください。
インベントリの任意のテクスチャを右クリックするとUUIDが取得(クリップボードにコピー)できます。

//粒が消失するまでの時間(秒?)
PSYS_PART_MAX_AGE, 2.0,

//粒の落ちるパターンの指定
PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_DROP,

//粒の加速度?
PSYS_SRC_ACCEL, <0.0,0.0,-5.0>,
※落ちていくスピードと解釈しました。

サンプルをここまで設定するとうまい具合に落ちてくれたので
満足してしまいました。
後の設定情報については、以下に情報があります。
いずれ調べてみようかなあ・・・。

http://rpgstats.com/wiki/index.php?title=Ja:particles
http://rpgstats.com/wiki/index.php?title=Ja:llParticleSystem

後、パーティクルラボが非常に参考になります。
The Particle Laboratory
http://slurl.com/secondlife/Teal/192/66/26/

※パーティクルについての説明とサンプルの紹介があります。
あと、テクスチャとスクリプトの販売もしてます。
私はここでテクスチャを購入しました。
webのテレポートで着いたところにあるバルーンにrideして
目的地の操作盤でlabを選ぶと連れて行ってもらえますよ。
| アバター | 15:53 | comments(10) | trackbacks(0)
HTTPREQUEST
secondlifeの世界から外部の世界(web)に
アクセスするhttprequestを試してみました。
結構Request自体は、細かく送れるようなので
いろいろ試してみる価値はあるかも。

ただ、responseで受け取れる情報は、
「The body is currently limited to 2049 bytes.」
となっているので、webサーバ側のプログラムをうまく作らないと
できることが限られてしまいます。
タイマーを仕掛けたオブジェクトを作成し、
メールの新着アラートぐらいは作れそうですね。
webサーバ側のプログラムは別途作成が必要ですが・・・。

以下、サンプルです。



key requestid;

default
{
state_entry()
{
llSetText("Httprequest is not yet sent", <255,255,255>, 1.0);
}

touch(integer nd)
{
requestid = llHTTPRequest("http://www.google.co.jp/",[HTTP_METHOD,"GET"],"");
}

http_response(key request_id, integer status, list metadata, string body)
{
if (request_id == requestid)
{
llSetText(body, <255,255,255>, 1.0);
} else {
llSetText((string)status+" error", <255,255,255>, 1.0);
//llSay(0,(string)status+" error");
}
}
}


※ここではgoogleを呼んでますが、特に意味ありません。

touchイベントでllHTTPRequestを呼んでます。
リクエストは、非同期通信としてレスポンスを受け取ります。
レスポンスが来た時点で、http_responseイベントが発動します。
ちなみに、request_idでNULL_KEYが帰ってきたときはエラーのようです。
bodyは、タグを含めて2049バイトしか受け取れません。

http://rpgstats.com/wiki/index.php?title=LlHTTPRequest
http://rpgstats.com/wiki/index.php?title=Http_response
| Communications | 04:51 | comments(15) | trackbacks(0)
プリムの回転でアバターにマバタキをさせたい2
訂正です。
blogに書いたスクリプトと
primに設定したスクリプトが違ってました。

結果としては、
・アタッチメントとして取り付けた際に基準となる座標軸がずれてしまう事
は、間違いで
ちゃんとずれないでマバタキさせることができました!
(とはいっても片目ですが・・・)

原因は
llGetLocalRot、llSetLocalRot

llGetRot、llSetRot
と書き間違えていたこと
blogに書いた内容が正解です。

・・・なんで間違えたかな?

| アバター | 02:20 | comments(0) | trackbacks(0)
プリムの回転でアバターにマバタキをさせたい
アバターにまばたきをさせたいと思い、
こんなスクリプトを書いてみました。
結果としては、失敗に終わりましたが・・・。

敗因は2つ。
・右目と左目を同期させて動かすことができなかった事。
  (できるにはできたんですが、顔に取り付ける段になって失敗)
・アタッチメントとして取り付けた際に基準となる座標軸がずれてしまう事

両方とも、自分の力不足が原因なのですが、
調べきれず無念のリタイヤ。

ただ、いくつか今後も利用できるノウハウがあると思うので
書いたスクリプトの解説をします。


float minBlinkTime = 0.5;
float maxBlinkTime = 12.0;

float randBetween(float min, float max){
return llFrand(max - min) + min;
}

integer Mabataki(float prm1){
rotation x_180 = llEuler2Rot( <0, prm1 * 180 * DEG_TO_RAD, 0> );
rotation new_rot = llGetLocalRot() * x_180;
llSetLocalRot(new_rot);
return 1;
}

default
{
state_entry(){
llSetTimerEvent(randBetween(minBlinkTime,maxBlinkTime));
}

timer(){
llSetTimerEvent(0.0);
Mabataki(1.0);
llSleep(0.1);
Mabataki(-1.0);
llSetTimerEvent(randBetween(minBlinkTime,maxBlinkTime));
}
}


上記のプログラムは、ランダムな値をタイマーの発生間隔に設定し
タイマーのイベントで、プリムの軸に回転を与える、というものです。
まばたきは、半円形で中を中空にしたプリムを目の上にかぶせて、
それ目にかぶせるように回転させて実現しようとしました。

randBetween()は、少数型の数値を最大値と最小値を指定して発生させる
関数です。lsl-wikiのまんまです。
これで、まばたきの発生タイミングをランダムにしています。
llFrandは、引数で与えた値を最大値とする乱数を発生させる関数。
返り値は、floatですが、wikiにintegerを受け取るサンプルも書いてあります。
http://rpgstats.com/wiki/index.php?title=LlFrand

Mabataki()は、プリムを回転させる関数です。
引数に回転させる量を指定します。
prm1 * 180 * DEG_TO_RAD
180度に引数の値を係数として掛けて、
さらに角度をラディアンに変換する定数を掛けてます。
llEuler2Rot()は、vecter型からrotation型に変換する関数。
得た値は、別のrotationに掛けることで、ベクターで指定した変化の度合いを加えることができます。

llGetLocalRot()で疑問だったのがこれ。
llGetRotという関数もあるですが、いまいちどっちを使ったらいいのかわかりません。
LlGetRotの説明にはアタッチメントで利用する場合は、アバターのrotationが返ってくるとあるので
llGetLocalRotにしました。
http://rpgstats.com/wiki/index.php?title=LlGetRot
http://rpgstats.com/wiki/index.php?title=LlGetLocalRot

まぶたは、顔の1プリムとしてリンクしたいので、まぶた自身のrotationがほしかったのです。
llSetLocalRot()は、上記で算出した変更後のrotationの値をプリムに設定する処理。
右目の子供として左目をリンクさせることで、両目に同じ変化を与えることがでます。
ただ回転軸上に子プリムを配置しないと妙な回転をしてしまうので注意。

で、これを顔に貼り付けると顔ごと回転してしまったり、右目しか回転しなかったりとなってしまうので
顔の別のパーツのアタッチメントとしてまぶたを取り付けることにしたのですが、
アタッチメントにした途端、回転の軸がずれてしまい、指定した変化の値があらぬ方向に影響し・・・。
この辺の調整をうまくやる方法はどうしたらよいのですかね・・・。

ただものの角度を回転させるだけとか、向きを気にせず利用できるものとかには
使えるかもしれませんね、・・・オウムの頭とか???

ファンネルもつくれるかな?
| アバター | 01:43 | comments(0) | trackbacks(0)
定数のこれなに?
PI

パイ=円周率
そのままか・・・。
| 基本の基本 | 01:13 | comments(1) | trackbacks(0)
Annoyances(迷惑)
やっちゃいけない事のいろいろ
http://rpgstats.com/wiki/index.php?title=Annoyances
| 基本の基本 | 01:11 | comments(0) | trackbacks(0)
制御構造
for, do-while, if-else, return, while は、他の言語と対して変わらない
foreachとかあれば便利なんだけど・・・
jump,stateは、個人的にはなじみが無い

jump
goto文ってことかな
ループを強制的に抜けたい時とかに使える
jump getOut;
@getOut;
「@」を付けて飛び先指定
jump

state
状態を表す
defaultも状態の一つ。最初から定義されている。
自分で状態を作る場合は、関数と同じ。
state anotherstate{
xxxxxx;
}

state anotherstate;
で状態が切り替わる。

defaultも状態だからstate default;で元に戻るという意味に。
イベントハンドラは同じで、その処理が異なる場合に利用できます
もちろん、グローバル変数を作って、ifで判別してもいいけど。
state
| 基本の基本 | 01:10 | comments(1) | trackbacks(0)
変数
integer bar = 3;
スコープがあるので、どのファンクションからでも呼びたい場合は
defaultの外に定義する。型の指定を忘れずに。

あんまり説明する事ないかな。
| 基本の基本 | 01:04 | comments(0) | trackbacks(0)
CALENDAR
S M T W T F S
      1
2345678
9101112131415
16171819202122
23242526272829
30      
<< September 2007 >>
RECOMMEND
SELECTED ENTRIES
CATEGORIES
ARCHIVES
RECENT COMMENT
LINKS
PROFILE