2010年07月02日
LSLのC#実装は凍結らしいです
先日、ちょっと触れたC#ですが、どうやら凍結らしいです。6月30日のBabbageさんのオフィスアワーで発表されたみたいです。
User:Babbage Linden/Office Hours/2010 06 30
LSLを無理にC#化すると、既存のスクリプトを動かすために、LSLとC#の両方のサポートを続けていかなくてはならなくなりますし、保守性の面から考えるとこれでよかった気がします。なにか、他の有意義なことにリソースを割いてくれるといいですね( ̄∇  ̄ )
User:Babbage Linden/Office Hours/2010 06 30
LSLを無理にC#化すると、既存のスクリプトを動かすために、LSLとC#の両方のサポートを続けていかなくてはならなくなりますし、保守性の面から考えるとこれでよかった気がします。なにか、他の有意義なことにリソースを割いてくれるといいですね( ̄∇  ̄ )
2010年06月27日
LSLに導入されるC#ってバージョン1.0の予定なの?
Babbage Lindenさんのオフィスアワーのログを見てると、C#の話題が時々出てるようですが、どうも、最初のリリースでは C# 1.0(一部2.0も?)をサポートするような発言が散見されます。
↓ オフィスアワーのログはこのページの下のほうにあります
User:Babbage Linden
C#は、ちょっと前に4.0が出ました。それぞれのバージョンで結構大胆に新機能を追加しているのですが、特に3.0で大量の新機能を追加したため、2.0と3.0では、ほとんど別言語のようになっています。
C#をメインで使っている身としては正直1.0では物足りないんですよねえ。1.0はJavaとよく似た普通のオブジェクト指向言語といった感じで、クラスライブラリの充実度以外は特に際立った長所はなく、下手したらLSLで書くより面倒になる可能性もあります。2.0ではジェネリック[1]や、匿名関数[2]が入りますが、これも3.0のラムダ式[3]の便利さに比べるとインパクトが落ちますね。
実は、LSLのC#導入に向けて、C#入門の連載でもしようかと思っていたのですが、2.0以前は極めて普通の言語なんで、ちょっと書くのは見合わせようかなと思ってます。
語句の説明
1. 引数やフィールドの型を特定せず、様々な型で特定ロジックを使いまわせる機能
2. 名前を持たない関数、使用したいタイミングで定義して使う
3. 匿名関数を簡単に書ける構文
↓ オフィスアワーのログはこのページの下のほうにあります
User:Babbage Linden
C#は、ちょっと前に4.0が出ました。それぞれのバージョンで結構大胆に新機能を追加しているのですが、特に3.0で大量の新機能を追加したため、2.0と3.0では、ほとんど別言語のようになっています。
C#をメインで使っている身としては正直1.0では物足りないんですよねえ。1.0はJavaとよく似た普通のオブジェクト指向言語といった感じで、クラスライブラリの充実度以外は特に際立った長所はなく、下手したらLSLで書くより面倒になる可能性もあります。2.0ではジェネリック[1]や、匿名関数[2]が入りますが、これも3.0のラムダ式[3]の便利さに比べるとインパクトが落ちますね。
実は、LSLのC#導入に向けて、C#入門の連載でもしようかと思っていたのですが、2.0以前は極めて普通の言語なんで、ちょっと書くのは見合わせようかなと思ってます。
語句の説明
1. 引数やフィールドの型を特定せず、様々な型で特定ロジックを使いまわせる機能
2. 名前を持たない関数、使用したいタイミングで定義して使う
3. 匿名関数を簡単に書ける構文
2010年02月20日
Eclipse+VrapperでのLSL開発
さぶろーは、以前、LSLをViで開発する環境をつくったのですが、ViではLSL関数の引数までは補完されないので、引数を忘れた場合は、いちいちリファレンスを引いていました。これがメンドくさくて、引数まで補完してくれるEclipseへ乗り換えも考えたのですが、やっぱりViのキーバインドを使いたくて躊躇する毎日・・・。
ところが、最近EclipseのViプラグインで、Vrapperというのがあることを知りました。
Eclipseのキーバインドをvim風にできるVrapperが素晴らしすぎる件について
VrapperはViのキーバインドを使えるようにしつつ、補完も使えるということで、EclipseのLSLプラグインの補完も効くかどうか試してみることにしました( ̄∇  ̄ )
さぶろーが知ってるLSLプラグインは、LSL Plusと、ByronStar SLの2つ。
LSL Plusの方は、単体テストまでコード上で書くことができたりして、大規模開発を志向しているようです。ただ生成されるファイルが多かったり、直接LSLを編集せず、LSLPという独自のファイルを編集して、そこからLSLを生成する仕組みだったりと、メンドくさいことも多そうな感じ。
逆にByronStarは、単純にLSLを編集するだけの機能のようで、お手軽な感じで、個人的にはこちらの方が好きになりました。ただ、こちらは、2007年から開発が止まっているようなんで、そこだけ注意が必要です。
試してみたところ、両プラグインとも、Viモード中でも補完は有効でした。さぶろーとしては、Vrapper+ByronStarの環境を使っていこうかなと思います。
LSLはC#が導入される予定なので、そうなるとEclipseでは厳しくなるでしょうから、さぶろーの中では、今回の環境はそれまでの繋ぎっていう感じです。C#が出たら、使い慣れたVisualStudioやMonoDevelopで開発出来るようになるとうれしいんですが、どうなるんでしょうか( ̄∇  ̄ )
ところが、最近EclipseのViプラグインで、Vrapperというのがあることを知りました。
Eclipseのキーバインドをvim風にできるVrapperが素晴らしすぎる件について
VrapperはViのキーバインドを使えるようにしつつ、補完も使えるということで、EclipseのLSLプラグインの補完も効くかどうか試してみることにしました( ̄∇  ̄ )
さぶろーが知ってるLSLプラグインは、LSL Plusと、ByronStar SLの2つ。
LSL Plusの方は、単体テストまでコード上で書くことができたりして、大規模開発を志向しているようです。ただ生成されるファイルが多かったり、直接LSLを編集せず、LSLPという独自のファイルを編集して、そこからLSLを生成する仕組みだったりと、メンドくさいことも多そうな感じ。
逆にByronStarは、単純にLSLを編集するだけの機能のようで、お手軽な感じで、個人的にはこちらの方が好きになりました。ただ、こちらは、2007年から開発が止まっているようなんで、そこだけ注意が必要です。
試してみたところ、両プラグインとも、Viモード中でも補完は有効でした。さぶろーとしては、Vrapper+ByronStarの環境を使っていこうかなと思います。
LSLはC#が導入される予定なので、そうなるとEclipseでは厳しくなるでしょうから、さぶろーの中では、今回の環境はそれまでの繋ぎっていう感じです。C#が出たら、使い慣れたVisualStudioやMonoDevelopで開発出来るようになるとうれしいんですが、どうなるんでしょうか( ̄∇  ̄ )
2009年08月15日
Web連携するときのオブジェクトの識別子
LSLCon2009用に、小物を作ろうと思ってて、Web上にオブジェクトを識別するためのIDを登録したかったんだけど、何を登録すればよいか迷ってしまいました。
今回作成するものは、フリーで配布するのですが、それが設置された場合にllHttpRequestでサーバに通信して、データベースに自分の情報を登録する仕様です。その際、他のオブジェクトと被らないように主キーとなるものが必要なのですが・・・。
オブジェクトはコピー不可で配布するという前提で、ぱっと思い浮かんだ主キー候補は、以下の3つ。
しかし、UUIDはRezするごとに変更されます。今回配布するものは、サーバに1度登録した場合、ずっとそのデータを引き継ぎたいのでこれではダメです。位置情報も位置を変更されると同様にデータを引き継げないのでダメです。オーナーのUUIDを主キーにすると、1人のオーナーが複数設置できなくなるのでこれも避けたい。
で結局、全部ダメ orz
今のところ、オブジェクトの説明欄に、他のオブジェクトと被らない文字列を入れてもらって、それを主キーにしようかと考えていますが、なんかこういう場合に、うまく使えるIDとかないんでしょうかね( ̄~ ̄)
今回作成するものは、フリーで配布するのですが、それが設置された場合にllHttpRequestでサーバに通信して、データベースに自分の情報を登録する仕様です。その際、他のオブジェクトと被らないように主キーとなるものが必要なのですが・・・。
オブジェクトはコピー不可で配布するという前提で、ぱっと思い浮かんだ主キー候補は、以下の3つ。
- UUID
- 位置情報
- オーナーのUUID
しかし、UUIDはRezするごとに変更されます。今回配布するものは、サーバに1度登録した場合、ずっとそのデータを引き継ぎたいのでこれではダメです。位置情報も位置を変更されると同様にデータを引き継げないのでダメです。オーナーのUUIDを主キーにすると、1人のオーナーが複数設置できなくなるのでこれも避けたい。
で結局、全部ダメ orz
今のところ、オブジェクトの説明欄に、他のオブジェクトと被らない文字列を入れてもらって、それを主キーにしようかと考えていますが、なんかこういう場合に、うまく使えるIDとかないんでしょうかね( ̄~ ̄)
2009年04月28日
インワールドからWebサーバへポーリング
さて、今のクリエモンには、配布先を一覧できるページがあったりします。非常にみつけにくいところにありますが・・・。
配布場所一覧
でも、配布場所は今1カ所しかないので、HTMLにベタ書きで書いてあるんですね。これが、次のクリエモンでは、複数のベンダーがWebサイトにポーリングして、常に最新の配布先一覧を表示できるようになります。今日は、そのやり方を書いてみます。
まず、LSL側のコードです。llSetTimerEventで、1時間毎に、llHTTPRequestを実行してます。バージョン情報を渡したくて、GETのクエリで渡そうかとも思いましたが、何となくPOSTにしました・・・。
余談ですが、今のクリエモンを持っていって頂いた方はクリエモンの名前が「Creamon Ver.ST」になっていたのにお気づきかと思います。このSTは、「先行テスト版」の略なんです、気づきました? さらに前のバージョンでOT→「お試し版」というのを配布してた時期もあります。上記コードでは、バージョンTUとなってますね。正式版のバージョン名が推測できるかもしれませんね^^
次は、サーバ側です。何回か書いてるとおり、サーバ側はC#で書いてます。
先頭で、セカンドライフから送られてきたヘッダから、RegionとPositionの情報を取得しています。ヘッダの詳しい情報は、WikiのllHTTPRequestのページにのってます。
つづいてバージョン情報を取得しますが、ここで定数で定義してある現在のバージョンと等しいかチェックして、異なる場合は処理を終了してます。古いバージョンがいつまでも残らないための対策というわけです。
Sim名は、「Jin Ho (264448, 233984) 」みたいな情報が入ってくるので、SubStringで「(」から後を消してから、Trimで一応スペースを削除してます。Positionは、「(173.009827, 75.551231, 60.950001)」みたいな形式でくるので、かっこやスペースを消して、カンマで分割後、それぞれをMath.Round関数で整数に四捨五入。どうせURLにしか使わないので、最後に「/」を区切り文字にして連結してます。ここは、匿名メソッドを使ってます。あんまりこういう書き方はしないので分かりにくいかもしれません。
無事解析が済んだら、各情報をmemcachedに格納しています。本来はDBやファイルなどに格納した方が消えなくてよいのですが、テーブル定義がめんどくさかったので、memcachedに格納することにしちゃいました( ̄∇ ̄) まあ、消えても1時間後には復活しますしね。
表示時には、これらの情報から、SLURLを生成する必要があります。StringBuilderを使って、こんな感じでやってます。
注意点としてはSim名にはスペースが入ることがあるので、URLエンコードが必要なことくらいですね。
SLとWebの連動にはいろんな可能性がありそうですよね。最近は、PHPとLSLが連携する商品が普通に出てくるようになりましたし、これからもあっと驚くようなものが出てくるかもしれませんね(^^)
配布場所一覧
でも、配布場所は今1カ所しかないので、HTMLにベタ書きで書いてあるんですね。これが、次のクリエモンでは、複数のベンダーがWebサイトにポーリングして、常に最新の配布先一覧を表示できるようになります。今日は、そのやり方を書いてみます。
まず、LSL側のコードです。llSetTimerEventで、1時間毎に、llHTTPRequestを実行してます。バージョン情報を渡したくて、GETのクエリで渡そうかとも思いましたが、何となくPOSTにしました・・・。
default
{
state_entry()
{
llSetTimerEvent(60 * 60);
}
timer()
{
llHTTPRequest("http://www.creamon.net/○○○", [HTTP_METHOD, "POST", HTTP_MIMETYPE, "application/x-www-form-urlencoded"], "version=TU");
}
}
余談ですが、今のクリエモンを持っていって頂いた方はクリエモンの名前が「Creamon Ver.ST」になっていたのにお気づきかと思います。このSTは、「先行テスト版」の略なんです、気づきました? さらに前のバージョンでOT→「お試し版」というのを配布してた時期もあります。上記コードでは、バージョンTUとなってますね。正式版のバージョン名が推測できるかもしれませんね^^
次は、サーバ側です。何回か書いてるとおり、サーバ側はC#で書いてます。
// 生のヘッダ文字列を取得
string region = Request.Headers["X-SecondLife-Region"];
string position = Request.Headers["X-SecondLife-Local-Position"];
// versionが古ければ、格納しない
string version = Form["version"].Trim();
if (!version.Equals(VersionConst.CURRENT)) { return; }
// Sim名取得
region=region.Substring(0, region.IndexOf("(")).Trim();
// Position取得
position = position.Replace("(", "").Replace(")", "").Replace(" ", "");
string[] positionMembers = position.Split(',');
positionMembers = Array.ConvertAll<string, string>
(positionMembers, new Converter<string, string>
(delegate(string s)
{
return Math.Round(double.Parse(s)).ToString();
}
)
);
position = string.Join("/", positionMembers);
:
:
// memcachedに登録する処理
(省略)
先頭で、セカンドライフから送られてきたヘッダから、RegionとPositionの情報を取得しています。ヘッダの詳しい情報は、WikiのllHTTPRequestのページにのってます。
つづいてバージョン情報を取得しますが、ここで定数で定義してある現在のバージョンと等しいかチェックして、異なる場合は処理を終了してます。古いバージョンがいつまでも残らないための対策というわけです。
Sim名は、「Jin Ho (264448, 233984) 」みたいな情報が入ってくるので、SubStringで「(」から後を消してから、Trimで一応スペースを削除してます。Positionは、「(173.009827, 75.551231, 60.950001)」みたいな形式でくるので、かっこやスペースを消して、カンマで分割後、それぞれをMath.Round関数で整数に四捨五入。どうせURLにしか使わないので、最後に「/」を区切り文字にして連結してます。ここは、匿名メソッドを使ってます。あんまりこういう書き方はしないので分かりにくいかもしれません。
無事解析が済んだら、各情報をmemcachedに格納しています。本来はDBやファイルなどに格納した方が消えなくてよいのですが、テーブル定義がめんどくさかったので、memcachedに格納することにしちゃいました( ̄∇ ̄) まあ、消えても1時間後には復活しますしね。
表示時には、これらの情報から、SLURLを生成する必要があります。StringBuilderを使って、こんな感じでやってます。
2009/07/21追記:2行目は「.Append(Uri.EscapeDataString(Region))」が正解でした。詳しくは、「クリエモン、URLエンコード周りのバグを修正しました」をご覧ください m(_ _)m
StringBuilder("http://slurl.com/secondlife/")
.Append(HttpUtility.UrlEncode(Region))
.Append("/")
.Append(Position)
.ToString();
注意点としてはSim名にはスペースが入ることがあるので、URLエンコードが必要なことくらいですね。
SLとWebの連動にはいろんな可能性がありそうですよね。最近は、PHPとLSLが連携する商品が普通に出てくるようになりましたし、これからもあっと驚くようなものが出てくるかもしれませんね(^^)
2008年12月17日
オブジェクトのコンテンツに自分自身を入れて再帰Rezしたい
オブジェクトのコンテンツに自分自身を入れて、そのコンテンツ内の自分をRez。Rezされたオブジェクトが、またコンテンツ内の自分をRezというのをやりたかったんですが・・・。
とりあえず、Rezするスクリプトを作成
コンテンツにRez用スクリプトと、自分自身を入れる。

タッチ連打、あれ・・・。2つめでRezが止まってしまいました。

実は2つめのObjectには、コンテンツにスクリプトしか入ってないのです。これは絶対不可避な問題で、最初の1つ目には自分を入れれないからです。たとえば、下の1つ目のObjectを持ち物に入れて、それを2つ目のオブジェクトの代わりに使っても、今度は3つ目で同じように止まってしまいます。100回入れ直せば100回Rezできると思いますが、無限にRezさせるのはできません(多分)。
そこで考え方を変えて、Rez後にObjectを渡してす方法を試してみました。これなら煩わしい下準備もなく無限にRez可能になるはず!
スクリプトはこちら。
で、タッチ連打~。成功したようです(・∀・)
とりあえず、Rezするスクリプトを作成
default
{
touch_start(integer total_number)
{
llRezObject("Object",
llGetPos() + <0.0, 0.0, 1.5>,
ZERO_VECTOR,
ZERO_ROTATION,
0);
}
}
コンテンツにRez用スクリプトと、自分自身を入れる。

タッチ連打、あれ・・・。2つめでRezが止まってしまいました。

実は2つめのObjectには、コンテンツにスクリプトしか入ってないのです。これは絶対不可避な問題で、最初の1つ目には自分を入れれないからです。たとえば、下の1つ目のObjectを持ち物に入れて、それを2つ目のオブジェクトの代わりに使っても、今度は3つ目で同じように止まってしまいます。100回入れ直せば100回Rezできると思いますが、無限にRezさせるのはできません(多分)。
そこで考え方を変えて、Rez後にObjectを渡してす方法を試してみました。これなら煩わしい下準備もなく無限にRez可能になるはず!
スクリプトはこちら。
default
{
touch_start(integer total_number)
{
llRezObject("Object",
llGetPos() + <0.0, 0.0, 1.5>,
ZERO_VECTOR,
ZERO_ROTATION,
0);
}
// Rezが終わった後に呼ばれるイベント
object_rez(key id)
{
// idにRezしたkeyが入ってくるのでObjectを渡す
llGiveInventory(id, "Object");
}
}
で、タッチ連打~。成功したようです(・∀・)

2008年10月25日
Babbage Linden さんが来ました
LSL Conのイベントで、SLのMonoエンジンを担当しているBabbage Lindenさんが来てました。

いろいろ話してましたけど、さぶろーが気になったのは以下のような話
さぶろーは英語あんまり得意じゃないので間違いもあるかも。おかしな点があればコメントで指摘してください^^

いろいろ話してましたけど、さぶろーが気になったのは以下のような話
- Mono版のSilverLightである、MoonLightのサンドボックス実行をまねて、SLに安全なC#用のサンドボックスを作りたいと言う話。
- C#がなんでも出来すぎるから、サンドボックス内で実行させてSimサーバーの安全性を確保するということでいいのかな?
- Previewグリッドで動いてる「プリムをHttpサーバにする機能」で、一時的なURLを使っているのを、DDNSみたいな仕組みを作って、静的URLでアクセス可能にするという話
- リンデンは時々こういうスケールのでかい話をする。面白い発想だと思いました。
***追記*** これは勘違いでした。そういう仕組みを自分で作って対処してくださいということでした。 - Http通信を使えば、スクリプトのライブラリ化が出来るよという話
- このあたりは英語よく分からなかったから間違えてるかも
***追記*** これも勘違いでした(勘違い率50%突破)。なにかライブラリ化を促進するための仕組みが用意されるようです。どんな形式なのかはいまいち分かりませんでしたが、Import文とかで別のLSLを参照したりとか出来るといいですね。 - Fakeさんが、Http Server in Primの返す、Content-Type Headerを、text/plain以外に設定できるようにお願い → Babbageさんは、Kellyさんに言ってみますとのこと
- Content-Typeが自由に設定できるようになれば、text/htmlを設定してブラウザでプリムからもらったHTMLを閲覧できるなどが出来るようになる! これも面白い発想でした。プリムの周りで起こったことをRSSで配信とかも出来るようになるかなと妄想。
さぶろーは英語あんまり得意じゃないので間違いもあるかも。おかしな点があればコメントで指摘してください^^
2008年10月19日
LSLCon2008 開幕
いやー始まりましたね。
http://lsl-con.org/2008/

初日のイベントは、アイテムデモやスクリプター会など、どれも濃い内容でした。さらに常時閲覧可能な展示物や、スクリプトを学びながら遊べるクエストなど、イベント以外にも盛りだくさんの内容となっています。
さぶろーが、SLを始めて最初に体験した大きなイベントが去年のLSLConで、このイベントは特別な思い入れがあります。前回は見てるだけでしたが、今回は半年かけて制作してきたアバターを展示しています。
昨日は制作が間に合わなくて、展示ブースは同人誌を落としたコミケブースみたいになってましたけど、今日やっと動く状態になったので、コソッと設置してきました。
今はまだバグも多く、テスト段階なので、大規模な告知はなしです。興味のある方は直接会場で試してみてください^^
LSLCon2008会場
http://slurl.com/secondlife/New%20Script/230/183/30
http://lsl-con.org/2008/

初日のイベントは、アイテムデモやスクリプター会など、どれも濃い内容でした。さらに常時閲覧可能な展示物や、スクリプトを学びながら遊べるクエストなど、イベント以外にも盛りだくさんの内容となっています。
さぶろーが、SLを始めて最初に体験した大きなイベントが去年のLSLConで、このイベントは特別な思い入れがあります。前回は見てるだけでしたが、今回は半年かけて制作してきたアバターを展示しています。
昨日は制作が間に合わなくて、展示ブースは同人誌を落としたコミケブースみたいになってましたけど、今日やっと動く状態になったので、コソッと設置してきました。
今はまだバグも多く、テスト段階なので、大規模な告知はなしです。興味のある方は直接会場で試してみてください^^
LSLCon2008会場
http://slurl.com/secondlife/New%20Script/230/183/30
2008年10月16日
VimでLSLを書く環境を整える
SabroがLSLを書く時は、今まで、LSL-Editorを使ってきたのですが、これは日本語を入力するとフォントが崩れてしまいますし、さらに最近フォントを弄ったら英語の入力でもフォントがおかしくなるようになってしまったので(Sabroの環境だけ?)、エディタをVimに変更してみることにしました。
Vimはシンタックスカラーのハイライトが出来るほか、Vim7からは(間違い:dictファイルによる補完はVim7からではないようです)lslEditorほど親切でないにしてもキーワードの自動補完が出来るようになっているので、、そこそこ楽にコーディングできます。
以下環境の作り方 (Windows用Kaoriya版向け)
シンタックスハイライト
自動補完
正直間に合うかどうか微妙なところ。(_ _ ||
追記:
あっ。自動補完にはautocomplpopが便利ですよ。というかこれがないと個人的には使い物になりません。
Vimはシンタックスカラーのハイライトが出来るほか、
以下環境の作り方 (Windows用Kaoriya版向け)
シンタックスハイライト
- Shillというページに、lsl.vimファイルがあるのでダウンロード
- lsl.vimにはバグがあるので、I sort my thought... LSL用Vim Syntaxファイルを参考にして修正。
- lsl.vimを、Vimのruntime/syntaxフォルダへほりこむ ノ ̄∇ ̄)ノ
- runtime/filetype.vimの、「augroup filetypedetect、augroup END」の間に以下を追記
au BufNewFile,BufRead *.lsl setf lsl
自動補完
- シンタックスハイライトと同じくShillから、lsl.dictファイルをダウンロード
- vim.dictファイルを適当なフォルダに設置。Sabroはそのへんの作法とかよく知らないんですが、runtime/dictというフォルダを作ってそこへ入れときました。
- vimrcに以下を追記(dictのあるフォルダは環境によって変えてください)
autocmd FileType lsl set dictionary+=$VIMRUNTIME/dict/lsl.dict
正直間に合うかどうか微妙なところ。(_ _ ||
追記:
あっ。自動補完にはautocomplpopが便利ですよ。というかこれがないと個人的には使い物になりません。
2008年08月21日
LSLにおけるGPLの効力ってどれくらいの範囲?
久方ぶりです。
最近は更新頻度が低くて何もしてないように見えますが、実はしっかりアバター制作を進めてます。まあ、アバター制作にすでに5ヶ月かけてる時点でアレですが・・・。
で、アバターはアニメ調にしようと考えてるので、デフォルトのアニメーションではフィットしないなあと思い、AO(アニメーションオーバーライド)することにしたのです。
AOのスクリプトを1から作るのは結構大変そうだと思い、Tinyアバターのボディークラッシャーに入っているスクリプトを使わせてもらおうかとソースを見てみたのですが、これってGPLだったんですね Σ( ̄□  ̄ ) てっきり、修正BSDかMITあたりかと思っていました。
以下、スクリプトのコメントを一部抜粋。
実は、セキュリティの関係でスクリプトの1つを閲覧不可にしたかったのですが、GPLの効力がどれくらい効くのか、よく分からなくて困っています。
まず、AOスクリプトと、ListenやLinkMessageで通信を行う場合は、それらのソースは公開する必要があると思います(多分)。では、通信が発生しなければどうなんでしょうか? 同じプリム内にあるスクリプトは公開しなければいけない気もしますけどねぇ。
たとえば、AOのスクリプトに対して、別のスクリプトの取り得る格納位置として以下のような場合があると思います。
これらのうち、ソース公開の必要があるのはどこまででしょうか?4は公開の必要はないと思いますが・・・。ちなみにSabroが絶賛制作中のアバターでは、上記3のスクリプトを非公開にしたいです。
Windowsアプリとかで、GPLのDBをアプリケーションに付属して配布する場合でも、ソース公開の義務があった気がするので、やっぱりダメだろうなぁ (_ _ |||
やはり車輪の再発明が必要か (つ д`)
それにしても、こうしてみると上記のようなパターンに柔軟に対応できるLSL用のライセンス体系とかほしくなりますね(^^)
最近は更新頻度が低くて何もしてないように見えますが、実はしっかりアバター制作を進めてます。まあ、アバター制作にすでに5ヶ月かけてる時点でアレですが・・・。
で、アバターはアニメ調にしようと考えてるので、デフォルトのアニメーションではフィットしないなあと思い、AO(アニメーションオーバーライド)することにしたのです。
AOのスクリプトを1から作るのは結構大変そうだと思い、Tinyアバターのボディークラッシャーに入っているスクリプトを使わせてもらおうかとソースを見てみたのですが、これってGPLだったんですね Σ( ̄□  ̄ ) てっきり、修正BSDかMITあたりかと思っていました。
以下、スクリプトのコメントを一部抜粋。
//
// Don't ask me for tech support. I won't give it.
// Copyright (C) 2004 Francis Chung
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 U
実は、セキュリティの関係でスクリプトの1つを閲覧不可にしたかったのですが、GPLの効力がどれくらい効くのか、よく分からなくて困っています。
まず、AOスクリプトと、ListenやLinkMessageで通信を行う場合は、それらのソースは公開する必要があると思います(多分)。では、通信が発生しなければどうなんでしょうか? 同じプリム内にあるスクリプトは公開しなければいけない気もしますけどねぇ。
たとえば、AOのスクリプトに対して、別のスクリプトの取り得る格納位置として以下のような場合があると思います。
- 同じプリム内
- 同じオブジェクトの別のプリム内
- 別のオブジェクトに入っているが、1つの商品として扱われる場合(オブジェクトと操作用HUDとか)
- まったく関係ない別のオブジェクトに入っている場合
これらのうち、ソース公開の必要があるのはどこまででしょうか?4は公開の必要はないと思いますが・・・。ちなみにSabroが絶賛制作中のアバターでは、上記3のスクリプトを非公開にしたいです。
Windowsアプリとかで、GPLのDBをアプリケーションに付属して配布する場合でも、ソース公開の義務があった気がするので、やっぱりダメだろうなぁ (_ _ |||
やはり車輪の再発明が必要か (つ д`)
それにしても、こうしてみると上記のようなパターンに柔軟に対応できるLSL用のライセンス体系とかほしくなりますね(^^)
2008年01月29日
LSLスクリプトからアバターを回転させるには
LSLからアバターを動かすのは、わりと簡単。
だいたい次のような方法があります。
でも、LSLからアバターを回転させるのは難しいです。
回転の関数として、llLookAt、llRotLookAt、llSetTorqueなどがありますが、これらはアタッチメントから使っても、オブジェクトが回転する、もしくは何も起こらないだけでアバターには作用しません。また、自分は今までllPushObjectの第3引数に値を設定すれば、出来るんじゃないかと思ってたのですが、これも駄目でした。第2引数での移動は出来るのに・・・ (-_- )
llPushObject(key id, vector impulse, vector ang_impulse, integer local)
JIRAにも要望があったので多分関数一発では出来ないんでしょう。
New Feature -> LSL -> Agent -> Rotation Control
なので、どうしても回転させたい場合は、オブジェクトに座らせて、そのオブジェクトを回転させることになります。できればSpirittanでは、移動制御時にオブジェクトに座らせたくないのですが、ダッシュ中の方向転換とかを、きれいに出来るようにするには、座り方式でいくしかないみたいです。う~ん困った。
だいたい次のような方法があります。
- アタッチメントからllMoveToTarget、llSetForce、llApplyImpulse等を使う
- llPushObjectを使う
- オブジェクトや乗り物に座らせて、そちらを動かす
でも、LSLからアバターを回転させるのは難しいです。
回転の関数として、llLookAt、llRotLookAt、llSetTorqueなどがありますが、これらはアタッチメントから使っても、オブジェクトが回転する、もしくは何も起こらないだけでアバターには作用しません。また、自分は今までllPushObjectの第3引数に値を設定すれば、出来るんじゃないかと思ってたのですが、これも駄目でした。第2引数での移動は出来るのに・・・ (-_- )
llPushObject(key id, vector impulse, vector ang_impulse, integer local)
JIRAにも要望があったので多分関数一発では出来ないんでしょう。
New Feature -> LSL -> Agent -> Rotation Control
なので、どうしても回転させたい場合は、オブジェクトに座らせて、そのオブジェクトを回転させることになります。できればSpirittanでは、移動制御時にオブジェクトに座らせたくないのですが、ダッシュ中の方向転換とかを、きれいに出来るようにするには、座り方式でいくしかないみたいです。う~ん困った。
2007年12月28日
LSL-Editorでユーザ定義変数も補完する
LSLの開発には、LSL-Editorを使用しています。このエディターはコード自動補完が優秀なのですが、デフォルトの設定ではユーザ定義変数の補完機能がOFFになっています(もしかしたら、最近のバージョンをインストールするとONになってたりするかもしれません)

メニューから、「Tools → options → Text Editor → Code Completion」と進んで、真ん中のチェックをONにするだけで補完が効くようになります。お手軽なので、使ってる人はぜひONにしておきましょう。
ちなみに、なぜ今頃こんなエントリを書いたかというと、自分は今日気づいたからです。お、遅い・・・orz

メニューから、「Tools → options → Text Editor → Code Completion」と進んで、真ん中のチェックをONにするだけで補完が効くようになります。お手軽なので、使ってる人はぜひONにしておきましょう。
ちなみに、なぜ今頃こんなエントリを書いたかというと、自分は今日気づいたからです。お、遅い・・・orz
タグ :LSLLSL-Editor
2007年12月01日
ホーミング弾の作り方
以前ちらっと書いたとおり、Spirittanにはホーミング弾が存在します。その作り方がなかなか難しかったので、ちょっと公開してみます。
ホーミングというとまず思い浮かぶのは、llSensorとllMoveToTargetの組み合わせによる追尾型のアタッチメントですが、llMoveToTargetをホーミング弾に使用すると、どうしてもカクカクした動きになり見栄えがよくありません。
そこで、SpirittanではllSetForce関数で弾に力を加えることで、徐々に曲がっていく感じの弾を作ってみました。
以下は、弾に埋め込んであるスクリプトのホーミング関連部分のみ抜き出したものです。
このスクリプトには、次の要素があります。
大まかに説明すると、on_rez時にllSensorRepeatで毎秒サーチを行うよう設定し、敵が見つかったらsensorイベントで弾に力を加えます。加える力の大きさは、getForce関数で計算しています。
このスクリプトの肝は、弾に加える力を計算するgetForce関数です。他の部分は特に難しいことはしていないので、この関数に絞って解説します。
最初に弾の位置、速度、加速度を取得した後、敵⇔弾間の距離を取得します。

次に、発射地点から敵までの距離と、弾までの距離をチェックして、弾の方が遠い場合は加える力をZERO_VECTORにしています。こうしておかないと、弾をよけても後ろからホーミングしてしまいます。llVecMag関数でベクトルの大きさを取得して距離を比較しています。

後ろからホーミング
チェックが終わった後は、弾に加えるべき加速度を計算しています。これは、以下のような手順で行います。

4で使う公式は、等加速度直線運動の公式です。あったねこんなの。

距離 = 初速 × 時間 + 1/2 × 加速度 × 時間^2
図の赤い矢印の方向への初速は0なので右辺の第1項は0になります。時間tは手順3で取得済みです。距離Sは「enemyPos - (myPos + time * vel)」で求めています。あとは、加速度が左辺にくるように整理してから、現在の加速度accを引くことで加えるべき加速度newaccが求められます。
求まった加速度が大きすぎる場合は、補正をかけます。今回の例では加速度ベクトルの大きさが10より大きかった場合は、大きさが10になるように調整しています。これをしておかないと弾がものすごいスピードで曲がってきて不自然な動きになります。
最後に運動方程式(F=ma)を使って、加速度から力を求めて返します。
あとは、llSetForce関数の第2引数をFALSEにして、グローバル軸にたいして力をかけてあげれば完了です。
完成した弾
ホーミングというとまず思い浮かぶのは、llSensorとllMoveToTargetの組み合わせによる追尾型のアタッチメントですが、llMoveToTargetをホーミング弾に使用すると、どうしてもカクカクした動きになり見栄えがよくありません。
そこで、SpirittanではllSetForce関数で弾に力を加えることで、徐々に曲がっていく感じの弾を作ってみました。
以下は、弾に埋め込んであるスクリプトのホーミング関連部分のみ抜き出したものです。
// ホーミング対象(敵)のKey key enemyKey = "xxxx-xxxx-xxxx-xxxx"; // 弾が発射された地点 vector firstPos; // 敵の現在地へ弾を移動させるために必要な力を計算する vector getForce(vector enemyPos) { vector myPos = llGetPos(); vector vel = llGetVel(); vector acc = llGetAccel(); // 弾と敵の距離 vector distance = enemyPos - myPos; // 弾発射地点から見て、敵より弾の方が遠い位置にあれば力は加えない vector firstPosDistance1 = enemyPos - firstPos; vector firstPosDistance2 = llGetPos() - firstPos; if(llVecMag(firstPosDistance2) > llVecMag(firstPosDistance1)) { return ZERO_VECTOR; } // 弾を敵の現在地へホーミングさせるために必要な、現在の速度と直角方向の加速度 float theta = llRot2Angle(llRotBetween(distance, vel)); float velMag = llVecMag(distance) * llCos(theta); float time = velMag / llVecMag(vel); vector newacc = (2 * (enemyPos - (myPos + time * vel)) / llPow(time, 2)) - acc; // 加速度が大きすぎる場合は補正 if(llVecMag(newacc) > 10) { newacc = newacc * 10 / llVecMag(newacc); } // 力 = 質量 × 加速度 return llGetMass() * newacc; } default { on_rez(integer start_param) { // 弾を浮かせる設定 vector v = llGetPos(); llSetHoverHeight(v.z - llGround(<0.0,0.0,0.0>), FALSE, 1.0); // 1秒ごとに敵をサーチ llSensorRepeat("", enemyKey, AGENT, 100, PI, 1.0); // Rezされた位置を記録しておく firstPos = llGetPos(); } sensor(integer total_number) { vector pos = llDetectedPos(0); // 敵が見つかった場合、弾をホーミングさせるために力を加える llSetForce(getForce(pos), FALSE); } }
このスクリプトには、次の要素があります。
- グローバル変数
- enemyKey (ホーミング対象(敵)のKey、実際は発射直後に取得)
- firstPos (弾が発射された地点)
- グローバル関数
- getForce (ホーミングさせるために弾に加える力を計算)
- defaultステートのイベント
- on_rez (弾が生成された直後の処理)
- sensor (敵を発見した際の処理)
大まかに説明すると、on_rez時にllSensorRepeatで毎秒サーチを行うよう設定し、敵が見つかったらsensorイベントで弾に力を加えます。加える力の大きさは、getForce関数で計算しています。
このスクリプトの肝は、弾に加える力を計算するgetForce関数です。他の部分は特に難しいことはしていないので、この関数に絞って解説します。
最初に弾の位置、速度、加速度を取得した後、敵⇔弾間の距離を取得します。
// 弾と敵の距離 vector distance = enemyPos - myPos;

次に、発射地点から敵までの距離と、弾までの距離をチェックして、弾の方が遠い場合は加える力をZERO_VECTORにしています。こうしておかないと、弾をよけても後ろからホーミングしてしまいます。llVecMag関数でベクトルの大きさを取得して距離を比較しています。
// 弾発射地点から見て、敵より弾の方が遠い位置にあれば力は加えない vector firstPosDistance1 = enemyPos - firstPos; vector firstPosDistance2 = llGetPos() - firstPos; if(llVecMag(firstPosDistance2) > llVecMag(firstPosDistance1)) { return ZERO_VECTOR; }

後ろからホーミング
チェックが終わった後は、弾に加えるべき加速度を計算しています。これは、以下のような手順で行います。
- 弾⇔敵を結ぶ直線と、弾の進行方向の間の角θを求める
- llCos関数を使って、図のような弾進行方向の進むべき距離velMagを計算する
- 先ほど求めたvelMagを進むのに、今のスピードでどれだけの時間が掛かるか求める
- 物理の公式を使って、距離、時間から赤い矢印方向へ必要な加速度を求める
// 弾を敵の現在地へホーミングさせるために必要な、現在の速度と直角方向の加速度 float theta = llRot2Angle(llRotBetween(distance, vel)); float velMag = llVecMag(distance) * llCos(theta); float time = velMag / llVecMag(vel); vector newacc = (2 * (enemyPos - (myPos + time * vel)) / llPow(time, 2)) - acc;

4で使う公式は、等加速度直線運動の公式です。あったねこんなの。

距離 = 初速 × 時間 + 1/2 × 加速度 × 時間^2
図の赤い矢印の方向への初速は0なので右辺の第1項は0になります。時間tは手順3で取得済みです。距離Sは「enemyPos - (myPos + time * vel)」で求めています。あとは、加速度が左辺にくるように整理してから、現在の加速度accを引くことで加えるべき加速度newaccが求められます。
求まった加速度が大きすぎる場合は、補正をかけます。今回の例では加速度ベクトルの大きさが10より大きかった場合は、大きさが10になるように調整しています。これをしておかないと弾がものすごいスピードで曲がってきて不自然な動きになります。
// 加速度が大きすぎる場合は補正 if(llVecMag(newacc) > 10) { newacc = newacc * 10 / llVecMag(newacc); }不自然な弾の動き
最後に運動方程式(F=ma)を使って、加速度から力を求めて返します。
// 力 = 質量 × 加速度 return llGetMass() * newacc;
あとは、llSetForce関数の第2引数をFALSEにして、グローバル軸にたいして力をかけてあげれば完了です。
完成した弾
2007年11月01日
同一プリム内のすべてのスクリプトをリセットする
小さな小さなネタ。
スクリプトがおかしな状態にならないよう、しっかり作りたかったのですが、外部アバターのSayや想定外なCollisionとか、はたまた、微妙なタイミングでのログオフやSLクライアントのハングアップなど、対応事項が多すぎるので、もうユーザの手動リセット作ってゴマカしとけという思いでリセット関数作ることにしました。
以下の関数を呼び出すと、同一プリム内のすべてのスクリプトをリセットします。
(同一オブジェクト内ではないので注意)
For文では、llGetInventoryNumberで、コンテンツ内のスクリプトの数を取得し、その数だけループをまわしています。そして、llGetInventoryNameで、スクリプト名を取得して、llResetOtherScriptに引き渡してリセットしてるわけです。
以外に小さい関数になってビックリ。エントリにするまでもなかったか・・・
追記:
先ほどのスクリプト、バグがありました。上記のままでは、リセットを実行しているスクリプト自身もリセットされてしまうので、まだリセットすべきスクリプトが残っているうちに自分がリセットされてしまうと、それ以後のリセットがされなくなります。
バグ修正版を載せておきます。こちらは、スクリプト名が自分自身の場合、リセットを飛ばすようになっています。
自分をリセットしないので、完璧なものではないです。メインのスクリプトと分けて、リセットをトリガするだけの小さなスクリプトとして使うのがいいと思います。
追記:
maayaさんにもらったコメントを反映して修正しました。
これで、自分自身もリセットされます^^
スクリプトがおかしな状態にならないよう、しっかり作りたかったのですが、外部アバターのSayや想定外なCollisionとか、はたまた、微妙なタイミングでのログオフやSLクライアントのハングアップなど、対応事項が多すぎるので、もうユーザの手動リセット作ってゴマカしとけという思いでリセット関数作ることにしました。
以下の関数を呼び出すと、同一プリム内のすべてのスクリプトをリセットします。
(同一オブジェクト内ではないので注意)
resetAllScript() { integer i; for(i=0; i<llGetInventoryNumber(INVENTORY_SCRIPT); i++) { llResetOtherScript(llGetInventoryName(INVENTORY_SCRIPT, i)); } }
For文では、llGetInventoryNumberで、コンテンツ内のスクリプトの数を取得し、その数だけループをまわしています。そして、llGetInventoryNameで、スクリプト名を取得して、llResetOtherScriptに引き渡してリセットしてるわけです。
以外に小さい関数になってビックリ。エントリにするまでもなかったか・・・
追記:
先ほどのスクリプト、バグがありました。上記のままでは、リセットを実行しているスクリプト自身もリセットされてしまうので、まだリセットすべきスクリプトが残っているうちに自分がリセットされてしまうと、それ以後のリセットがされなくなります。
バグ修正版を載せておきます。こちらは、スクリプト名が自分自身の場合、リセットを飛ばすようになっています。
resetAllScript() { integer i; for(i=0; i<llGetInventoryNumber(INVENTORY_SCRIPT); i++) { string scriptName = llGetInventoryName(INVENTORY_SCRIPT, i); if(scriptName != llGetScriptName()) { llResetOtherScript(scriptName); } } }
自分をリセットしないので、完璧なものではないです。メインのスクリプトと分けて、リセットをトリガするだけの小さなスクリプトとして使うのがいいと思います。
追記:
maayaさんにもらったコメントを反映して修正しました。
これで、自分自身もリセットされます^^
resetAllScript() { integer i; for(i=0; i<llGetInventoryNumber(INVENTORY_SCRIPT); i++) { string scriptName = llGetInventoryName(INVENTORY_SCRIPT, i); if(scriptName != llGetScriptName()) { llResetOtherScript(scriptName); } } llResetScript(); }
2007年09月14日
パーティクル小ネタ
プリム本体に向かって飛んでくるパーティクルです。
溜め撃ちみたいな、武器をつくりたかったので。

パーティクル関数のソース。使ってたのをそのまま持ってきたから、いらないパラメータも、いっぱいあるかも。
PSYS_PART_FLAGS に PSYS_PART_TARGET_POS_MASKを指定すると、PSYS_SRC_TARGET_KEYで指定したターゲットに、パーティクルが飛ぶようになります。
アタッチしてみた
余談ですが、このブログに書かれたスクリプトは、自由に自分の作品に組み込んでもらったりしてかまいません。ただ、検証不十分なことも多いので、十分にテストして自己責任でお使いください。
溜め撃ちみたいな、武器をつくりたかったので。

パーティクル関数のソース。使ってたのをそのまま持ってきたから、いらないパラメータも、いっぱいあるかも。
getEnergy1(vector color){ // 色は変更可にしてあります llParticleSystem([ PSYS_PART_FLAGS, PSYS_PART_EMISSIVE_MASK | PSYS_PART_TARGET_POS_MASK, // これがポイント PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_EXPLODE, PSYS_PART_START_ALPHA, 1.0, PSYS_PART_END_ALPHA, 0.0, PSYS_PART_START_COLOR, color, PSYS_PART_END_COLOR, color, PSYS_PART_START_SCALE, <1.2, 1.2, 1.2>, PSYS_PART_END_SCALE, <1.2, 1.2, 1.2>, PSYS_SRC_MAX_AGE, 0.0, PSYS_PART_MAX_AGE, 1.0, PSYS_SRC_ACCEL, <0.0, 0.0, 0.0>, PSYS_SRC_ANGLE_BEGIN, PI, PSYS_SRC_ANGLE_END, PI, PSYS_SRC_BURST_PART_COUNT, 1, PSYS_SRC_BURST_RADIUS, 3.0, // ここでパーティクル半径を指定 PSYS_SRC_BURST_RATE, 0.0, PSYS_SRC_BURST_SPEED_MIN, 0.0, PSYS_SRC_BURST_SPEED_MAX, 0.0, PSYS_SRC_OMEGA, <0.0, 0.0, 0.0>, PSYS_SRC_TEXTURE, "" PSYS_SRC_TARGET_KEY, llGetKey() // ターゲットを自分に指定 ]); }
PSYS_PART_FLAGS に PSYS_PART_TARGET_POS_MASKを指定すると、PSYS_SRC_TARGET_KEYで指定したターゲットに、パーティクルが飛ぶようになります。
アタッチしてみた
余談ですが、このブログに書かれたスクリプトは、自由に自分の作品に組み込んでもらったりしてかまいません。ただ、検証不十分なことも多いので、十分にテストして自己責任でお使いください。
2007年08月26日
物理オブジェクトの回転を防ぐ
物理オブジェクトって体当たりすると、くるくる回転しちゃうよね。
昔、移動制御用の物理オブジェクトが回転して、制御されているアバターも回りだしちゃったことがあって、対策したから書いとこーかな。
実は、最初に対処法を探したときは、LSL関数の一覧をダーっと見たんだけど、回転を止めるみたいな関数はなかったんで、サラリとあきらめた orz
で、2週間くらい前にリファレンスをタラタラ見てたら、llSetStatus関数に、STATUS_ROTATE_Xみたいなパラメータを発見。X軸の周りを回転させる、させないを制御できるらしい。YとZもあった。
これらのパラメータをFALSEにセットしてやれば、
回転しない物理オブジェクトの出来上がり\(^o^)/
こうすれば、Z軸の周りのみ回転させることもできる。
で、ちょっとした実験。微妙に浮かした物理オブジェクトに体当たりしてみる。左は全方位回転、真ん中はZ軸のみ回転、右は回転しない。

以下が実験のムービー
ちゃんとできてる(^_^)
昔、移動制御用の物理オブジェクトが回転して、制御されているアバターも回りだしちゃったことがあって、対策したから書いとこーかな。
実は、最初に対処法を探したときは、LSL関数の一覧をダーっと見たんだけど、回転を止めるみたいな関数はなかったんで、サラリとあきらめた orz
で、2週間くらい前にリファレンスをタラタラ見てたら、llSetStatus関数に、STATUS_ROTATE_Xみたいなパラメータを発見。X軸の周りを回転させる、させないを制御できるらしい。YとZもあった。
これらのパラメータをFALSEにセットしてやれば、
回転しない物理オブジェクトの出来上がり\(^o^)/
state_entry()
{
// stop rotation X Y Z
llSetStatus(STATUS_ROTATE_X |
STATUS_ROTATE_Y |
STATUS_ROTATE_Z,
FALSE);
}
こうすれば、Z軸の周りのみ回転させることもできる。
state_entry() { // stop rotation X Y llSetStatus(STATUS_ROTATE_X | STATUS_ROTATE_Y, FALSE); // not stop rotation Z llSetStatus(STATUS_ROTATE_Z, TRUE); }
で、ちょっとした実験。微妙に浮かした物理オブジェクトに体当たりしてみる。左は全方位回転、真ん中はZ軸のみ回転、右は回転しない。

以下が実験のムービー
ちゃんとできてる(^_^)