Yes Second Life

セカンドライフ向けWebサービスを作ってました。このブログではVR・メタバースのことを書いていきます

High Fidelityを、ローカルマシンにインストールして接続する

1年ぶりですね。

毎年アドベントカレンダーの季節になると、更新されるこのブログ。
お察しの通り、この記事は「セカンドライフ技術系 Advent Calendar 2015」向けの記事です。

www.adventar.org

しかも、セカンドライフ技術系のアドベントカレンダーなのに、今回はHigh Fidelityというまだテスト段階のメタバースについて書いていきます。High Fidelityの創業者が、元リンデンCEOのフィリップさんということで一応セカンドライフとの関連も深いので、その辺は大目に見てくださいね。

f:id:sabro:20151124023137p:plain

High Fidelity

トップサイトに書いてあるとおり、High Fidelityは、サーバ、クライアントともにオープンソースで開発されているメタバースです。リンデンラボの次期メタバース「SANSAR」は、少なくとも最初はクローズに開発される予定であり、はじめからオープンであることはHigh Fidelityの際立った特徴です。

github.com
ソースは、GitHubで管理されてます。

f:id:sabro:20151124024429p:plain

Download - High Fidelity

High Fidelityのダウンロードページに行くと、Stack ManagerとInterfaceの2つがあることが分かります。サーバ側がStack Manager、クライアントのビューワがInterfaceです。ダウンロードしたインストーラを実行し、普通に進めていけば簡単にインストールできます。

f:id:sabro:20151124025118p:plain

インストールが終わって、Stack Managerを実行してみたところ、何回かエラーダイアログがでましたが、無視して進めていきました。

f:id:sabro:20151124025153p:plain

そしたらちゃんと、サーバが起動したので良しとします。
右上に「hifi://localhost」みたいなURLがあって、こちらをクリックするとInterfaceが起動します。

f:id:sabro:20151129222306p:plain

なぜかアバターはロボでした。とにかくこれで、ローカルマシンでサーバを立ち上げ接続できました。もっと大変かと思っていたので、割りと拍子抜けです。

f:id:sabro:20151129224633p:plain

適当にオブジェクトを作ってみましたが、セカンドライフとそんなには変わらない操作でつくれました。この辺はまた機会があれば、書いてみようとおもいます。

Pathfindingで、複数箇所を巡回させる

これは、セカンドライフ技術系アドベントカレンダーの記事です。

セカンドライフ技術系 Advent Calendar 2014 - Adventar
今回は、Pathfindingで複数箇所を巡回させてみます。

Secondlife Pathfinding機能 - llPatrolPoints - YouTube
複数箇所を巡回させたいときは、llPatrolPoints関数を使います。

次の例では、"target"という名前のオブジェクトを周りからさがして、複数見つかった場合に、そのポイントを巡回させます。

default {
    
    state_entry()
    {
        // オブジェクトをPathfindingのキャラクターに設定
        llCreateCharacter([]);
    }

    touch_start(integer num)
    {
        // タッチされたら、targetという名前のオブジェクトを探す
        llSensor("target", "", PASSIVE, 25.0, PI);
    }
    
    sensor(integer num)
    {
        // オブジェクトが1つの場合、パトロールできないので処理続行しない
        if(num<=1) return;
        
        integer i;
        list l;
        
        // targetというオブジェクト郡の位置をリストに格納
        for(i=0; i<num; i++)
        {
            l += llDetectedPos(i);
        }
        
        // オブジェクト郡を巡視する
        llPatrolPoints(l, []);
    }

    no_sensor()
    {
        // targetというオブジェクトが見つからなかったら何もしない
        llOwnerSay("No target in range.");
    }
}

巡回ポイントを1つしか指定しないとエラーになるので気をつけてください。llPatrolPoints関数には、リストで1つ引数を指定できます。

オプション 説明 デフォルト値
PATROL_PAUSE_AT_WAYPOINTS 巡回ポイントに到達した時、一瞬止まるようにする FALSE

前回の追跡に続いて、巡回を紹介しましたが、Pathfindingにはまだまだ色んな動作があります。気になったら調べてみると面白いですよ( ̄∇  ̄ )

PathFindingで、自分についてくるオブジェクトをつくる

これは、セカンドライフ技術系アドベントカレンダー向けの記事です。

自分についてくるオブジェクトは、今までも力技でつくれたんですが、最近追加されたPathFindingを使うと、とても簡単に実装できます。


Secondlife Pathfinding機能 - llPursue - YouTube

PathFindingは、直訳すると経路探索ですが、色んな動きができるNPC(ノンプレイヤーキャラクター)を作れる機能になっています。自分についてくる他にも、何かから逃げる動きをさせたり、特定の場所を徘徊させたりもできます。

何かについてくるようにしたい場合は、次のようにすればできます。

default
{
    state_entry()
    {
        // オブジェクトをPathfindingのキャラクターに設定
        // 実際には引数には色んなパラメータが渡せます
        llCreateCharacter([]);
    }
 
    touch_start(integer total_number)
    {
        // タッチした人の2m後ろを追跡する
        llPursue(llDetectedKey(0), [PURSUIT_OFFSET, <-2.0, 0.0, 0.0>]);
    }
}

たったこれだけ、llCreateCharacter関数で、Pathfindingのキャラクターとして設定し、llPursue関数で追跡させるように命令すればOKです。llPursue関数にもリストでいくつかの引数が渡せます。

オプション 説明 デフォルト値
PURSUIT_OFFSET 追跡対象からの距離 ZERO_VECTOR
REQUIRE_LINE_OF_SIGHT 動作するのに物理的に到達可能である必要があるか FALSE
PURSUIT_FUZZ_FACTOR PURSUIT_OFFSETからランダムで位置をずらす、0.0~1.0で指定 0.0
PURSUIT_INTERCEPT ターゲットの未来の位置を予測して動くか FALSE
PURSUIT_GOAL_TOLERANCE 実際のゴールからどれくらい離れてるのを許容するか 追跡者の大きさに比例

とても簡単に高度な動きが実装できるので、試してみると面白いとおもいますよ( ̄∇  ̄ )

セカンドライフの魅力

これはセカンドライフ非技術系アドベントカレンダー向けの記事です。

セカンドライフ非技術系 Advent Calendar 2014 - Adventar
ぼくにとっての、セカンドライフの魅力を語ります。

ルールがないこと

ふつうのMMOだと、クエストが用意されていたり、スキルシステムがあったりして、決められた手順を追ってゲームを進めます。しかし、セカンドライフには決められたことがなにもないので、自分でなにかを創りださないとなにも始まりません。それが逆に、なにか作れる人にとっては魅力になります。

まだ何もない世界で、自分が培ってきた技術力をつかって、何かを生み出していく、それがこの世界そのものを拡張していく、そういうエキサイティングな面白さがあります。

ルールを作り出せること

しかし、ルールがないと遊べないという人がいることも事実。というかそういう人のほうが多数派です。だったら何か自分たちでルールを作り出せばいい。セカンドライフには、ユーザ自身がルールを作っていけるほどの自由があります。
QuestMeister
これは、ぼくが作ったクエストマイスターというサイトです。このサイトで情報を入力して、セカンドライフ上でオブジェクトを配置すれば、誰でもクエストを作ることができます。自由すぎて何をしていいかわからない世界に、一定のルールを作りだせました。セカンドライフAPI、LSL言語がそれを可能にします。

仮想世界は今でこそ自由ですが、やがては色んなサービスがこうした枠組みを作っていき、最終的には手順を追って物事を進めていけるような世界になっていくとおもいます。まさになにもなかったWebで、掲示板、ブログ、動画サイトなんかが、フォーマットを与えていったように。

まだそうなっていない今、こうした枠組みをあーだこーだ考えて実装していくことが、高度な知的ゲームのようで自分には魅力的なのです。

子プリムに名前でアクセスする

2014年、セカンドライフ技術系アドベントカレンダーの1発めの記事です。

セカンドライフのオブジェクトは、いくつかのプリムをリンクさせて作成するわけですが、リンクされた子プリムには基本的に数値インデックスでしかアクセスできません。

llMessageLinked(2, 0, llGetScriptName(), "");    // 第1引数の「2」がインデックス

llSetLinkPrimitiveParams(3, [PRIM_FULLBRIGHT, ALL_SIDES, FALSE]);    // 第1引数の「3」がインデックス

しかし、子プリムの番号はリンクをやり直したりすると変わってしまいます。これではそのたびに、スクリプトの数値インデックスの変更が必要で不便。できれば子プリムの名前を指定してアクセスしたいところです。そこで、最初に名前とインデックスの索引をつくってしまう方法が楽です。

list linkPrims;    // 索引格納用リスト


// 索引を作成
createIndex()
{
    integer i;
    linkPrims = ["root"];    // インデックス0はルートプリムを表す
    for(i=1; i<=llGetNumberOfPrims(); i++)
    {
        // インデックス順に子プリムの名前をlinkPrimsへ格納
        string linkPrim = llGetLinkName(i);
        linkPrims += [linkPrim];
    }
}

// 名前をインデックスに変換
integer getIndex(string name)
{
    return llListFindList(linkPrims, [name]);   
}


default
{
    state_entry()
    {
        createIndex();
    }

    touch_start(integer num)
    {
    // 子プリムに名前でアクセス
        llSetLinkColor(getIndex("ResetButton"), <1.0, 0.0, 0.0>, ALL_SIDES);
    }

    // リンク変更時にリセット(いちごさんの指摘で追加)
    changed(integer change)
    { 
        if(change & CHANGED_LINK)
        {
            llResetScript(); 
        }
    }
}

この方法だと、機能追加でHUDにボタンを追加したりしても、子プリムの名前を変えないかぎりスクリプトのインデックスは変更不要でメンテも楽ですね( ̄∇  ̄ )

FacebookのOculus買収と、仮想世界への本気度

FacebookがOculusを買収して、けっこうなニュースになってるけど、「Facebookが資金をもてあまして流行りものに手を出したんじゃ」みたいな批判を目にしたので、FacebookのVRへの本気度について知ってる範囲で書いておきます。

2007年からのセカンドライファーならおそらく誰でも知っているCory Ondrjkaさんという開発者がいます。セカンドライフスクリプト言語、LSLなどを開発した方で、VR開発者の中でも非常にカリスマ性がある人です。Coryさんは、VRバブルにのって拡大するリンデンラボと反りが合わず、CEOのフィリップと口論してやめることになったのですが、2010年からFacebookで働いています。

そして、Facebookアプリでもある独自の仮想世界CloudPartyを、2012年6月に立ち上げました。OculusRiftの発表も2012年の6月でしたが、当然CloudPartyの開発自体はそれ以前から始められていたはずで、つまりFacebookはOculusがでてくる前から、VR技術には注目していたことになります。

なので、資金を持て余したとか、流行りモノだからという理由じゃなくて、いま自分たちの取り組んでいるVRに必要だと感じて、Oculusを買収したというのが自然な流れだとおもいます。

Facebookの本気度については、こういう理由で自分は疑ってないんですが、批判はもうひとつあります。いままでユーザが育ててきてオープンだったOculusが、Facebookによって不自由なものにされてしまうんじゃないかというものです。

こちらに関しては、正直よく分かりません。ぼく自身Facebookのクローズさがイヤで、アカウントだけ作ってあるものの全く使ってない状態です。ただ、FacebookのVR戦略にまず関わってるであろうCoryさんの性格上、あまりクローズにはならないのではないかと自分は楽観視しています。Coryさんはセカンドライフでユーザが作ったものがリンデンラボでなくユーザ自身に著作権が属するように積極的に働きかけました。そういったオープンな姿勢をみているので、変な囲い込みをするのが自分にはなかなか想像できません。

まとめると、FacebookのVRへの取り組みは本気であり、目的意識なくOculusを買収したわけではない。Facebookに買収されてクローズなかんじになるかは分からないが自分は楽観視しているというかんじです。

アニメーションオーバーライドできる新関数

セカンドライフアバターのデフォルトの歩いたり座ったりするアニメーションは、今までは基本的に上書きするのにハックが必要でした。タイマーをコンマ数秒単位で回して、アバターの状態を監視する方法です。しかしこれはシムへの負荷がかなり掛かるものでした。

そこで最近、負荷を抑えつつアニメーションを上書きできる新関数が追加されました。

llSetAnimationOverride

llSetAnimationOverride( string anim_state, string anim )

llSetAnimationOverrideは、アニメーションを上書きします。第1引数は「上書きする状態」、第2引数は上書きするアニメーションの名前です。例えば、歩く動作をしているとき座るアニメーションを再生したい場合は、次のようにします。

default
{
    touch_start(integer num)
    {
        // パーミッションを取得
        llRequestPermissions(llDetectedKey(0), PERMISSION_OVERRIDE_ANIMATIONS);
    }
    
    run_time_permissions(integer perm)
    {
        if(perm & PERMISSION_OVERRIDE_ANIMATIONS)
        {
            // 歩く動作のとき、座るアニメーションをさせる
            llSetAnimationOverride("Walking", "sit");
        }
    } 
}

上書きできる状態は、これだけあります。

  • Crouching
  • CrouchWalking
  • Falling Down
  • Flying
  • FlyingSlow
  • Hovering
  • Hovering Down
  • Hovering Up
  • Jumping
  • Landing
  • PreJumping
  • Running
  • Sitting
  • Sitting on Ground
  • Standing
  • Standing Up
  • Striding
  • Soft Landing
  • Taking Off
  • Turning Left
  • Turning Right
  • Walking

アニメーションは、基本的にはプリムに格納しておいて、その名前を第2引数で指定するのですが、組み込みアニメーションであれば、プリムになくても使用できます。上記のサンプルも組み込みアニメーションを使いました。

llGetAnimationOverride

llGetAnimationOverride( string anim_state )

llGetAnimationOverrideは、いま再生しているアニメーションの名前を取得できます。引数には、アニメーションの名前を取得したい状態を指定します。歩いているときは、普段は「walk」というアニメーションが再生されていますが、さっきのllSetAnimationOverrideのサンプルを実行したあとで以下のスクリプトを動かすと、「sit」と表示されます。

default
{
    touch_start(integer num)
    {
        // パーミッションを取得
        llRequestPermissions(llDetectedKey(0), PERMISSION_OVERRIDE_ANIMATIONS);
    }
    
    run_time_permissions(integer perm)
    {
        if(perm & PERMISSION_OVERRIDE_ANIMATIONS)
        {
            // 歩いているときに再生されているアニメーションの名前を取得
            llOwnerSay(llGetAnimationOverride("Walking"));
        }
    }
}

llResetAnimationOverride

llResetAnimationOverride( string anim_state )

現在指定されているアニメーションを、デフォルトのものにリセットします。引数にはアニメーションをリセットしたい状態を指定します。llSetAnimationOverrideのサンプルを実行したあとで、次のスクリプトを動かすと、歩くときのアニメーションが「walk」にもどります。

default
{
    touch_start(integer num)
    {
        // パーミッションを取得
        llRequestPermissions(llDetectedKey(0), PERMISSION_OVERRIDE_ANIMATIONS);
    }
    
    run_time_permissions(integer perm)
    {
        if(perm & PERMISSION_OVERRIDE_ANIMATIONS)
        {
            // 歩いているときに再生されているアニメーションをデフォルトにもどす
            llResetAnimationOverride("Walking");
        }
    }
}

なお、llResetAnimationOverrideだけ引数に「ALL」が指定でき、すべてのアニメーションをリセットすることができます。

自分で何かつくるときはもちろん、既存のアニメーションオーバーライドツールを使う時も、できれば新しい関数を使って負荷をへらしているかチェックしてみるといいかもしれませんね( ̄∇  ̄ )