Get started (A-FRAME)
This is a brief introduction on how to incorporate VerseEngine using A-FRAME and VerseEngine's simple library verse-three.
Alternatively, you can download the complete source code from here (github.com).
Step 0: Create the world
First, a simple space is created in A-FRAME.
For more information, see the A-FRAME manual.
<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: Prepare to add processing every frame
...
<script>
if (!window.__extendAnimationLoop) {
window.__extendAnimationLoop = () => {};
}
AFRAME.registerSystem("extend-animation-loop", {
tick: function (time, timeDelta) {
window.__extendAnimationLoop(time, timeDelta);
},
});
</script>
</head>
...
Step 2: Set up the player
...
<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: Load VerseEngine
Load the IIFE version of verse-three and the three-vrm used inside the verse-three.
...
<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: Connect with VerseEngine
You can prepare your own animation and avatar files, though, You can also download verse-three example and try it out quickly.
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";
// Animation files
const ANIMATION_MAP = {
idle: "./asset/animation/idle.fbx",
walk: "./asset/animation/walk.fbx",
};
// Avatar files
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 = []; // obstacle
let teleportTargetObjects = []; // VR teleport destination
let interactableObjects = []; // clickable object
let collisionBoxes = []; // Bounding boxes for collisionObjects and teleportTargetObjects
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, // Improve performance by omitting hair and other shaking
}
);
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,
}
);
// Set processing for every frame
window.__extendAnimationLoop = (_time, timeDelta) => {
res.tick(timeDelta / 1000);
};
}
})();
index.html
...
<script src="./setup-verse.js"></script>
</head>
...
Link to completed version, etc.
For more detailed usage, see verse-three reference.
Tips
WARNING
VerseEngine builds a network of users for each web page URL.
When using localhost or local IP (e.g. 192.168.xxx.xxx) for development, some URLs may connect to strangers.
Either assign a dedicated name to the IP address in the hosts file,
Alternatively, you can work around it by giving the html file a sufficiently long random filename.
# https://very-long-name12345678/
127.0.0.1 very-long-name12345678
TIP
To use all functions, you must communicate via HTTPS.
Examples of services that can easily support HTTPS for web servers
- Cloudflare - The Web Performance & Security Company
- Let's Encrypt - free, automated, and open certificate authority
- ZeroSSL - Free SSL Certificates and SSL Tools
Tools to create local development certificates
Example of starting HTTPS Server for local development on Mac
# Install mkcert using Homebrew
brew install mkcert
mkcert -install
# Create a certificate (change the IP address to match your environment)
mkdir cert
cd cert
mkcert localhost 127.0.0.1 192.168.10.2
cd ..
# Start the server
npx http-server -c-1 --ssl --key ./cert/localhost+2-key.pem --cert ./cert/localhost+2.pem -p 8080