japan.internet.comThe Internet & IT Network
RSS
  • ニュース
  • コラム
  • リサーチ
  • ヘッドライン
  • 特集
  • ブログ
  • プレスリリース
  • 専門チャンネル
  • イベント
  • ランキング
  • ニュースメール
2009年7月4日
文字サイズ文字サイズ小文字サイズ中文字サイズ大
デベロッパー2007年6月5日 13:10

リンデンスクリプトを使ったSecond Lifeのプログラミング

海外海外internet.com発の記事
  • このエントリーを含むはてなブックマーク
  • この記事をクリップ!
  • Buzzurlにブックマーク
  • Yahoo!ブックマークに登録
  • newsing it!
  • この記事をokyuuへインポート

はじめに

 Second Life(SL)は、Linden Labs社が制作した大人気の多人数同時参加型オンラインゲーム(MMOG)です。Second Lifeはゲームをはるかに超えるものだと考える人も大勢います。Second Lifeをよく知らない人は、映画『マトリックス』を思い出してください。『マトリックス』では、人々がシミュレーションの世界の中で生活し、仕事をしていました。Second Lifeもこれとそっくりで、アバターと呼ばれるコンピュータプレーヤーがSecond Lifeの3Dの世界の中で生活します。

 Second Lifeに明確な目的はありません。この世界で起きることはすべて、プレーヤーの自由な発想に任されています。しかも、Second Lifeは無料でプレーすることができます。登録する必要はありますが、土地を所有するのでなければ有料会員になる必要はありません。

 ただし『マトリックス』と違い、現在のコンピュータ技術は現実の世界を丸ごとシミュレートできるほど進歩していません。そのため、Second Lifeには2つの大きな短所があります。第一に、プレーヤーはコンピュータの画面を通してSecond Lifeの世界を目にします。最先端の3Dグラフィックスが使われているとはいえ、シミュレーションはどことなく漫画チックに見えます。それでも、Second Life内ではかなり水準の高いビジュアル表現が随所に見られます。

 第二に、Second Lifeのシミュレーションは物理的に完全なシミュレーションではありません。重要な要素、例えば重力などは存在しますが、車のような複雑なものは完全にシミュレートできないことがあります。車の内燃機関や電気部品のすべての側面をシミュレートするのはかなり難しいことだからです。最新のコンピュータであっても、このような処理にはパワーが足りません。とはいえ、これから見ていくように、Second Lifeの世界には車やその他多くの種類の乗り物が存在します。

 Second Lifeにアクセスするには、無料のクライアントを使用します。クライアントはWindows、Macintosh、UNIXなど、各種のOSに対応しています。さらに、Linden Labs社はつい先日、クライアントをオープンソースとして公開しました。これにより、サードパーティの開発者がSecond Lifeクライアントのさまざまな追加機能を作成し、機能を拡張できるようになっています。

Linden Scripting Languageについて

 Second Lifeには、単純な物理エンジンの機能の限界をフォローするために、Linden Scripting Language(以下LSL)というスクリプト型のプログラミング言語が用意されています。車のすべての側面をシミュレートする代わりに、車の動き方を指示するスクリプトをプログラマが作成します。このスクリプトでは、音を鳴らすことも、車の進路を変えることも、衝突を検出することもできます。例えば車のスクリプトを作成する場合、現実味を加えるために、運転中でなければ進路を変更できないようにすることもできます。

 本稿ではLSLの概要を紹介します。ここで説明する内容を十分に活用するには、Second Lifeにおける「制作」(building)の基礎知識があることが望ましいものの、それは本稿のコードを理解する上で必須ではありません。制作とは、3Dプリミティブ(基本形状)をSecond Lifeの世界に配置する作業です。Second Lifeで目にするものはすべて、制作者によって作成されています。

 本稿では、読者が既にSecond Lifeの制作工程にある程度精通しているという前提で話を進めます。Second Lifeで簡単なオブジェクトを制作するのはそれほど難しいことではありませんが、物を制作するにはSecond Lifeの世界のやり方を学ぶ必要があります。試しに制作してみるには、Second Lifeウィンドウの下にある[Build]ボタンをクリックします。

LSLの基礎

 LSLは、一見するとCによく似ています。しかし、プログラミングはCよりもずっと簡単です。ポインタはなく、strcmpなどの関数を使わずに直接文字列を比較できます。LSLはオブジェクト指向ではありません。独自のオブジェクトを作成することはできず、3D関連のオブジェクトもわずかしか用意されていません。LSLは状態ベースです。すべてのLSLスクリプトには特定の状態があり、状態を遷移させて関数を実行します。この概念は、多くのプログラミング言語とかなり異なるものです。状態マシンは多くの言語で作成できますが、LSLでは状態マシンの概念がもともと言語に組み込まれています。

 LSLスクリプトは、Second Lifeの3Dプリミティブの中に配置されます。オブジェクトはプリミティブの集合です。例えば、Second Lifeの車は1つのオブジェクトです。しかし、車オブジェクトは、それぞれが独自のスクリプトを含む多くのプリミティブで構成されます。また、プリミティブは相互に、あるいは人間のプレーヤーと通信することができます。高度なプログラミングを使うと、プリミティブはSLの外部にあるWebページとも通信できるようになります。

 LSLはイベントドリブンでもあります。Second Lifeのほとんどのオブジェクトは、イベントをきっかけに状態を遷移しながら機能します。Second Lifeには、さまざまな種類のイベントが用意されています。その多くはユーザーがオブジェクトに触れたり座ったりしたときに発生するユーザーベースのイベントですが、ユーザーの操作が不要なタイマーイベントもサポートされています。

 これ以降は、Second Lifeでエレベータを動かす方法について説明します。

簡単なエレベータの制作

 例として、本稿ではSecond Lifeの高層ビルを上下に移動するエレベータを作成する方法を示します。図1は、このエレベータを設置する高層ビルです。

図1 Second Lifeの高層ビル。この高層ビルにエレベータを設置する
図1 Second Lifeの高層ビル。この高層ビルにエレベータを設置する

 このエレベータが実際に動作する様子は、Second LifeのX座標51、Y座標79の位置にある「Gyeonu」リージョンで確認できます。Second Lifeでは、「SLURL」と呼ばれる特殊なURLが使われます。SLクライアントが既にインストールされていれば、エレベータの場所に直接移動することができます。SLURLリンクがうまく機能しない場合は、SLクライアントの[Map]ボタンをクリックして「Gyeonu」を検索し、座標フィールドに「95」、「23」、「44」という座標を入力します(※編集部注:現在、該当する座標に高層ビルのオブジェクトは設置されていないようです)。

 このリンクをたどると、エレベータがあるエリアの上空マップを示すWebサイトが開きます。Second Lifeがインストールされている場合は、[Teleport Now]をクリックしてその場所にテレポートすることができます。エレベータを使うと、屋上を含めて、高層ビルのすべての階に移動できます。エレベータを利用するには、単にエレベータに「座る」(sit)という動作を行います。これは組み込みの動作の1つで、ゲーム内のオブジェクトを右クリックするとポップアップメニューに表示されます。エレベータのポップアップメニューでは、行き先の階を選択することができます。また、高層ビルの各階にある緑色のボタンを使うと、エレベータをその階に呼ぶことができます。

エレベータの作成

 エレベータを制御しているのは2つのスクリプトです。1つ目のスクリプト「Elevator.lsl」(リスト1)は、エレベータを実際に動かし、アバターとの対話を処理します。2つ目のスクリプト「CallElevator.lsl」(リスト2)は、高層ビルの各階にある緑色のエレベータ呼び出しボタンに適用されます。このスクリプトはメインのエレベータスクリプトと通信し、エレベータを呼び出します。リスト(および[ダウンロードサンプル[#dl])にはスクリプト全体が含まれていますが、メインのエレベータスクリプトがsitイベントにどのように反応し、メニューをどのように表示するかが分かるように、スクリプトを順を追って見ていくことにします。

リスト1 Elevator.lslスクリプト
integer CHANNEL = 42; // dialog channel
list MENU_MAIN
 = ["Floor 1", "Floor 2", "Floor 3", 
    "Floor 4", "Floor 5", "Floor 6", "Floor 7"]; // the main menu

float BOTTOM = 27.300;
float FLOOR_HEIGHT = 10;
float SPEED = 1;
float target;

default
{
    state_entry()
    {
        // listen for dialog answers (from multiple users)
        llListen(CHANNEL, "", NULL_KEY, "");
        llSitTarget(<0,-0.5,0.5>, llEuler2Rot(<0,0,-90>) );
        llSetText("Sit Here to Ride Elevator",<0,0,0>,1.0);
        target = BOTTOM;
    }

    listen(integer channel, string name, key id, string message)
    {
        integer idx = llListFindList(MENU_MAIN, [message]);
        if( idx!=-1 )
        {
            llSay(0,"Elevator heading to " + message + "." );
            target = BOTTOM + (idx*10);
            state moving;
        }
    }

    changed(integer Change)
    {
        llDialog(llAvatarOnSitTarget(), 
                 "Where to?", 
                 MENU_MAIN, CHANNEL);
    }

}

state moving
{


    state_entry()
    {
        llSetTimerEvent(0.1);
    }

    timer()
    {
        vector pos = llGetPos();
        pos.x = 50.500;
        pos.y = 90.740;

        if( pos.z!=target )
        {
            if( pos.z>target )
            {
                pos.z = pos.z - SPEED;
            }
            else
            {
                pos.z = pos.z + SPEED;
            }
        }

        if(  llFabs(pos.z - target) < SPEED )
        {
            pos.z = target;
            llSetTimerEvent(0);
            llSetPos(pos);
            llSay(0,"Elevator has reached its target." );
            state default;
        }

        llSetPos(pos);

    }
}
リスト2 CallElevator.lslスクリプト(超高層ビルの各階にある緑色の呼び出しボタンでエレベータを呼び出す)
default
{
    state_entry()
    {
        llSetText("Touch to Call Elevator",<0,0,0>,1.0);
    }

    touch_start(integer total_number)
    {

        llShout(42, "Floor 1");
    }
}

エレベータの構成とセットアップ

 エレベータスクリプトの先頭付近に、次のようないくつかの「定数」があります。

integer CHANNEL = 42; // dialog channel
list MENU_MAIN
  = ["Floor 1", "Floor 2", "Floor 3", "Floor 4", 
     "Floor 5", "Floor 6", "Floor 7"]; // the main menu

float BOTTOM = 27.300;
float FLOOR_HEIGHT = 10;
float SPEED = 1;

 LSLは定数をサポートしていないので、これらは実際には論理上の定数にすぎません。ここでは高層ビルに合わせた値を指定していますが、これらの定数を変更し、各自のビルに合わせてエレベータを構成することもできます。

 CHANNELは、エレベータがエレベータ呼び出しボタンと通信するためのチャンネルを定義します。MAIN_MENUは、各階に対応するボタンを定義します。それと同時に、エレベータの行き先となる階の数も定義します。BOTTOMは、ビルの1階のZ座標です。SPEEDは、エレベータが移動する速度です。

 エレベータをセットアップするために、state_entryイベントを発生させています。このイベントは、特定の状態に遷移するたびに発生します。リスト1(または下のコード)を見ると分かるように、state_entryはdefault状態の中に配置されています。default状態はLSLスクリプト起動時の最初の状態なので、このイベントはエレベータが始動すると発生します。システムは、オブジェクトが新しい状態に遷移するたびにstate_entryイベントを発生させます。すべてのオブジェクトはdefault状態で起動するので、プログラムが起動すると常にdefault状態のstate_entryイベントが最初に発生します。次にイベントのコードを示します。

state_entry()
{
   // listen for dialog answers (from multiple users)
   llListen(CHANNEL, "", NULL_KEY, "");
   llSitTarget(<0,-0.5,0.5>, llEuler2Rot(<0,0,-90>) );
   llSetText("Sit Here to Ride Elevator",<0,0,0>,1.0);
   target = BOTTOM;
}

 llListenコマンドは、指定したチャンネルでリスニングを開始するための指示です。これにより、スクリプトはユーザーがエレベータを呼び出したときにイベントを受け取るようになります。llSitTargetでは、ユーザーがオブジェクトのどこに座ればよいかをx、y、z座標で指定しています。最初のベクトル<0,-0.5,0.5>は、エレベータの中心を基準とする位置を指定します。2番目のベクトル<0,0,-90>は、オブジェクトに対してユーザーのアバターをどのように回転させるかを指定します。この場合、アバターはz軸を中心に-90度回転します。z軸とは上下に延びる軸のことです。従って、z軸を中心に回転すると、アバターは地面に足を付けたままで、対面している方向を変えます。z軸は、頭の上から足の下まで延びている架空の直線と考えることができます。さらに、llSitTargetを呼び出すことで、ユーザーがオブジェクトに座ったときにオブジェクトがイベントを受け取れるようにしています。

 llSetTextメソッドは、オブジェクトの上部に説明文を表示します。この例では、エレベータに乗るには座る必要があることをユーザーに知らせます。最後に、エレベータの行き先のz座標(target変数に格納される)をBOTTOM、つまり1階に初期化しています。

図2 エレベータに乗った様子(高層ビルの1階でエレベータの作動を待っているところ)
図2 エレベータに乗った様子(高層ビルの1階でエレベータの作動を待っているところ)

メニューの表示

 ユーザーがエレベータに座ると、Second Lifeはエレベータスクリプトにchangedイベントを送ります。イベントハンドラのコードは次のようになっています。

changed(integer Change)
{
  llDialog(llAvatarOnSitTarget(), "Where to?",
     MENU_MAIN, CHANNEL);
}

 llDialogメソッドは、ユーザーに簡単なメニューを表示します。

 ユーザーが行き先の階を選択すると、次のlistenイベントが発生します。

listen(integer channel, string name, key id, string message)
{
  integer idx = llListFindList(MENU_MAIN, [message]);
  if( idx!=-1 )
  {
    llSay(0,"Elevator heading to " + message + "." );
    target = BOTTOM + (idx*10);
    state moving;
  }
}

 listenイベントが発生すると、messageイベントパラメータにユーザーが選択したメニュー項目のテキストが格納されます。listenイベントはまず、MAIN_MENU配列で選択された要素のインデックスを確認し、整数変数idxの値を、ユーザーが選択した階の0で始まるインデックスに設定します。次に、エレベータはその階に向かっていることを報告し、target変数を設定します。最後に、状態をmovingに変更します。

 複数のアバターが一度にエレベータに座る可能性もあります。この場合は、行き先の階を選ぶポップアップがアバター全員に表示されます。このエレベータは、現実のエレベータのように階を「待ち行列に入れる」ことはしません。代わりに、最後のアバターが選択した階がエレベータの行き先になり、すべてのアバターがこれに相乗りします。

オブジェクトの移動

 Second Lifeでオブジェクトを動かすには2種類の方法があります。オブジェクトを押して物理的に動かす方法と、x、y、z座標を使って直接動かす方法です。オブジェクトを直接動かす方が物理的に動かすよりもいくぶん簡単なので、ここでは座標ベースの移動を中心に説明します。幸い、エレベータは上下に1次元の動きしかしないので、座標ベースの移動はかなり単純です。ご想像どおり、Second Lifeでは、これはz座標になります。

 エレベータはmoving状態に遷移すると動き始めます。moving状態が始まると、state_entryイベントが発生します。

state moving
{
   state_entry()
   {
      llSetTimerEvent(0.1);
   }
 :
 :
 :

 state_entryイベントは、毎秒10回ずつ時を刻むタイマーを登録します。タイマーが時を刻むごとにtimerイベントが発生します。するとtimerイベントのコードがエレベータを動かします。timerイベントは、まず次のようにエレベータの現在位置を取得します。

timer()
{
   vector pos = llGetPos();
 :
 :
 :

 指定された行き先とz座標が一致しない場合、スクリプトは行き先に向かって上または下にエレベータを動かします。SPEED定数は、エレベータが行き先に到達する速度を制御します。

if( pos.z!=target )
{
   if( pos.z>target )
   {
      pos.z = pos.z - SPEED;
   }
   else
   {
      pos.z = pos.z + SPEED;
   }
}

 エレベータの現在位置が行き先間近になる(SPEED定数に指定された距離より短くなる)までは、タイマーが時を刻むたびにこれが繰り返されます。現在位置が行き先間近になると、スクリプトはエレベータを行き先の正確な位置まで移動し、タイマーを停止し、ユーザーに通知し、エレベータの状態をdefault状態に戻します。

if(  llFabs(pos.z - target) < SPEED )
{
   pos.z = target;
   llSetTimerEvent(0);
   llSetPos(pos);
   llSay(0,"Elevator has reached its target." );
   state default;
}

 まだ行き先間近になっていない場合はエレベータを動かし続ける必要があるので、タイマースクリプトは計算された新しい位置にエレベータを移動します。

llSetPos(pos);

 ユーザーがエレベータに座って制御する以外に、緑色のエレベータ呼び出しボタンを使って特定の階にエレベータを呼び出すこともできます。これを行うには、オブジェクト間の通信が必要になります。

オブジェクト間の通信

 Second Lifeのオブジェクトは、アバター同士が会話するのとほぼ同じ方法で相互に通信します。アバターは、近くにいるアバターに聞こえるように何かを「言う」(Say)ことができます。また、遠くに聞こえるように「叫ぶ」(Shout)こともできます。Second Lifeの通信は、すべて特定のチャンネルを通じて行われます。アバターは通常、チャンネル0を使って会話します。オブジェクトがチャンネル0でリスンしていれば、そのオブジェクトは近くにいるユーザーの話を聞くことができます。同様に、オブジェクトがチャンネル0で発言すれば、近くにいるユーザーはそれを聞くことができます。

 高層ビルの各階には、エレベータを呼び出す緑色のボタンがあります。このボタンの仕組みはごく単純で、単にどの階でエレベータを呼び出しているかを伝えているだけです。これをエレベータのメニューとまったく同じ方法で行っています。

 各階のボタンにはtouch_startイベントが含まれており、ユーザーがボタンに触れるとイベントが発生します。次に示すのは、1階のボタンのtouch_startイベントです。

touch_start(integer total_number)
{
  llShout(CHANNEL, "Floor 1");
}

 このスクリプトではメッセージを叫んで(Shout)います。高層ビルは非常に高い場合があるので、llSayメソッドよりも遠くまでメッセージを届ける必要があるからです。エレベータはlistenイベントでこのメッセージを受信し、指定された階への移動を開始します。

 本稿では、Second Lifeでエレベータを作成する方法を紹介しました。これはほんの入り口にすぎません。LSLライブラリの数々のメソッドを使うと、ゲーム内のオブジェクトをほぼ無限に作り出すことができます。商取引を行って他のオブジェクトを販売するためのオブジェクトを作成したり、Second Life内のゲームで使う武器を作成したりできます。また、オブジェクトで外部のWebサーバとやり取りし、スクリプトで外部リソースを利用することもできます。

 LSLについてさらに詳しく知りたければ、Linden Labs社が運用するLSLプログラミングに関するWikiで詳細な情報を得ることができます。

著者紹介

Jeff Heaton(Jeff Heaton)
ライター、大学教員、コンサルタントとして活動中。4冊の著作があり、論文誌および雑誌で20を超える記事を発表。また、個人のWebサイトを管理し、人工知能とスパイダー/ボットプログラミングをはじめとする話題について情報発信を行っている。メールの宛先はjheaton@heatonresearch.com
このエントリーを含むはてなブックマーク この記事をクリップ!
BuzzurlにブックマークBuzzurlにブックマーク Yahoo!ブックマークに登録
この記事をokyuuへインポート
最新トップニュース
データメーション
【データメーション】
中国が「Green Dam」フィルタ規制を撤回(7月1日)
Graphic Design Forum
【Graphic Design Forum】
Chris Dickman(6月25日)
プライバシー ジャパン・インターネットコム版
【プライバシー ジャパン・インターネットコム版】
グーグル・ストリートビューの問題について総務省の見解(6月23日)
エンジニアの独り言
【エンジニアの独り言】
システムを「使う」時代のエンジニアに求められるもの(6月2日)
最新ハイテク講座
最新ハイテク講座
電気は家庭でつくる時代へ!燃料電池「エネファーム」(7月3日)
アクセス解析で見るWebマーケティング
アクセス解析で見るWebマーケティング
決定力を探るアクセス解析(7月3日)
百式のネットビジネス研究
百式のネットビジネス研究
ファーストフードを高級っぽく盛り付けて紹介している「Fancy Fast Food」(7月3日)
週刊-サイト別アクセス状況データ
週刊-サイト別アクセス状況データ
ビデオリサーチインタラクティブ調査(月間インターネットオーディエンスデータ)(7月2日)
成約率、反応率を上げる Web 文章術
成約率、反応率を上げる Web 文章術
言葉がダイレクトにキャッシュを生む(7月2日)
不況時代の Web ビジネス最適化講座
不況時代の Web ビジネス最適化講座
アクセス解析エキスパートここだけの話、Web コンシェルジュの“勉強法”こっそり教えます(7月2日)
「Webからの脅威」―その傾向と最新対策
「Webからの脅威」―その傾向と最新対策
不正プログラムの分類(7月1日)
DevX
DevX
JavaScriptとDOMによる動的なWebページの作成(6月30日)
エンジニア転職ノウハウ開発室
エンジニア転職ノウハウ開発室
今のままで大丈夫?3匹の子ブタ的キャリア危険度診断(6月30日)
アイレップの SEM フロンティア
アイレップの SEM フロンティア
Web サイトは「無駄な穴のたくさん開いたじょうご」〜サイト成果向上の基本的な考え方(6月30日)
Copyright 2009 Japan Internet.com K.K. All Rights Reserved.http://www.internet.com/