|
|
Google SiteSearch Box - KazMuzik Blog
2009-05-28 07:07
2009-05-28
長い間、このエントリをアップデートしてきましたが、ブログの移行に際して、今後はアップデートはアップデートしないことにしました。Tags: american_life, caltrain, computer_technology, español, français, game, health, immigration, internet, japanese, jobs_in_america, music, music_and_computer, music_gear, music_technology, programming, rebate, recycle_and_donation, second_life, tax, test, useful_link, walking
|
|
|
|
|
|
|
|
|
|
|
|
Large Pyramid - LSL #18 - SL #29 - KazMuzik Blog
2008-12-07 18:01
正八面体は、思った以上に簡単だったので、一辺が 2倍の 17.32m のピラミッドを作ってみます。まず、ひとつの prim で作成できる最大の一辺 8.66m の正三角形を、4つ組み合わせて、一辺 17.32m の正三角形を作成します。
setRotation() {
llSetRot(llEuler2Rot(<0.0, 0.0, PI>));
}
rezTriangles() {
vector p = llGetPos();
llRezAtRoot("triangle", p + < 5.0, 0.0, 0.0>, <0.0, 0.0, 0.0>, ZERO_ROTATION, 0);
llRezAtRoot("triangle", p + <-2.5, 4.330127, 0.0>, <0.0, 0.0, 0.0>, ZERO_ROTATION, 0);
llRezAtRoot("triangle", p + <-2.5,-4.330127, 0.0>, <0.0, 0.0, 0.0>, ZERO_ROTATION, 0);
}
default {
state_entry() {
}
touch_start(integer total_number) {
llRequestPermissions(llGetOwner(), PERMISSION_CHANGE_LINKS);
}
run_time_permissions(integer perm) {
if (perm & PERMISSION_CHANGE_LINKS) {
setRotation();
rezTriangles();
}
}
object_rez(key id) {
llCreateLink(id, TRUE);
}
} |
一辺 8.66m の正三角形の prim に、このスクリプトと、同じ prim を "triangle" という名前で、contents に入れます。これを touch すると、大きな正三角形が出来ます。まずは、自分自身を 180°回転してから、それぞれの辺の方向 5m のところに、つまり、辺に対して線対称となるように、同じ正三角形を 3個、配置します。この大きな三角形を "large triangle" としておきます。
この "large triangle" と、8.66m の正方形の prim "square 8.66m" を、底辺の中心となる prim に入れて、次のスクリプトを実行して、touch すると、大きなピラミッドができます。
rezTriangle(vector pos, vector rot) {
llRezAtRoot("large triangle", pos, <0.0, 0.0, 0.0>, llEuler2Rot(rot), 0);
}
rezTriangles() {
vector p = llGetPos();
rezTriangle(p + <-5.773502, 0.0, 4.08248>, <0.0, PI-0.95532, 0.0>);
rezTriangle(p + < 5.773502, 0.0, 4.08248>, <0.0, 0.95532, 0.0>);
rezTriangle(p + <0.0,-5.773502, 4.08248>, <PI+0.95532, 0.0, PI_BY_TWO>);
rezTriangle(p + <0.0, 5.773502, 4.08248>, <PI-0.95532, 0.0,-PI_BY_TWO>);
}
rezSquares() {
rezSquaresX(llGetPos());
}
rezSquaresX(vector pos) {
rezSquaresY(pos + <-4.330127, 0.0, 0.0>);
rezSquaresY(pos + < 4.330127, 0.0, 0.0>);
}
rezSquaresY(vector pos) {
rezSquare(pos + <0.0, -4.330127, 0.0>);
rezSquare(pos + <0.0, 4.330127, 0.0>);
}
rezSquare(vector pos) {
llRezObject("square 8.66m", pos, <0.0, 0.0, 0.0>, ZERO_ROTATION, 0);
}
setInvisible() {
llSetScale(<0.01, 0.01, 0.01>);
llSetAlpha(0.0, ALL_SIDES);
}
default {
state_entry() {
}
touch_start(integer total_number) {
llRequestPermissions(llGetOwner(), PERMISSION_CHANGE_LINKS);
}
run_time_permissions(integer perm) {
if (perm & PERMISSION_CHANGE_LINKS) {
rezTriangles();
rezSquares();
setInvisible();
}
}
object_rez(key id) {
llCreateLink(id, TRUE);
}
} |
側面の大きな正三角形を作成したときに、root となる prim を 180°回転したせいか、rotation の vector は、調整する必要がありました。Tags: programming, second_life
|
|
Octahedron - LSL #17 - SL #28 - KazMuzik Blog
2008-12-07 08:59
12/5 には、pyramid を作成しましたが、そのスクリプトを応用して、正八面体(Regular Octahedron)を作ってみました。前回の pyramid は、すべての辺の長さが等しいので、四角錐の 4つの正三角形の側面を、下の方にも作り、底面の正方形を消すだけです。
rezTriangle(vector pos, vector rot) {
llRezAtRoot("triangle", pos, <0.0, 0.0, 0.0>, llEuler2Rot(rot), 0);
}
rezTriangles() {
vector p = llGetPos();
rezTriangle(p + <-2.88675, 0.0, 2.04124>, <0.0, -0.95532, 0.0>);
rezTriangle(p + < 2.88675, 0.0, 2.04124>, <0.0, PI+0.95532, 0.0>);
rezTriangle(p + <0.0,-2.88675, 2.04124>, < 0.95532, 0.0, PI_BY_TWO>);
rezTriangle(p + <0.0, 2.88675, 2.04124>, <-0.95532, 0.0, -PI_BY_TWO>);
rezTriangle(p + <-2.88675, 0.0, -2.04124>, <0.0, 0.95532, 0.0>);
rezTriangle(p + < 2.88675, 0.0, -2.04124>, <0.0, PI-0.95532, 0.0>);
rezTriangle(p + <0.0,-2.88675, -2.04124>, <-0.95532, 0.0, PI_BY_TWO>);
rezTriangle(p + <0.0, 2.88675, -2.04124>, < 0.95532, 0.0, -PI_BY_TWO>);
}
setInvisible() {
llSetScale(<0.01, 0.01, 0.01>);
llSetAlpha(0.0, ALL_SIDES);
}
default {
state_entry() {
}
touch_start(integer total_number) {
llRequestPermissions(llGetOwner(), PERMISSION_CHANGE_LINKS);
}
run_time_permissions(integer perm) {
if (perm & PERMISSION_CHANGE_LINKS) {
rezTriangles();
setInvisible();
}
}
object_rez(key id) {
llCreateLink(id, TRUE);
}
} |
Tags: programming, second_life
|
|
SL #27 - Angel Ring (home-made) - KazMuzik Blog
2008-12-06 13:59
Angel's Kiss で、free の Angel wings を get したので、自分で、Angel Ring を作ってみました。といっても、そんなにたいそうなものではなく、prim の building block type に Cylinder を選び、Hollow を 60% に設定して、まん中に穴をあけました。直径は、25cm にしました。また、texture は、白にして、Glow を 0.10 に設定しました。
これを頭の上になるように、attach するわけですが、Skull (頭蓋骨)には、すでに、prim の hair が付いていることが多いので、Left Ear (左耳)に attach して、position を調整して、頭の真上にくるようにしました。
ついでに、体を取り囲むような、光の球を作ってみました。これもひとつの prim で、直径 3m の Sphere に、Hollow を 95% にしました。また、texture は、Default Media Texture で、色は適当に選び、Transparency を 90%, Glow を 0.11 にしてみました。これは、体の中心に attach されるように、Stomach に attach しました。
Tags: second_life
|
|
Pyramid - LSL #16 - SL #26 - KazMuzik Blog
2008-12-05 19:12
20m の大きな Cube (立方体)の作成に成功したので、今回は Pyramid にチャレンジします。まずは、各面がひとつの prim のものから始めます。
Prim の形状(building block type)には、今まで使用してきた Box の他にも、Cylinder (円柱), Sphere (球), Torus (ドーナツ形)などがあります。今回は、正三角形の面があるので、Prism (三角錐)を使ってみます。これで、X, Y軸方向の長さを 10m にセットしたところ、大きな正三角形がでてきました。しかし、一辺 10m の正方形よりは、ひとまわり小さく、一辺が 10m はないようです。そこで、タイルの texture を貼付けてみたところ、高さが 7.5m で、中心は、1/3 の、底辺から 2.5m のところにあることがわかりました。ちなみに、高さの方向は、X軸で、頂点が (5.0, 0), 底辺が x=-2.5 です。このため、底面の正方形の一辺の長さは、7.5 * 2 / sqrt(3) = 5 * sqrt(3) = 8.66025m となります。つまり、すべての辺が 8.66m の正四角錐を作ることになります。
まずは、一辺 8.66025m の正方形の prim を作成して、その中に、上記の正三角形の prim を入れます。あとは、この正三角形を 4回、適当に回転して、適切な位置に rez して link するスクリプトを入れて、実行するだけです。しかし、その position と rotation を求めるのが、簡単ではありません。まずは、スクリプトから、紹介します。
rezTriangles() {
vector p = llGetPos();
llRezAtRoot("triangle", p + <-2.88675, 0.0, 2.04124>, <0.0, 0.0, 0.0>, llEuler2Rot(<0.0, -0.95532, 0.0>), 0);
llRezAtRoot("triangle", p + < 2.88675, 0.0, 2.04124>, <0.0, 0.0, 0.0>, llEuler2Rot(<0.0, PI+0.95532, 0.0>), 0);
llRezAtRoot("triangle", p + <0.0,-2.88675, 2.04124>, <0.0, 0.0, 0.0>, llEuler2Rot(< 0.95532, 0.0, PI_BY_TWO>), 0);
llRezAtRoot("triangle", p + <0.0, 2.88675, 2.04124>, <0.0, 0.0, 0.0>, llEuler2Rot(<-0.95532, 0.0, -PI_BY_TWO>), 0);
}
default {
state_entry() {
}
touch_start(integer total_number) {
llRequestPermissions(llGetOwner(), PERMISSION_CHANGE_LINKS);
}
run_time_permissions(integer perm) {
if (perm & PERMISSION_CHANGE_LINKS) {
rezTriangles();
}
}
object_rez(key id) {
llCreateLink(id, TRUE);
}
} |
まず、最初の面ですが、X軸とZ軸を含む平面で考えると、正三角錐の高さの平方は、(7.5)^2 - (5 * sqrt(3) / 2)^2 = (5/2)^2 * 6 なので、高さは、(5/2)*sqrt(6) = 6.12372m となります。これと、中心が底辺から 1/3 のところにあることを利用して、上記スクリプトでの position が決まります。
また、rotation については、Y軸の上(北)から見て、負の方向に回転する必要があります。ここでは、python を利用してみました。
$ python
Python 2.5.1 (r251:54863, Apr 15 2008, 22:57:26)
>>> from math import sqrt
>>> from math import atan2
>>> 5*sqrt(3)
8.6602540378443855
>>> 5*sqrt(6)/2
6.1237243569579451
>>> atan2( sqrt(6), sqrt(3) )
0.9553166181245093
>>>
|
ひとつ決まれば、あとの 3つは、符号を変えたり、軸を変えたり、rotation に関しては、PI (180度)や PI_BY_TWO (90度) だけ調整することにより、試行錯誤でも得られます。

 Tags: programming, second_life
|
|
|
|
Fitting Room in the Sky - LSL #15 - SL #24 - KazMuzik Blog
2008-12-04 19:37
20m cube の応用として、上空に、個人専用の Fitting Room を作成してみます。
Prim を作成して、20m cube のオブジェクトと、次のスクリプトを入れます。
float height = 3776.0;
moveUp() {
vector p = llGetPos();
float h = p.z;
while (h + 10.0 < height) {
p += <0.0, 0.0, 10.0>;
h += 10.0;
llSetPos(p);
}
p.z = height;
llSetPos(p);
}
default {
state_entry() {
}
changed(integer param) {
if (param & CHANGED_LINK) {
moveUp();
string name = llGetInventoryName(INVENTORY_OBJECT, 0);
llRezObject(name, llGetPos(), <0.0, 0.0, 0.0>, ZERO_ROTATION, 0);
llSetTimerEvent(6.0);
}
}
timer() {
llDie();
}
} |
これにすわると、1行目に指定された高さまで移動して、そこで、20m cube を作成して、timer() イベントにより、自分自身を消します。これで、上空の 20m cube の中に、閉じ込められることになります。
Second Life 内に、自分の家がない場合、いろいろな服を買ったり、free で get してきても、自由に試すことが、なかなか出来ないかもしれません。しかし、このスクリプトが入れた prim があれば、sandbox へ行って、rez して、座るだけで、20m cube の自分専用の fitting room となります。
Sandbox では、使用後は、削除していくのがエチケットですが、この場合、上空から、自分自身(avator) が落下してしまうことになります。Tags: programming, second_life
|
|
20m Cube (#2 linked) - LSL #14 - SL #23 - KazMuzik Blog
2008-12-04 07:56
前回の続きで、20m cube を構成する 8つの parts を、link して、ひとつのオブジェクトとします。
まずは、8つのオブジェクトに、"cube 10m ---", "cube 10m ---+", ..., "cube 10m +++" と名付けて、take します。符号は、順に、X, Y, Z軸の、どちらの surface をもつかを示します。例えば、"---" は、西、南、下の 3つの面をもつことをあらわします。
次は、上記のオブジェクトを rez して link するスクリプトです。
rezCube(string object, vector pos) {
llRezObject(object, pos, <0.0, 0.0, 0.0>, ZERO_ROTATION, 0);
}
rezCubes() {
string name = "cube 10m ";
vector pos = llGetPos();
rezCubesX(name, pos);
}
rezCubesX(string name, vector pos) {
rezCubesY(name + "-", pos + <-5.0, 0.0, 0.0>);
rezCubesY(name + "+", pos + < 5.0, 0.0, 0.0>);
}
rezCubesY(string name, vector pos) {
rezCubesZ(name + "-", pos + <0.0, -5.0, 0.0>);
rezCubesZ(name + "+", pos + <0.0, 5.0, 0.0>);
}
rezCubesZ(string name, vector pos) {
rezCube(name + "-", pos + <0.0, 0.0, -5.0>);
rezCube(name + "+", pos + <0.0, 0.0, 5.0>);
}
setInvisible() {
llSetScale(<0.01, 0.01, 0.01>);
llSetAlpha(0.0, ALL_SIDES);
}
default {
state_entry() {
}
touch_start(integer n) {
key owner = llGetOwner();
integer i;
for (i = 0; i < n; i++) {
if (llDetectedKey(i) == owner) {
llRequestPermissions(owner, PERMISSION_CHANGE_LINKS);
}
}
}
run_time_permissions(integer perm) {
if (perm & PERMISSION_CHANGE_LINKS) {
rezCubes();
setInvisible();
}
}
object_rez(key id) {
llCreateLink(id, TRUE);
}
} |
このスクリプトと上記の 8つのオブジェクトを入れた prim を touch することにより、一辺 20m の cube が、ひとつのオブジェクトとして、作成できます。いったん、link されてしまえば、contents の内容は、すべて削除してもかまいません。これで、例えば、object の編集で、texture を一気に変更すること、などができるようになります。もちろん、rez や take も、1回の操作で、できます。
その後、30m cube の作成にもチャレンジしてみましたが、llRezObject() の制限にひっかかってきました。11/30 には、各軸で、20m が制限らしいと書きましたが、今日の結果では、root position の直線距離で、17m が限界のようです。例えば、一辺 10m の立方体を 2つ並べることを考えます。ひとつの面を共有するように並べた場合、中心間の距離は、10m で、これは大丈夫です。また、ひとつの辺を共有するように並べた場合は、10 x sqrt(2) = 14.14213m で、これも大丈夫です。ところが、ひとつの頂点だけを共有するように、X,Y,Z各軸に 10m ずつ平行移動するような場合は、10 x sqrt(3) = 17.32050m となり、17m を超えているせいか、rez に失敗してしまいます。Tags: programming, second_life
|
|
20m Cube - LSL #13 - SL #22 - KazMuzik Blog
2008-12-04 07:24
Flexible に cuboid の surface を作れる script を書いたので、これを利用して、一辺 20m の Cube (立方体)を作ってみます。
まず、surface 用のスクリプトでは、size = <10.0, 10.0, 10.0> としておきます。
次に、20m cube を、8つの 10m cube に分解して、それぞれの cube の 3つの surface を作るスクリプトです。
vector size = <10.0, 10.0, 10.0>;
integer rezParam;
rezRectangle(string surface, vector pos, integer param) {
llRezAtRoot(surface, pos, <0.0, 0.0, 0.0>, ZERO_ROTATION, param);
}
rezRectangles() {
string surface = llGetInventoryName(INVENTORY_OBJECT, 0);
vector pos = llGetPos();
if (rezParam & 1) {
rezRectangle(surface, pos + <-size.x/2, 0.0, 0.0>, 1);
}
if (rezParam & 2) {
rezRectangle(surface, pos + < size.x/2, 0.0, 0.0>, 1);
}
if (rezParam & 4) {
rezRectangle(surface, pos + < 0.0,-size.y/2, 0.0>, 2);
}
if (rezParam & 8) {
rezRectangle(surface, pos + < 0.0, size.y/2, 0.0>, 2);
}
if (rezParam & 16) {
rezRectangle(surface, pos + < 0.0, 0.0,-size.z/2>, 3);
}
if (rezParam & 32) {
rezRectangle(surface, pos + < 0.0, 0.0, size.z/2>, 3);
}
}
setInvisible() {
llSetScale(<0.01, 0.01, 0.01>);
llSetAlpha(0.0, ALL_SIDES);
}
default {
state_entry() {
}
touch_start(integer n) {
if (rezParam > 0) {
llRequestPermissions(llGetOwner(), PERMISSION_CHANGE_LINKS);
}
}
on_rez(integer param) {
rezParam = param;
}
run_time_permissions(integer perm) {
if (perm & PERMISSION_CHANGE_LINKS) {
rezRectangles();
setInvisible();
}
}
object_rez(key id) {
llCreateLink(id, TRUE);
}
} |
これは、次のスクリプトを含むオブジェクトから、rez されますが、そのときのパラメータから、どの surface をつくるか決定します。例えば、21 = 1 + 4 + 16 の場合、1 により、X軸(東西)の西側の面を作成します。同様に、4 は Y軸(南北)の南側の面、... となります。
Rez のときには、パラメータを保存するだけで、実際には touch されてから、surface(s) を rez して、link します。これは、全体では 24 = 3 x 8 = 4 x 6 個の 10m x 10m surface になり、llCreateLink() は 1秒間 sleep するため、一気にすべて rez してしまうと、event queue が overflow してしまうためです。これは、複数のオブジェクトを link するときの重要な注意点で、できるだけ、使用時ではなく、事前にlink しておくことが大切です。このため、スクリプトを作成時用と使用時用の 2つを準備することになり、少し煩雑になってしまいます。
次に、上記のスクリプトを含む 8つのオブジェクトを生成するスクリプトです。
rezCube(string object, vector pos, integer param) {
llRezObject(object, pos, <0.0, 0.0, 0.0>, ZERO_ROTATION, param);
}
rezCubes() {
string object = llGetInventoryName(INVENTORY_OBJECT, 0);
vector pos = llGetPos();
rezCubesX(object, pos, 0);
}
rezCubesX(string object, vector pos, integer param) {
rezCubesY(object, pos + <-5.0, 0.0, 0.0>, param+1);
rezCubesY(object, pos + < 5.0, 0.0, 0.0>, param+2);
}
rezCubesY(string object, vector pos, integer param) {
rezCubesZ(object, pos + <0.0, -5.0, 0.0>, param+4);
rezCubesZ(object, pos + <0.0, 5.0, 0.0>, param+8);
}
rezCubesZ(string object, vector pos, integer param) {
rezCube(object, pos + <0.0, 0.0, -5.0>, param+16);
rezCube(object, pos + <0.0, 0.0, 5.0>, param+32);
}
default {
state_entry() {
}
touch_start(integer n) {
key owner = llGetOwner();
integer i;
for (i = 0; i < n; i++) {
if (llDetectedKey(i) == owner) {
rezCubes();
}
}
}
} |
このスクリプトと、上記のスクリプトを含むオブジェクトをもつ prim を作成して、touch すると、それを中心に、立体的に 8つの prim が配置されるので、それぞれを順番に touch していくことにより、20m cube が作成されます。ただし、この時点では、8つの parts は link されていません。また、touch するときは、event queue が overflow しないように、あまり急ぎすぎては、いけません。
つづくTags: programming, second_life
|
|
Cuboid - LSL #12 - SL #21 - KazMuzik Blog
2008-12-03 22:19
先週は、大きな square (正方形)を作りましたが、今週は、平面から、立体に challenge を移しました。このために、Z軸も考慮する必要があるため、できるだけ、X, Y, Z 軸に対して、対称的に扱えるようにしました。
まずは、直方体 (cuboid) の面 (surface) を作るためのスクリプトです。簡単のため、各辺の長さは、1行目に vector として記述してあります。この prim を llRezObject() で rez すると、最後のパラメータが on_rez() イベントのパラメータとして渡ってくるので、これで、どの面を作成するのかを決定します。これが、1 の場合は、X軸に垂直な面を作るため、この例では、4m x 5m の長方形になります。2, 3 の場合は、同様に、Y軸、Z軸に垂直な長方形を作成します。また、普通に、inventory から、drag and drop すると、パラメータが 0 となるため、特に、大きさは変更されず、このため、オブジェクトの編集やデバッグも容易に行えます。
vector size = <3.0, 4.0, 5.0>;
setSize(integer param) {
vector v = size;
if (param == 1) {
v.x = 0.01;
}
else if (param == 2) {
v.y = 0.01;
}
else if (param == 3) {
v.z = 0.01;
}
else {
return;
}
llSetScale(v);
}
default {
state_entry() {
}
on_rez(integer param) {
if (param > 0) {
setSize(param);
}
}
} |
次に、これを利用して、cuboid の 6つの面を作成して、link するスクリプトです。中心になる prim にこのスクリプトと、上記のスクリプトを含む prim "rectangle surface" を入れて、touch すると、6つの面を作成して、link して、自分自身(中心の prim)は、可能な限り小さくなって、見えなくなります。
vector size = <3.0, 4.0, 5.0>;
key owner;
rezRectangle(vector pos, integer param) {
llRezAtRoot("rectangle surface", pos, <0.0, 0.0, 0.0>, ZERO_ROTATION, param);
}
rezRectangles() {
vector pos = llGetPos();
rezRectangle(pos + < size.x/2, 0.0, 0.0>, 1);
rezRectangle(pos + <-size.x/2, 0.0, 0.0>, 1);
rezRectangle(pos + < 0.0, size.y/2, 0.0>, 2);
rezRectangle(pos + < 0.0,-size.y/2, 0.0>, 2);
rezRectangle(pos + < 0.0, 0.0, size.z/2>, 3);
rezRectangle(pos + < 0.0, 0.0,-size.z/2>, 3);
}
setInvisible() {
llSetScale(<0.01, 0.01, 0.01>);
llSetAlpha(0.0, ALL_SIDES);
}
default {
state_entry() {
owner = llGetOwner();
}
touch_start(integer n) {
llRequestPermissions(owner, PERMISSION_CHANGE_LINKS);
}
run_time_permissions(integer perm) {
if (perm & PERMISSION_CHANGE_LINKS) {
rezRectangles();
setInvisible();
}
}
object_rez(key id) {
llCreateLink(id, TRUE);
}
} |
これで、X軸方向に 3m, Y軸方向に 4m, Z軸方向に 5m の直方体(の6面)が作成され、中心の小さな見えない prim に link されます。
1行目の vector の値を変更することにより、各辺を 10m までの範囲で、変更することができます。Tags: programming, second_life
|
|
home-made light(s) #2 - SL #20 - KazMuzik Blog
2008-12-03 05:42
サンタのドレスを get したので、試してみたところ、足が黒っぽくなってしまいました。(*) そこで、左右両方の upper leg に、昨日の face light を attach してみました。これで、足も明るくなりました。
2008-12-06 update (*) Display のガンマ補正を、きちんと行っていなかったのも原因だったようです。Mac OS X の System Preferences > Displays > Color > Calibrate... で、目が疲れないように、わざと画面が暗くなるような極端な設定をしていました。今までは、色の正確さが需要なアプリケーションは、ほとんど使っていなかったため、色が区別できればいい程度だったので、問題はありませんでしたが、Second Life の avatar の見栄えを考慮する場合は、正確に補正する必要があります。今日(12/6)、きちんと Calibrate したところ、色がだいぶん、自然になってきたようです。
ところが、upper leg からの下からの光で、逆に、顔が影になってしまったようです。12/2 に書いたように、やたらに light を追加するのは、控えた方が良さそうです。Tags: second_life
|
|
Face Light (home-made, 自作) - LSL #11 - SL #19 - KazMuzik Blog
2008-12-02 05:36
Face Light を自作してみました。昨日の snapshot(s) などを見ると、顔が暗くなっています。妻のアドバイスによると、face light というアイテムがあり、それを装備することにより、顔が明るくなって、美しく見えるとのことです。といっても、free の face light を探すのも面倒なので、自分でスクリプトを書いて、実装することにしました。
基本的なアイデアは、prim をひとつ(あるいは複数)、顔の正面に置いて、Lightの設定により、光源として使うことです。ただし、光源の prim が見えてしまっては邪魔なので、見えないようにする工夫が必要です。また、最初から、見えないような prim を作ってしまうと、編集できなくなってしまうので、スクリプト内の on_rez() イベントで、見えなくすることにします。
まずは、光源の prim を作成します。Features タブに、Light の項目があるので、check して、必要ならばパラメータを調整します。それから、Contents に、次の、見えなくするスクリプトを入れます。基本的には、llSetAlpha() で 0.0 を設定することにより、透明にしていますが、念のため、サイズをできるだけ小さくしています。
setInvisible() {
llSetScale(<0.01, 0.01, 0.01>);
llSetAlpha(0.0, ALL_SIDES);
}
default {
state_entry() {
}
on_rez(integer param) {
setInvisible();
}
} | |
これに、"light" と名付けて、take します。
次に、Nose (など)に attach (装着)する prim を作成し、上記の "light" object を入れます。さらに次のスクリプトにより、touch_start() イベントを受け取ったら、"light" object を rez して、link することにします。
string light = "light";
key owner;
setLightName() {
if (llGetInventoryNumber(INVENTORY_OBJECT) > 0) {
light = llGetInventoryName(INVENTORY_OBJECT, 0);
}
}
rezLight(vector pos) {
llRezObject(light, pos, <0.0, 0.0, 0.0>, ZERO_ROTATION, 0);
}
rezLights() {
setLightName();
vector pos = llGetPos();
vector v = <0.10, 0.0, 0.0>;
vector p = pos + v;
rezLight(p);
}
default {
state_entry() {
owner = llGetOwner();
}
touch_start(integer n) {
integer i;
for (i = 0; i < n; i++) {
if (llDetectedKey(i) == owner) {
llRequestPermissions(owner, PERMISSION_CHANGE_LINKS);
}
}
}
run_time_permissions(integer perm) {
if (perm & PERMISSION_CHANGE_LINKS) {
rezLights();
}
}
object_rez(key id) {
llCreateLink(id, TRUE);
}
} | |
これで、touch すると、このオブジェクトには、見えない光源となるオブジェクトが link されることになります。
なお、この例では、attach される部所の 10cm のところに、光源のオブジェクトを 1個だけ、配置しています。必要ならば、光源の個数や場所を調整しておきます。
この時点で、Contents の中身は不要になるので、いったん削除しておきます。
さらに、attach() イベントを受け取ったら、自分自身を同様に見えなくする最初のものと似たスクリプトを入れます。
setInvisible() {
llSetScale(<0.01, 0.01, 0.01>);
llSetAlpha(0.0, ALL_SIDES);
}
default {
state_entry() {
}
attach(key id) {
setInvisible();
}
} | |
これを take して、自分の inventory に入れておきます。このオブジェクトを、例えば nose に attach すると、鼻の 10cm 先に、光源ができて、顔が明るくなるわけです。
上記のスクリプトや、object のパラメータを変更することにより、光源の数、距離、色などを調整することが可能です。ただし、あまり多くの光源を使うと、逆に陰ができたりして、調整がむずかしくなることがあるので、注意が必要です。
下記は、snapshots ですが、以前の snapshot と比べて、顔の色が明るくなっているのが、わかります。特に、左の snapshot は、夜ですが、明るく照らされているのがわかります。
Tags: programming, second_life
|
|
|
|
50m square floor #2 - LSL #10 - SL #17 - KazMuzik Blog
2008-11-30 11:28
11/28 には、object の rez や link には、10m の壁があるようだと書きましたが、いろいろ試してみたところ、どうも、それぞれの軸に対して 20m が限界のようです。ただし、llRezAtRoot() では、object の原点で決まるのに対して、link では、object の中心で決まるようです。Spec を調べたわけではなく、自分でいくつかスクリプトを書いて確かめてみただけなので、ひょっとして間違いがあるかもしれませんが。(*)
このため、正方形の大きな object を作成しようとした時には、今まで紹介したように、ひとつの object としては、30m まで、また、ひとつの script から複数の object を rez する場合には 50m というのが、限界のようです。ただし、後者に対しては、複数の入れ子になった object を用意して、 on_rez() イベントで、連鎖的に行う場合には、もっと大きなものが作れると思います。
なお、11/29 のタイルの 50m square floor は、厚さを 1m で作成しましたが、smoke texture に変更して、厚さも 0.01m に変更しようとしたところ、texture はすべてに適用されましたが、size (scale) は、root object にだけ適用されました。このため、表面がでこぼこになってしまったため、作り直しました。

まずは、右上に配置する 30m(東西, X軸) x 20m(南北, Y軸)の長方形の object を作成します。このために、まず、prim をひとつ作成して、size を 10m x 10m x 0.01m にし、Smoke Texture を貼付けます。これを、"square 10m" と名付けて、いったん inventory に取り込みます。これを、再び rez して、この中に、同じ "square 10m" を入れ、次のようなスクリプトを新規作成します。
string square10 = "square 10m";
key owner;
rezObjects() {
vector pos = llGetPos();
integer i;
for (i = -1; i < 2; i++) {
integer j;
for (j = 0; j < 2; j++) {
if (i != 0 || j != 0) {
llRezObject(square10, pos + <10.0 * (float)i, 10.0 * (float)j, 0.0>,
<0.0, 0.0, 0.0>, <0.0, 0.0, 0,0>, 0);
}
}
}
}
default {
state_entry() {
owner = llGetOwner();
}
touch_start(integer total_number) {
integer i;
for (i = 0; i < total_number; i++) {
if (llDetectedKey(i) == owner) {
llRequestPermissions(owner, PERMISSION_CHANGE_LINKS);
}
}
}
run_time_permissions(integer perm) {
if (perm & PERMISSION_CHANGE_LINKS) {
rezObjects();
}
}
object_rez(key id) {
llCreateLink(id, TRUE);
}
} |
これを実行して、touch すると、目的の 30m x 20m の長方形の object ができます。Contents にある "square 10m" とスクリプトは、用済みなので削除します。これを、"rectangle 30x20-1" と名付けて、inventory に取り込んでおきます。
同様にして、左上に配置される 20m x 30m の "rectangle 20x30-2" を、さらに、左下の "rectangle 30x20-3", 右下の "rectangle 20x30-4" を作成します。
次に、中心となる "square 10m" を rez して、この中に、上記の 4つの大きな長方形の objects を入れます。これに、次のようなスクリプトを新規作成します。
makeLargeSquare() {
vector pos = llGetPos();
llRezAtRoot("rectangle 30x20-1", pos + < 10.0, 10.0, 0.0>, <0.0, 0.0, 0.0>, <0.0, 0.0, 0,0>, 0);
llRezAtRoot("rectangle 20x30-2", pos + <-10.0, 10.0, 0.0>, <0.0, 0.0, 0.0>, <0.0, 0.0, 0,0>, 0);
llRezAtRoot("rectangle 30x20-3", pos + <-10.0, -10.0, 0.0>, <0.0, 0.0, 0.0>, <0.0, 0.0, 0,0>, 0);
llRezAtRoot("rectangle 20x30-4", pos + < 10.0, -10.0, 0.0>, <0.0, 0.0, 0.0>, <0.0, 0.0, 0,0>, 0);
}
default {
state_entry() {
}
on_rez(integer param) {
makeLargeSquare();
}
} |
これを、"square 10m to 50m on rez" と名付けて、inventory に取り込みます。後は、この 10m square の prim を rez すると、まわりに 4つの大きな長方形が rez されて、50m の正方形ができます。ただし、link は、されていないので、独立した 5つの objects として、扱われます。
また、普通に rez してしまうと、地上に 50m squre ができてしまいます。そこで、もう一工夫します。別の適当な大きさの prim を作成して、上記の "square 10m to 50m on rez" と次のスクリプトをいれます。
default {
state_entry() {
}
touch_start(integer n) {
if (llDetectedKey(0) == llGetOwner()) {
vector p = llGetPos();
llRezObject("square 10m to 50m on rez", p + <0.0, 0.0, -1.0>,
<0.0, 0.0, 0.0>, <0.0, 0.0, 0,0>, 0);
}
}
} |
これに、座ってから、object の edit window から、position の Z に 4096.0 以下の大きな値を入力します。これで、座ったまま、一気に上空に移動するので、そこで touch すると、上記の snapshot のように、足下を中心に、50m 四方の正方形の床ができます。
2008-12-04 update (*) -> 20m Cube (#2 linked) - LSL #14 - SL #23 .. 17m !?Tags: programming, second_life
|
|
30m square floor with fence (single large object) - LSL #9 - SL #16 - KazMuzik Blog
2008-11-30 01:08
土地を持っていないので、スクリプトを実行してみるには、sandbox で行う必要があります。ただし、地上は、ごちゃごちゃしていることが多いので、最近では、数千メートルの上空で作業しています。ただし、50m 四方の広い床があったとしても、間違って、落ちてしまう可能性もあり、Second Life 内のことなので、大事に至ることはありませんが、多少、面倒なことになります。このため、10m の立方体を作成したテクニックを応用して、30m 四方の床の回りに、高さ 10m のフェンスを作ってみました。今回も、smoke の(半)透明な texture を使ってみました。


これで落ちる心配をせずに、安心して作業することができるようになりました。
なお、Library にある Default Transparent Texture をセットしたところ、完全に透明になりました。どこまで床があるか全くわかりませんが、フェンスがあるので大丈夫です。ところが、この完全に透明なオブジェクトがマウスで選択できなくなりました。このため、削除もできず、自動的に消滅するまで、放置しておくことになってしまいました。Tags: programming, second_life
|
|
SL #15 - some snapshots and free items - KazMuzik Blog
2008-11-29 23:27
スクリプト作成の作業が一息ついたので、少しの時間、Second Life 内を歩いて、記念撮影をしてきました。Thanksgiving も終わり、Second Life 内も、クリスマスの雰囲気が漂っています。

また、この回転木馬の近くに、レートのいい dance pad の camp があったので、ちょっとやってみました。奥の方の dance pad で、サンタ帽をかぶって、ギターを持っているのが、私の avatar です。

レートは L$2 / 15分 で、1回の限度が $24 までとなっているので、$24 get するには、3時間そこに立って(踊って)いる必要があります。
このブログも、Second Life のおかげで、snapshot が載るようになり、視覚的にも、楽しんでもらえるようになってきました。ところが、avatar がダサ過ぎる、とか、センスがない、という意見が出てきました。たしかに、11/28 の宮崎での snapshot のように、男ふたりでギターを持ってビーチに寝転んでいる写真を見ても、楽しくもなんともありません。
しかし、時間もお金もかけている余裕はないので、手っ取り早く、ソラマメ(SLMame) という Second Life の日本語のブログサイトの、フリーという tag で、複数の人が紹介している人気のありそうな shop で、free item または L$1 の item を get してくることにしました。
ところが、ほとんど女性用の服ばかりで、男性用の物は、ほとんどありません。とりあえず、評判の良さそうな数カ所行ってみました。
まずは、月島にある J's で、free gift である UNISEX Engineer Long Boots を get しました。なんとなく Unix Engineer に見間違えたのですが、そんなはずはありません。これは、group gift となっていて、J's Update Group に参加しないともらえないようです。
次に、Bijou の Xmas Gift を get しに行きました。こちらは、特に、日本人を対象にした shop ではないようです。
また、Cafe yui という shop では、L$1 の Black jacket を購入してきました。
という感じで、だいたい上から下まで揃ったと思ったのですが、ズボンがなかったので、これは、Library にある Musician Female Jeans ですませました。ついでに、Eys, Hair, Medium Skin なども、Musician Female (by Renegade Clothing) からもってきました。Tags: second_life
|
|
|
|
50m square floor - LSL #7 - SL #13 - KazMuzik Blog
2008-11-29 09:54
昨日は、30m 四方の square object を作成しましたが、さらに大きな床を作ろうとすると、けっこう大変です。まずは、snapshot からです。

50m 四方の square の床の上に立っています。小さい青いひとつのタイルの大きさは、2.5m 四方なので、一辺 20個で、50m というのがわかります。ほぼ中央に、赤い帽子をかぶった avator が立っていますが、これからも、かなり大きいことがわかります。
いろいろ試行錯誤してみましたが、link における 10m の壁を乗り越えることはできず、ひとつの object として作成することはできませんでした。まん中の 10m 四方の prim のまわりに、20m x 30m の object を 4つ、取り囲むように配置しています。( 1 + (2 * 3) * 4 = 25 = 5 * 5 )
このため、小さな prim に script を組み込み、touch することにより、上記の 5つの object により、50m 四方の床を自動的に作るようにしました。
2008-11-29 update 別のタイルの texture を使ってみました。斜め 45度から眺めていますが、中の正方形の対角線が 10m、つまり、一辺はピタゴラスの定理(三平方の定理)より、10/sqrt(2) = 5 * sqrt(2) = 7.071m です。
 Tags: programming, second_life
|
|
30m Square (large object) - LSL #6 - SL #11 - KazMuzik Blog
2008-11-28 22:08
昨日の写真では、10m x 10m x 1m の prim を使用しましたが、Second Life の制限により、10m 以上の prim を作成することはできません。しかし、複数の prim を link して、ひとつの object として扱うことができます。これを Linden Script で記述して、一辺が 30m の square を作成してみます。
まずは、prim をひとつ作成して、size を 10m x 10m x 1m とします。名前は、"square 10m" として、take して、自分の inventory に取り込みます。これを、rez して、この中に、同じ "square 10m" を入れ、次の script を作成します。
string square10 = "square 10m";
key owner;
integer count = 8;
list squares = [];
rezSquares() {
vector pos = llGetPos();
integer i;
for (i = -1; i < 2; i++) {
integer j;
for (j = -1; j < 2; j++) {
if (i != 0 || j!= 0) {
llRezAtRoot(square10, pos + <10.0 * (float)i, 10.0 * (float)j, 0.0>,
<0.0, 0.0, 0.0>, <0.0, 0.0, 0,0>, 0);
}
}
}
}
default {
state_entry() {
owner = llGetOwner();
}
touch_start(integer total_number) {
integer i = 0;
for ( ; i < total_number; i++) {
if (llDetectedKey(i) == owner) {
llRequestPermissions(owner, PERMISSION_CHANGE_LINKS);
}
}
}
run_time_permissions(integer perm) {
if (perm & PERMISSION_CHANGE_LINKS) {
rezSquares();
}
}
object_rez(key id) {
squares += id;
count --;
if (count == 0) {
integer i;
for (i = 0; i < 8; i++) {
llCreateLink(llList2Key(squares, i), TRUE);
}
}
}
} |
Root になる、もともとの prim の inventory にある 10m 四方の prim を、8回 rez して、自分のまわりに並べます。これだけでも、一応 30m 四方の square ができますが、移動したり、take などの操作では、別々の 9個の object として扱わなければならないので、面倒です。そこで、llCreateLink() で、周辺の prim を、中央の root prim に、link しています。Link を変更するためには、runtime permission が必要になるため、最初に、llRequestPermissions() で、owner に permission を要求しています。
なお、一定の距離以上(多分、10m 以上)離れていると、llRezAtRoot() や llRezObject(), また、llCreateLink() が失敗します。また、このときに、object の root position で計算しているのか、center position なのかで、状況が変わってきます。このため、上記のスクリプトでは、llRezAtRoot() を用いて、root position で object を rez したあとに、まとめて、link しています。小さな object を扱っている場合は、特に問題がない場合もありますが、このように大きな object を扱う場合は、注意が必要です。
昨日と同様に、30m square の object を 3776m の上空に浮かべて、記念写真をとりました。

 Tags: programming, second_life
|
|
SL # 12 - shapshot in Miyazaki - KazMuzik Blog
2008-11-28 16:43
Second Life には、日本の有名な場所や、観光地などの SIM があるようです。これは、宮崎(県)のビーチでの snapshot です。

手前が、私の avator ですが、Library にある、"Musician Male by Renegade Clothing" を、ほとんどそのまま使っています。Second Life にいるほとんどの時間は、Linden Script を書いているので、アイテムなどを get している余裕は、あまりありません。
ちなみに、サングラスは、やはり Library にある "Musician Guy by Nylon Pinkney" から、"Musician Guy sunGlasses" だけをとってきて、ギターは、銀座の SIM にある楽器屋さんにあった、free のものを get しました。Free ですが、楽しい animation をサポートしていて、なかなか、すぐれものです。(*)
となりが、私の相棒で、彼も、Library の "Professional Male by Adam n Eve & Nylon Pink" で、すませているようです。一応、お揃いのサングラス、ギターを持っています。
2008-11-28 update (*) -> 30m Square (large object) - LSL #6 - SL #11 snapshotsTags: second_life
|
|
4,095m elevation - SL #10 - KazMuzik Blog
2008-11-27 19:01
標高 4,095m の上空に prim を置いて、その上で記念撮影しました。

Region 内の座標は、南西のコーナーを原点(0,0) として、X軸(東西), Y軸(南北)がそれぞれ 0.0 から 256.0 (float, 単位はメートル) ですが、高さの Z軸は、4096.0m まであります。簡単に高いところに行くには、sandbox で、prim を作成して、その上に座った後に、その prim を edit して、object タブの Position (meters) の Z を、大きな値に設定することです。上の写真では、もう一工夫して、大きな prim の上にたってみました。Tags: second_life
|
|
Camping - SL #11 - KazMuzik Blog
2008-11-25 21:42
Second Life で小銭を稼ぐ方法として、Camp があります。これは、特定の場所に sit しているだけで、10〜20分程度で、L$1 〜 2 程度がもらえます。しかし、1時間程度の時間制限があることが多く、1時間 camping しても、L$10 にはなりません。また、場所によっては、一定時間内に、何か話さないと、無効になったり、レートが下がったりするところもあります。それでも、Camp で有名な場所では、10ヶ所以上、用意してあっても、すべてうまっていて、なかなか利用できない場合があります。
US$10 で L$2,500 以上、手にした後では、1時間で L$10 程度のレートでは、割に合いませんが、長時間、PC から離れる場合などには、たまに利用したりしてます。
ただし、標準の設定では、30分何もしないと、自動的に Quit するので、Mac OS X の場合は、Control + Option + Command (Windows の場合は Control + Alt + Windows) + D の 4つのキーを同時に押して、Advanced メニューを有効にした後で、Character > Character Test > Go Away/AFK When Idle のチェックを外しておくと、便利です。Tags: second_life
|
|
|
|
item seller object - LSL #5 - SL #8 - KazMuzik Blog
2008-11-17 12:18
前回のスクリプトを改良して、inventroy にあるオブジェクトを売るスクリプトにしました。
integer defaultPrice = 100;
integer price = defaultPrice;
string item;
string notecardName;
integer notecardSize;
key owner;
key notecardRequest;
setInfo() {
item = llGetInventoryName(INVENTORY_OBJECT, 0);
if (item == "") {
llInstantMessage(owner, "Please put an item to my inventory.");
}
else {
notecardName = llGetInventoryName(INVENTORY_NOTECARD, 0);
if (notecardName == "") {
price = defaultPrice;
llInstantMessage(owner, "The price is set to L$" + (string)defaultPrice
+ " (default) because there is no notecard in my inventory.");
setItemPrice();
}
else {
integer value = (integer)notecardName;
if (value > 0) {
price = value;
llInstantMessage(owner, "The price is set to L$" + (string)price
+ " from the notecard name.");
setItemPrice();
}
else {
notecardSize = -1;
notecardRequest = llGetNumberOfNotecardLines(notecardName);
// -> dataserver()
}
}
}
}
setItemPrice() {
llSetObjectName(item + " - item seller");
llSetPayPrice(price, [price, PAY_HIDE, PAY_HIDE, PAY_HIDE]);
llSetText(replaceName(item) + "\nL$" + (string)price, <1.0, 1.0, 1.0>, 1.0);
string texture = llGetInventoryName(INVENTORY_TEXTURE, 0);
if (texture != "") {
llSetTexture(texture, ALL_SIDES);
}
}
string replaceName(string s) {
integer n = 0;
while (n >= 0) {
n = llSubStringIndex(s, " - ");
if (n >= 0) {
s = llDeleteSubString(s, n, n+2);
s = llInsertString(s, n, "\n");
}
}
return s;
}
default {
state_entry() {
owner = llGetOwner();
llSetClickAction(CLICK_ACTION_PAY);
setInfo();
}
changed(integer change) {
if (change & CHANGED_INVENTORY) {
setInfo();
}
}
dataserver(key query, string data) {
if (query == notecardRequest) {
if (notecardSize < 0) {
notecardSize = (integer)data;
if (notecardSize <= 0) {
price = defaultPrice;
llInstantMessage(owner, "The price is set to L$" + (string)price
+ " (default) because the notecard \"" + notecardName + "\" is empty.");
setItemPrice();
}
else {
notecardRequest = llGetNotecardLine(notecardName, 0);
// -> dataserver()
}
}
else {
price = (integer)data;
if (price < 0) {
price = 0;
llInstantMessage(owner, "The price for \"" + item + "\" is set to L$" + (string)price
+ " because the price of notecard \"" + notecardName + "\" is negative.");
}
else {
llInstantMessage(owner, "The price for \"" + item + "\" is set to L$" + (string)price
+ " from the notecard \"" + notecardName + "\".");
}
setItemPrice();
}
}
}
money(key buyer, integer amount) {
if (amount >= price) {
llInstantMessage(owner, llKey2Name(buyer) + " paid you L$" + (string)amount + " for \"" + item + "\".");
llInstantMessage(buyer, "Thank you for your purchase of \"" + item + "\" with L$" + (string)amount + ".");
llGiveInventory(buyer, item);
}
else {
llInstantMessage(buyer, "You paid L$" + (string)amount + ", but it is less than the price L$"
+ (string)price + ", so your payment is considered as a donation. Thank you for your donation.");
llInstantMessage(owner, llKey2Name(buyer) + " paid you L$" + (string)amount + " as donation.");
}
}
} |
価格は、前回と同様、inventory にある notecard から取得しますが、メンテナンスを容易にするため、もし notecard の名前が数字の場合は、それを価格にします。また、売り物であるオブジェクトの名前を取得して、それと価格をあわせて、llSetText() で、表示するようになっています。さらに、inventory に texture があれば、それも llSetTexture() でセットします。
Avator が Pay を実行すると、money() イベントが発生しますが、そこでは、支払われた金額と価格を比較して、価格に満たない場合は、donation とみなすことにしました。価格以上の支払いがあった場合には、llGiveInventory() で、売り物であるオブジェクトを、avator に与えます。
なお、llSetPayPrice() で、最初のパラメータも PAY_HIDE にして、llSetPayPrice(PAY_HIDE, [price, PAY_HIDE, PAY_HIDE, PAY_HIDE]); とすると、指定した金額のボタンだけが現れます。Tags: programming, second_life
|
|
Pay and price from notecard - LSL #4 - SL #7 - KazMuzik Blog
2008-11-16 13:23
前回の inventory にある notecard を用いて、payment の price を dynamic に設定する方法を紹介します。
Notecard の最初の行に、price を書いて、それを inventory に含めると、その金額が payment のときに参照されます。もし、notecard がひとつもない場合には、ハードコードされた defaultPrice が利用されます。
integer defaultPrice = 100;
integer price = defaultPrice;
string notecardName;
integer notecardSize;
key owner;
key notecardRequest;
setPrice() {
notecardName = llGetInventoryName(INVENTORY_NOTECARD, 0);
if (notecardName == "") {
price = defaultPrice;
llInstantMessage(owner, "The price is set to L$" + (string)defaultPrice
+ " because there is no notecard in my inventory.");
llSetPayPrice(price, [price, PAY_HIDE, PAY_HIDE, PAY_HIDE]);
}
else {
notecardSize = -1;
notecardRequest = llGetNumberOfNotecardLines(notecardName);
// -> dataserver()
}
}
default {
state_entry() {
owner = llGetOwner();
llSetClickAction(CLICK_ACTION_PAY);
setPrice();
}
changed(integer change) {
if (change & CHANGED_INVENTORY) {
setPrice();
}
}
dataserver(key query, string data) {
if (query == notecardRequest) {
if (notecardSize < 0) {
notecardSize = (integer)data;
if (notecardSize <= 0) {
price = defaultPrice;
llInstantMessage(owner, "The price is set to L$" + (string)defaultPrice
+ " because the notecard \"" + notecardName + "\" is empty.");
llSetPayPrice(price, [price, PAY_HIDE, PAY_HIDE, PAY_HIDE]);
}
else {
notecardRequest = llGetNotecardLine(notecardName, 0);
// -> dataserver()
}
}
else {
price = (integer)data;
if (price < 0) {
price = 0;
llInstantMessage(owner, "The price is set to L$" + (string)price
+ " because the price of notecard \"" + notecardName + "\" is negative.");
}
else {
llInstantMessage(owner, "The price is set to L$" + (string)price
+ " from the notecard \"" + notecardName + "\".");
}
llSetPayPrice(price, [price, PAY_HIDE, PAY_HIDE, PAY_HIDE]);
}
}
}
money(key payer, integer amount) {
llInstantMessage(owner, llKey2Name(payer) + " paid you L$" + (string)amount + ".");
}
} |
注意点としては、イベントが発生するような関数を呼び出した後は、そのイベントから、速やかに抜けるということです。これは、別のイベントが発生するため、そちらに制御が移ってしまうためです。この例では、私が定義した setPrice() という関数も、その中で、dataserver() というイベントが発生する llGetNumberOfNotecardLines() という関数を呼び出しているので、setPrice() はイベントの最後になるように書かなければいけません。最初は、その後に、llSetPayPrice() を書いていましたが、条件によって実行されないため、上記のことに気付き、setPrice() の中に入れました。
下記の実行例では、inventory に notecard がない状態でスタートしたので、最初は L$100 でしたが、1行目に 10 とだけ書かれた "sample price" という notecard を、inventory に含めたときに、L$10 になりました。さらに、notecard を edit して、1 としたところ、L$1 になりました。その後、Sample Payer という avator が、Pay に設定されている左クリックで、L$1 の支払いをしました。
[13:01] Payment Example: The price is set to L$100 because there is no notecard in my inventory.
[13:02] Payment Example: The price is set to L$10 from the notecard "sample price".
[13:03] Payment Example: The price is set to L$1 from the notecard "sample price".
[13:04] Payment Example: Sample Payer paid you L$1.
|
なお、Pay は、円形のメニューにありますが、money() イベントを実装しないと、選択できません。同様に、touch_start() イベントがないと、Touch ができません。今回の例では、簡単のため、削除しておきました。Tags: programming, second_life
|
|
Notecard and Inventory - LSL #3 - Second Life #6 - KazMuzik Blog
2008-11-15 11:19
オブジェクトで走るスクリプトを複数、書いていると、ほとんど同じですが、いくつかのパラメータだけが違うようなオブジェクトを作成したいことがあります。このような場合、スクリプトにハードコードしないで、それらのパラメータを Notecard に書いて、それを Inventory として持たせることで、同一のスクリプトを使うことができます。
まず、自分(オブジェクト)の inventory にアクセスする方法ですが、llGetInventoryNumber() と llGetInventoryName() という関数で、すべての、あるいは notecard など特定のタイプの inventory の数と、名前を得ることができます。
また、notecard に記述されている内容にアクセスするには、llGetNumberOfNotecardLines() で行数を得て、llGetNotecardLine() で、それぞれの行のテキストを得ることができます。ただし、これらは直接、結果を返すのではなく、dataserver() というイベントによって、受け取る必要があります。
これらの関数を応用したスクリプトで、Touch されたら、inventory にある最初の notecard の内容を、instant message で owner に送ります。
string notecardName;
integer notecardSize;
integer notecardRead;
list notecardLines;
key owner;
key notecardRequest;
setNotecard() {
notecardName = llGetInventoryName(INVENTORY_NOTECARD, 0);
}
printNotecardName() {
llInstantMessage(owner, "notecardName=\"" + notecardName + "\"");
}
printNotecardLines() {
string s = "";
s += "\n---BEGIN OF NOTECARD ---";
integer i = 0;
for ( ; i < notecardSize; i++) {
s += ("\n" + llList2String(notecardLines, i));
}
s += "\n--- END OF NOTECARD ---";
llInstantMessage(owner, s);
}
default {
state_entry() {
setNotecard();
owner = llGetOwner();
llSetTouchText("Read Notecard");
printNotecardName();
}
changed(integer change) {
if (change & CHANGED_INVENTORY) {
setNotecard();
printNotecardName();
}
}
touch_start(integer total_number) {
integer i = 0;
for ( ; i < total_number ; i++) {
key avator = llDetectedKey(i);
if (avator == owner) {
if (notecardName == "") {
llInstantMessage(owner, "no notecard in my inventory");
}
else {
notecardSize = -1;
notecardRequest = llGetNumberOfNotecardLines(notecardName);
}
}
}
}
dataserver(key query, string data) {
if (query == notecardRequest) {
if (notecardSize < 0) {
notecardSize = (integer)data;
if (notecardSize <= 0) {
llInstantMessage(owner, "The notecard \"" + notecardName + "\" is empty.");
}
else {
llInstantMessage(owner, (string)notecardSize + " lines"
+ " in the notecard \"" + notecardName + "\".");
notecardRead = 0;
notecardLines = [];
notecardRequest = llGetNotecardLine(notecardName, 0);
}
}
else {
notecardRead ++;
notecardLines += data;
if (notecardRead < notecardSize) {
notecardRequest = llGetNotecardLine(notecardName, notecardRead);
}
else {
printNotecardLines();
}
}
}
}
} |
Notecard の内容を保持するのに、list 型を使ってみました。また、途中で、inventory に変更があり、例えば、最初は notecard がなくても、途中で、追加された場合でも対応できるように、changed() イベントを実装してあります。また、status の外に、user defined の関数を定義しておきました。
[10:10] Notecard example: notecardName="sample text"
[10:11] Notecard example: 3 lines in the notecard "sample text".
[10:11] Notecard example:
---BEGIN OF NOTECARD ---
This is the first line.
This is the second line.
これは、3行目です。
--- END OF NOTECARD ---
|
この例のように、ASCII 以外の、例えば日本語の文字もきちんとサポートされています。Tags: programming, second_life
|
|
HTTP and JSP - Lenden Script Language (LSL) #2 - Second Life #5 - KazMuzik Blog
2008-11-13 01:16
11/11 には、簡単な Linden Script を紹介しましたが、iMac++ に Tomcat をセットアップしたので、HTTP で通信してみました。LSL Portal によると、llHTTPRequest() という関数に、URL を渡して呼び出すと、http_response() というイベントにより、HTTP Response を受け取れるようです。また、X-SecondLife-Object-Key などのヘッダーも送られるので、Servlet では、これを利用することができます。
それでは、Linden Script でのプログラムからです。
key httpRequestId;
key owner;
default {
state_entry() {
llSay(0, "Hello, Avatar!");
}
touch_start(integer total_number) {
key obj = llGetKey();
owner = llGetOwner();
key creator = llGetCreator();
string objName = llKey2Name(obj);
string ownerName = llKey2Name(owner);
string creatorName = llKey2Name(creator);
integer i = 0;
for ( ; i < total_number; i++) {
key avator = llDetectedKey(i);
if (avator != owner && avator != creator) {
llInstantMessage(avator, "Sorry, you cannot touch me.");
}
else {
llInstantMessage(avator,
"I am " + objName + " (" + (string)obj + ").");
llInstantMessage(avator,
"I am owned by " + ownerName + " (" + (string)owner + ").");
llInstantMessage(avator,
"I was created by " + creatorName + " (" + (string)creator + ").");
llInstantMessage(avator,
"You are " + llDetectedName(i) + " (" + (string)avator + ").");
httpRequestId = llHTTPRequest("http://kazmuzik.net:8080/sltest/test.jsp", [], "");
}
}
}
http_response(key requestId, integer status, list metadata, string body) {
if (requestId == httpRequestId) {
llInstantMessage(owner, llStringTrim(body, STRING_TRIM));
}
}
} |
ローカルで、前回と同様の情報を instant message で送った後、私の自宅にある iMac++ に HTTP Request を送っています。
この HTTP Request を受け付けて、処理する簡単な JSP です。
<%
String objectKey = request.getHeader("X-SecondLife-Object-Key");
String objectName = request.getHeader("X-SecondLife-Object-Name");
String ownerKey = request.getHeader("X-SecondLife-Owner-Key");
String ownerName = request.getHeader("X-SecondLife-Owner-Name");
java.util.Formatter f = new java.util.Formatter();
f.format("%s (%s) touched by %s (%s)%n", objectName, objectKey, ownerName, ownerKey);
%>
<%= f.toString() %>
|
これを Tomcat に deploy してやります。
$ cd /opt/tomcat/webapps
$ mkdir sltest
$ cd sltest
$ vi test.jsp (上記のファイル)
$
|
JSP なので、これだけです。
実際に、Second Life で、上記の Linden Script が走っているオブジェクト(HTTP sample object) をクリックするか、右クリックの円メニューから Touch を選択すると、次のようなメッセージを受け取ります。
[01:23] HTTP sample object: I am HTTP sample object (12345678-90ab-cdef-1234-567890abcdef).
[01:23] HTTP sample object: I am owned by Kaz Muzik (abcdef12-3456-7890-abcd-ef1234567890).
[01:23] HTTP sample object: I was created by Kaz Muzik (abcdef12-3456-7890-abcd-ef1234567890).
[01:23] HTTP sample object: You are Kaz Muzik (abcdef12-3456-7890-abcd-ef1234567890).
[01:23] HTTP sample object: HTTP sample object (12345678-90ab-cdef-1234-567890abcdef) touched \
by Kaz Muzik (abcdef12-3456-7890-abcd-ef1234567890)
|
最後のラインが、実際に、JSP が作成したものです。Tags: programming, second_life
|
|
Linden Script Language (LSL) - Second Life #4 - KazMuzik Blog
2008-11-11 20:11
Second Life の Linden Script Language (LSL) で簡単なスクリプトを書いてみました。Java や C言語で、event driven なコードを書いた方は、すぐにわかると思います。
まずは、オブジェクトを作りますが、これはどこでもできるわけではなく、sandbox と呼ばれる場所へ行く必要があります。そこで、mouse cursor を地面のところへ移動して、右クリックすると、丸いメニューが現れるので、Create を選択すると、立方体のオブジェクトができます。さらに、それを選択して、同様に、Edit... を選択すると、オブジェクト を edit する window がポップアッップしてきます。まん中あたりにタブがあるので、Content をクリックすると、その下の部分が変わるので、New Script... をクリックすると、新しいスクリプトが Contents に追加されます。それをダブルクリックするか、右ボタンで Open... を選択すると、スクリプト編集用の window がポップアップしてきます。
Second Life では、object はすべて UUID で表現される key を持っています。また、名前(name, type は string) もありますが、これは重複する可能性があるので、key で扱うのがいいようです。また、avator も object の一種なので、key (UUID) をもっています。まずは、touch されると、自分(object) と、その owner, それに、touch した avator の key と name をプリントする簡単なスクリプトを書いてみました。
default {
state_entry() {
llSay(0, "Hello, Avatar!");
}
touch_start(integer total_number) {
llSay(0, "Touched.");
key k = llGetKey();
llSay(0, "My key is " + (string)k + ".");
llSay(0, "My name is " + llKey2Name(k) + ".");
k = llGetOwner();
llSay(0, "My owner's key is " + (string)k + ".");
llSay(0, "My owner's name is " + llKey2Name(k) + ".");
integer i = 0;
for ( ; i < total_number; i++) {
k = llDetectedKey(i);
llInstantMessage(k, "Your key is " + (string)k + ".");
llInstantMessage(k, "Your name is " + llDetectedName(i) + ".");
}
}
} |
一番外側の括弧の default は state で、次のレベルの括弧の state_entry() や touch_start() が event です。この中に、実行するスクリプトを書いていきます。llSay() など、"ll" で始まるのが pre-defined された function で、llSay() は、特定の channel にメッセージを表示し、llInstantMessage() は、特定の user に、メッセージを送ります。
スクリプトを書いたら、右下の Save ボタンを押すと、コンパイルが実行され、エラーがなければ、save されて実行されます。オブジェクトをクリックして、メニューから Touch を選ぶと、
Object: Touched.
Object: My key is 12345678-90ab-cdef-1234-567890abcdef.
Object: My name is Object.
Object: My owner's key is abcdef12-3456-7890-abcd-ef1234567890.
Object: My owner's name is Kaz Muzik.
Object: Your key is abcdef12-3456-7890-abcd-ef1234567890.
Object: Your name is Kaz Muzik.
|
などと表示されます。
また、オブジェクトのメニューから、Take を選択すると、地面から、自分の inventory に移動されます。Tags: programming, second_life
|
|
|
|
Second Life #2 - KazMuzik Blog
2007-12-31 09:23
今朝 7am 過ぎに、時差の関係で、2008年になった日本から、Second Life を通して、メールで新年の挨拶が届きました。Second Life 内で IM メッセージを送ると、相手がログインしていない場合、現実の Eメールで届けられるようです。
この一週間、Linden Script Language (LSL) を少し勉強しましたが、もう少し、実際に Second Life でいろいろやってみないと、具体的に何を作ったらいいのかわからないような状態です。言語仕様自体は、Event driven で、object oriented (*) な script なので、Java や C++ が書ける人なら、すぐに理解できますが、pre-defined されている class(es) や function(s) がたくさんあるので、それらを使いこなすまでには、もう少し時間がかかりそうです。
2008-12-01 update (*) 約 1年経って、本格的に Linden Script を書き始めました。基本は、event driven ですが、言語仕様自体としては、object oriented というわけではありません(でした)。もちろん、プログラミングをする時には、オブジェクト指向の理解は役に立ちます。Tags: computer_technology, second_life
|
|
Second Life - KazMuzik Blog
2007-12-23 23:47
今週末は、クリスマスの休日(12/24, 25)とつながり、4連休となります。1ヶ月前の Thanksgiving も 4連休でしたが、PC の hardware upgrade で終わってしまった感じなので、今回は、今までできなかったことを、いくつかやってみようと考えています。
まずは、Linden Lab の Second Life です。最近は、日本でも話題になっているようです。専用のクライアントソフトをインストールする必要があります。Linux 版もありましたが、一応、Windows Vista でやってみました。最近は、ゲームをほとんどやっていませんが、ネットワーク対戦の Doom や Quake のような interface で、Diablo や Ultima Online のような communication というか、community のような雰囲気で、なつかしい感じがしました。
最初は、操作に慣れないため、かなりぎこちない動きでしたが、親切な方々にアドバイスをもらい、基本的な操作を覚えながら、だんだんと慣れていきました。
かなり奥が深いようですが、script を使って item も作れるようなので、そのうち、ここでも紹介できるかもしれません。Tags: computer_technology, second_life
|
|
|
|
|
|