Translations: English

Get started (A-FRAME)

A-FRAMEopen in new windowとVerseEngineの簡易ライブラリverse-threeを使った、VerseEngineの組み込み方法を簡単に紹介します。

あるいは、完成版のソースコードをここ(github.com)open in new windowからダウンロードすることもできます。

Step 0: 空間を作る

まず、A-FRAMEで簡単な空間を作成します。
詳細については、A-FRAMEのマニュアルopen in new windowを参照してください。

<html>
  <head>
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1,viewport-fit=cover"
    />
    <style>
      .a-enter-ar {
        display: none;
      }
    </style>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/aframe-master.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/aframe-environment-component.min.js"></script>
  </head>
  <body>
    <a-scene environment="ground: flat" renderer="colorManagement: true;">
    </a-scene>
  </body>
</html>

Step 1: 毎フレーム処理の追加の準備をする

...
  <script>
    if (!window.__extendAnimationLoop) {
      window.__extendAnimationLoop = () => {};
    }
    AFRAME.registerSystem("extend-animation-loop", {
      tick: function (time, timeDelta) {
        window.__extendAnimationLoop(time, timeDelta);
      },
    });
  </script>
</head>
...

 
 
 
 
 
 
 
 
 
 


Step 2: プレイヤーを設定する

...
<body>
  <a-scene environment="ground: flat" renderer="colorManagement: true;">
    <a-entity id="player">
      <a-entity id="headOffset">
        <a-entity id="head" camera position="0 1.6 0"></a-entity>
      </a-entity>
    </a-entity>
  </a-scene>
</body>
...



 
 
 
 
 



Step 3: VerseEngineをロードする

verse-threeのIIFE版と、verse-three内部で使用するthree-vrmopen in new windowをロードします。

  ...
  <script src="https://cdn.jsdelivr.net/npm/@pixiv/[email protected]/lib/three-vrm.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/@verseengine/[email protected]/dist/index.min.js"></script>
</head>
...

 
 


Step 4: VerseEngineと接続する

アニメーションとアバターファイルは独自に用意ことも可能ですが、 verse-threeのexampleopen in new windowをダウンロードして手早く試すこともできます。

setup-verse.js

(() => {
  const { VerseThree, THREE } = window;
  const VERSE_WASM_URL =
    "https://cdn.jsdelivr.net/npm/@verseengine/[email protected]/dist/verse_core_bg.wasm";
  const ENTRANCE_SERVER_URL = "https://entrance.verseengine.cloud";
  // アニメーションファイル
  const ANIMATION_MAP = {
    idle: "./asset/animation/idle.fbx",
    walk: "./asset/animation/walk.fbx",
  };
  // アバターファイル
  const range = (n) => [...Array(n).keys()];
  export const PRESET_AVATARS = [
    ...range(3).map((i) => `f${i}`),
    ...range(3).map((i) => `m${i}`),
  ].map((n) => ({
    thumbnailURL: `./asset/avatar/${n}.png`,
    avatarURL: `./asset/avatar/${n}.vrm`,
  }));
  const DEFAULT_AVATAR_URL =
    PRESET_AVATARS[Math.floor(Math.random() * PRESET_AVATARS.length)].avatarURL;
  const ICE_SERVERS = [
    { urls: "stun:stun.l.google.com:19302" },
    { urls: "stun:stun1.l.google.com:19302" },
  ];

  async function waitForAframeLoad(scene) {
    if (scene.hasLoaded) { return; }
    return new Promise((resolve) => scene.addEventListener("loaded", resolve));
  }

  main();

  function main() {
    if (document.readyState === "loading") {
      document.addEventListener("DOMContentLoaded", _main);
    } else {
      _main();
    }
  }

  async function _main() {
    const scene = document.querySelector("a-scene");
    await waitForAframeLoad(scene);

    let collisionObjects = []; // 障害物
    let teleportTargetObjects = []; // VRテレポート先
    let interactableObjects = []; // クリック対象
    let collisionBoxes = []; // collisionObjects, teleportTargetObjectsのBounding Box

    const updateCollisionBoxes = () => {
      collisionBoxes.length = 0;
      [...collisionObjects, ...teleportTargetObjects].map((el) => {
        el.traverse((c) => {
          if (!c.isMesh) { return; }
          collisionBoxes.push(new THREE.Box3().setFromObject(c));
        });
      });
    };
    const queryObjects = (q) =>  [...scene.querySelectorAll(q)].map((v) => v.object3D).filter((v) => v.visible);
    const updateCollisionObjects = () => {
      collisionObjects = queryObjects(".collider,.wall");
      teleportTargetObjects = queryObjects(".ground,.environmentGround");
      interactableObjects = queryObjects(".clickable");
      updateCollisionBoxes();
    };

    scene.object3D.updateMatrixWorld();
    updateCollisionObjects();

    const adapter = new VerseThree.AFrameEnvAdapter(
      scene,
      document.getElementById("headOffset").object3D,
      document.getElementById("player").object3D,
      () => collisionBoxes,
      () => collisionObjects,
      () => teleportTargetObjects,
      {
        getInteractableObjects: () => interactableObjects,
        onSelectDown: (o, _point) => {
          if (!o.el || !o.el.classList.contains("clickable")) {
            return;
          }
          o.el.emit("click", {intersectedEl: o.el});
        },
        isLowSpecMode: true, // 髪などの揺れを省略してパフォーマンスを改善する
      }
    );
    const mayBeLowSpecDevice = VerseThree.isLowSpecDevice();
    const res = await VerseThree.start(adapter, scene.object3D,
      VERSE_WASM_URL, ENTRANCE_SERVER_URL, DEFAULT_AVATAR_URL,
      ANIMATION_MAP, ICE_SERVERS,
      {
        maxNumberOfPeople: mayBeLowSpecDevice ? 8 : 16,
        maxNumberOfParallelFileTransfers: mayBeLowSpecDevice ? 1 : 4,
        presetAvatars: PRESET_AVATARS,
      }
    );

    // 定期処理の関数をセットする
    window.__extendAnimationLoop = (_time, timeDelta) => {
      res.tick(timeDelta / 1000);
    };
  }
})();

index.html

  ...
  <script src="./setup-verse.js"></script>
</head>
...

 


完成版のリンク等

より詳細な使い方は、verse-threeリファレンスopen in new windowを参照してください。

Tips

WARNING

VerseEngineは、WebページのURL毎にユーザーのネットワークを構築します。
開発のためにlocalhostやローカルIP(192.168.xxx.xxx等)を使うときに、URLによっては見知らぬ人と接続してしまう場合があります。
hostsファイルでIPに専用の名前を割り当てるか、htmlファイルに十分に長いランダムのファイル名をつけることでそれを回避することができます。

# https://very-long-name12345678/
127.0.0.1 very-long-name12345678

TIP

全ての機能を使うためには、HTTPSで通信する必要があります。

簡単にWebサーバーをHTTPS対応できるサービス

ローカルに開発用証明書を作成するツール

Macでローカル開発用にHTTPS Serverを起動する例

# Homebrewを使ってmkcertをインストールする
brew install mkcert
mkcert -install
# 証明書を作成する (IPアドレスは環境に合わせて変更)
mkdir cert
cd cert
mkcert localhost 127.0.0.1 192.168.10.2
cd ..
# サーバーを起動する
npx http-server -c-1 --ssl --key ./cert/localhost+2-key.pem --cert ./cert/localhost+2.pem -p 8080
Last Updated: