networked-aframe
Version:
A web framework for building multi-user virtual reality experiences.
195 lines (173 loc) • 7.44 kB
HTML
<html>
<head>
<meta charset="utf-8" />
<title>Nametag Example — Networked-Aframe</title>
<meta name="description" content="Nametag — Networked-Aframe" />
<script src="https://aframe.io/releases/1.6.0/aframe.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.8.1/socket.io.min.js"></script>
<script src="/easyrtc/easyrtc.js"></script>
<script src="/dist/networked-aframe.js"></script>
<script src="https://cdn.jsdelivr.net/gh/c-frame/aframe-extras@7.5.2/dist/aframe-extras.controls.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/aframe-environment-component@1.3.7/dist/aframe-environment-component.min.js"></script>
<script src="/js/spawn-in-circle.component.js"></script>
<script>
// Temporary workaround for template declaration; see issue 167
NAF.schemas.getComponentsOriginal = NAF.schemas.getComponents;
NAF.schemas.getComponents = (template) => {
if (!NAF.schemas.hasTemplate('#head-template')) {
NAF.schemas.add({
template: '#head-template',
components: [
{
component: 'position',
requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.001)
},
{
component: 'rotation',
requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.5)
},
// In this example, we don't sync the material.color itself, like the basic example;
// we instead sync player-info, which includes color setting + updating.
// (you can see an example of the other pattern in the basic.html demo)
'player-info'
]
});
}
if (!NAF.schemas.hasTemplate('#rig-template')) {
NAF.schemas.add({
template: '#rig-template',
components: [
{
component: 'position',
requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.001)
},
{
component: 'rotation',
requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.5)
}
]
});
}
const components = NAF.schemas.getComponentsOriginal(template);
return components;
};
</script>
<script>
window.ntExample = {
randomColor: () => {
return '#' + new THREE.Color(Math.random(), Math.random(), Math.random()).getHexString();
}
};
AFRAME.registerComponent('player-info', {
// notice that color and name are both listed in the schema; NAF will only keep
// properties declared in the schema in sync.
schema: {
name: { type: 'string', default: 'user-' + Math.round(Math.random() * 10000) },
color: {
type: 'color', // btw: color is just a string under the hood in A-Frame
default: window.ntExample.randomColor()
}
},
init: function () {
this.head = this.el.querySelector('.head');
this.nametag = this.el.querySelector('.nametag');
this.ownedByLocalUser = this.el.id === 'player';
if (this.ownedByLocalUser) {
// populate the html overlay with the correct name on init
this.nametagInput = document.getElementById('username-overlay');
this.nametagInput.value = this.data.name;
// add the initial color to the html overlay color picker button
document.querySelector('#color-changer').style.backgroundColor = this.data.color;
document.querySelector('#color-changer').style.color = this.data.color;
}
},
// here as an example, not used in current demo. Could build a user list, expanding on this.
listUsers: function () {
console.log(
'userlist',
[...document.querySelectorAll('[player-info]')].map((el) => el.components['player-info'].data.name)
);
},
newRandomColor: function () {
this.el.setAttribute('player-info', 'color', window.ntExample.randomColor());
},
update: function () {
if (this.head) this.head.setAttribute('material', 'color', this.data.color);
if (this.nametag) this.nametag.setAttribute('value', this.data.name);
}
});
</script>
</head>
<body>
<!-- This <button> and <input> will overlay on top of the A-Frame canvas--this style is all you need.
Note the onclick and oninput attributes; on every call, they will run .setAttribute() for the player-info
component's data. NAF will keep that property in sync, since 'name' and 'color' are declared in the schema
of the 'player-info' component
-->
<button
id="color-changer"
style="z-index: 100; bottom: 24px; left: 24px; position: fixed"
onclick="let newColor = window.ntExample.randomColor();
document.getElementById('player').setAttribute('player-info', 'color', newColor);
document.querySelector('#color-changer').style.backgroundColor = newColor;
document.querySelector('#color-changer').style.color = newColor;
"
>
■
</button>
<input
id="username-overlay"
style="z-index: 100; bottom: 24px; left: 48px; position: fixed"
oninput="document.getElementById('player').setAttribute('player-info', 'name', this.value)"
/>
<a-scene
networked-scene="
room: nametag;
debug: true;
adapter: wseasyrtc;
"
renderer="physicallyCorrectLights: true;"
>
<a-assets>
<template id="rig-template">
<a-entity></a-entity>
</template>
<template id="head-template">
<a-entity class="avatar" player-info>
<a-sphere class="head" scale="0.2 0.22 0.2"></a-sphere>
<a-entity class="face" position="0 0.05 0">
<a-sphere class="eye" color="white" position="0.06 0.05 -0.16" scale="0.04 0.04 0.04">
<a-sphere class="pupil" color="black" position="0 0 -1" scale="0.2 0.2 0.2"></a-sphere>
</a-sphere>
<a-sphere class="eye" color="white" position="-0.06 0.05 -0.16" scale="0.04 0.04 0.04">
<a-sphere class="pupil" color="black" position="0 0 -1" scale="0.2 0.2 0.2"></a-sphere>
</a-sphere>
</a-entity>
<!-- here we add a text component for a nametag; the value will be updated by the player-info component -->
<a-text
class="nametag"
value="?"
rotation="0 180 0"
position=".25 -.35 0"
side="double"
scale=".5 .5 .5"
></a-text>
</a-entity>
</template>
</a-assets>
<a-entity environment="preset:starry;groundColor:#000000;"></a-entity>
<a-entity light="type:ambient;intensity:0.5"></a-entity>
<a-entity id="rig" movement-controls="fly:true;" spawn-in-circle="radius:3" networked="template:#rig-template;">
<a-entity
id="player"
camera
position="0 1.6 0"
look-controls
networked="template:#head-template;"
visible="false"
></a-entity>
</a-entity>
</a-scene>
</body>
</html>