Translations: English
Get started (A-FRAME)
A-FRAMEとVerseEngineの簡易ライブラリverse-threeを使った、VerseEngineの組み込み方法を簡単に紹介します。
あるいは、完成版のソースコードをここ(github.com)からダウンロードすることもできます。
Step 0: 空間を作る
まず、A-FRAMEで簡単な空間を作成します。
詳細については、A-FRAMEのマニュアルを参照してください。
<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-vrmをロードします。
...
<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のexampleをダウンロードして手早く試すこともできます。
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リファレンスを参照してください。
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対応できるサービス
- Cloudflare - ウェブサイトのセキュリティとパフォーマンス改善
- Let's Encrypt - フリーな SSL/TLS 証明書
- ZeroSSL - Free SSL Certificates and SSL Tools
ローカルに開発用証明書を作成するツール
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