<!DOCTYPE html>
<html>
<head>
<title>VRM All Bones Sampler with Advanced Controls (Full Code)</title>
<meta charset="utf-8">
<script src="https://aframe.io/releases/1.5.0/aframe.min.js"></script>
<script src="./js/aframe-vrm.js"></script>
<script>
AFRAME.registerComponent('camera-relative-controls', {
schema: {
targetSpeed: { type: 'number', default: 5 },
acceleration: { type: 'number', default: 10 },
damping: { type: 'number', default: 8 },
brakingDeceleration: { type: 'number', default: 20 },
enabled: { type: 'boolean', default: true },
rotationSpeed: { type: 'number', default: 1.5 },
verticalSpeed: { type: 'number', default: 3 },
groundY: { type: 'number', default: 0 },
ceilingY: { type: 'number', default: 50 }
},
init: function () {
this.keys = {};
this.leftThumbstickInput = { x: 0, y: 0 };
this.rightThumbstickInput = { x: 0, y: 0 };
this.currentVelocity = new THREE.Vector3();
this.cameraDirection = new THREE.Vector3();
this.cameraRight = new THREE.Vector3();
this.moveDirection = new THREE.Vector3();
this.desiredVelocity = new THREE.Vector3();
this.cameraWorldQuaternion = new THREE.Quaternion();
this.rigEl = this.el;
this.cameraEl = this.el.querySelector('[camera]');
this.el.sceneEl.addEventListener('loaded', () => {
this.leftHand = document.getElementById('leftHand');
if (this.leftHand) { this.leftHand.addEventListener('thumbstickmoved', this.onLeftThumbstickMoved.bind(this)); }
this.rightHand = document.getElementById('rightHand');
if (this.rightHand) { this.rightHand.addEventListener('thumbstickmoved', this.onRightThumbstickMoved.bind(this)); }
});
window.addEventListener('keydown', this.onKeyDown.bind(this));
window.addEventListener('keyup', this.onKeyUp.bind(this));
},
remove: function () {
window.removeEventListener('keydown', this.onKeyDown.bind(this));
window.removeEventListener('keyup', this.onKeyUp.bind(this));
if (this.leftHand) { try { this.leftHand.removeEventListener('thumbstickmoved', this.onLeftThumbstickMoved.bind(this)); } catch(e){} }
if (this.rightHand) { try { this.rightHand.removeEventListener('thumbstickmoved', this.onRightThumbstickMoved.bind(this)); } catch(e){} }
},
onLeftThumbstickMoved: function (evt) { this.leftThumbstickInput.x = evt.detail.x; this.leftThumbstickInput.y = evt.detail.y; },
onRightThumbstickMoved: function (evt) { this.rightThumbstickInput.x = evt.detail.x; this.rightThumbstickInput.y = evt.detail.y; },
tick: function (time, timeDelta) {
if (!this.data.enabled || !this.cameraEl) return;
const data = this.data;
const dt = timeDelta / 1000;
const position = this.rigEl.object3D.position;
if (this.rigEl.sceneEl.is('vr-mode')) {
if (Math.abs(this.rightThumbstickInput.x) > 0.1) { this.rigEl.object3D.rotation.y -= this.rightThumbstickInput.x * data.rotationSpeed * dt; }
}
const cameraObject = this.cameraEl.object3D;
cameraObject.getWorldQuaternion(this.cameraWorldQuaternion);
this.cameraDirection.set(0, 0, -1).applyQuaternion(this.cameraWorldQuaternion);
this.cameraRight.set(1, 0, 0).applyQuaternion(this.cameraWorldQuaternion);
this.cameraRight.y = 0;
this.cameraRight.normalize();
this.moveDirection.set(0, 0, 0);
if (this.keys['KeyW'] || this.keys['ArrowUp']) { this.moveDirection.add(this.cameraDirection); }
if (this.keys['KeyS'] || this.keys['ArrowDown']) { this.moveDirection.sub(this.cameraDirection); }
if (this.keys['KeyA'] || this.keys['ArrowLeft']) { this.moveDirection.sub(this.cameraRight); }
if (this.keys['KeyD'] || this.keys['ArrowRight']) { this.moveDirection.add(this.cameraRight); }
if (Math.abs(this.leftThumbstickInput.y) > 0.1) { this.moveDirection.add(this.cameraDirection.clone().multiplyScalar(-this.leftThumbstickInput.y)); }
if (Math.abs(this.leftThumbstickInput.x) > 0.1) { this.moveDirection.add(this.cameraRight.clone().multiplyScalar(this.leftThumbstickInput.x)); }
const isInputting = this.moveDirection.lengthSq() > 0.0001;
if (isInputting) { this.moveDirection.normalize(); }
let lerpFactor = data.damping;
if (isInputting) {
this.desiredVelocity.copy(this.moveDirection).multiplyScalar(data.targetSpeed);
lerpFactor = data.acceleration;
} else {
this.desiredVelocity.set(0,0,0);
lerpFactor = data.damping;
}
const effectiveLerpFactor = 1.0 - Math.exp(-lerpFactor * dt);
this.currentVelocity.lerp(this.desiredVelocity, effectiveLerpFactor);
if (this.currentVelocity.lengthSq() > 0.0001) {
position.add(this.currentVelocity.clone().multiplyScalar(dt));
}
let verticalMovement = 0;
if (this.rigEl.sceneEl.is('vr-mode') && Math.abs(this.rightThumbstickInput.y) > 0.1) {
verticalMovement = this.rightThumbstickInput.y * data.verticalSpeed * dt;
}
const nextY = position.y + verticalMovement;
position.y = THREE.MathUtils.clamp(nextY, data.groundY, data.ceilingY);
},
onKeyDown: function (event) { if (['KeyW', 'ArrowUp', 'KeyS', 'ArrowDown', 'KeyA', 'ArrowLeft', 'KeyD', 'ArrowRight'].includes(event.code)) { this.keys[event.code] = true; } },
onKeyUp: function (event) { if (this.keys[event.code] !== undefined) { delete this.keys[event.code]; } }
});
</script>
</head>
<body>
<a-scene>
<a-entity
id="avatar"
vrm="src: ./vrm/tesA1_V0a.vrm"
vrm-anim
position="0 0 0"
rotation="0 180 0">
</a-entity>
<a-entity id="rig" position="0 0 2.5" camera-relative-controls="groundY: -1.5">
<a-entity id="player">
<a-entity id="camera" camera look-controls position="0 1.6 0"></a-entity>
<a-entity id="leftHand" oculus-touch-controls="hand: left"></a-entity>
<a-entity id="rightHand" oculus-touch-controls="hand: right"></a-entity>
</a-entity>
</a-entity>
<a-sky color="#ECECEC"></a-sky>
<a-plane position="0 0 0" rotation="-90 0 0" width="100" height="100" color="#FFFFFF" shadow></a-plane>
<a-light type="directional" color="#FFF" intensity="0.6" position="-1 2 2"></a-light>
<a-light type="ambient" color="#FFF" intensity="0.6"></a-light>
</a-scene>
<script>
// ========= 全ての標準ボーンを順番に動かすモーションデータ(完全版) =========
const allBonesSamplerMotion = {
// --- 0-4秒: 体幹 (Hips, Spine, Chest) ---
hips: { keys: [ { rot: [0, 0, 0], time: 0 }, { rot: [0, 10, 5], time: 1 }, { rot: [0, -10, -5], time: 3 }, { rot: [0, 0, 0], time: 43 } ] },
spine: { keys: [ { rot: [0, 0, 0], time: 0 }, { rot: [0, 20, 0], time: 1 }, { rot: [0, -20, 0], time: 3 }, { rot: [0, 0, 0], time: 43 } ] },
chest: { keys: [ { rot: [0, 0, 0], time: 0 }, { rot: [10, 0, 0], time: 1 }, { rot: [-10, 0, 0], time: 3 }, { rot: [0, 0, 0], time: 43 } ] },
// --- 4-9秒: 首と頭 (Neck, Head) ---
neck: { keys: [ { rot: [0, 0, 0], time: 4 }, { rot: [20, 0, 0], time: 5 }, { rot: [0, 0, 0], time: 6 }, { rot: [0, 0, 0], time: 43 } ] },
head: { keys: [ { rot: [0, 0, 0], time: 6 }, { rot: [0, 30, 0], time: 7 }, { rot: [0, -30, 0], time: 8 }, { rot: [0, 0, 0], time: 43 } ] },
// --- 9-15秒: 右腕 (Shoulder, UpperArm, LowerArm, Hand) ---
rightShoulder: { keys: [ { rot: [0, 0, 0], time: 9 }, { rot: [-10, 0, -20], time: 10 }, { rot: [0, 0, 0], time: 43 } ] },
rightUpperArm: { keys: [ { rot: [0, 0, 0], time: 9 }, { rot: [0, 0, -90], time: 11 }, { rot: [0, 0, 0], time: 43 } ] },
rightLowerArm: { keys: [ { rot: [0, 0, 0], time: 9 }, { rot: [-90, 0, 0], time: 12 }, { rot: [0, 0, 0], time: 43 } ] },
rightHand: { keys: [ { rot: [0, 0, 0], time: 9 }, { rot: [0, 0, 30], time: 13 }, { rot: [0, 0, -30], time: 14 }, { rot: [0, 0, 0], time: 43 } ] },
// --- 15-21秒: 左腕 (Shoulder, UpperArm, LowerArm, Hand) ---
leftShoulder: { keys: [ { rot: [0, 0, 0], time: 15 }, { rot: [-10, 0, 20], time: 16 }, { rot: [0, 0, 0], time: 43 } ] },
leftUpperArm: { keys: [ { rot: [0, 0, 0], time: 15 }, { rot: [0, 0, 90], time: 17 }, { rot: [0, 0, 0], time: 43 } ] },
leftLowerArm: { keys: [ { rot: [0, 0, 0], time: 15 }, { rot: [-90, 0, 0], time: 18 }, { rot: [0, 0, 0], time: 43 } ] },
leftHand: { keys: [ { rot: [0, 0, 0], time: 15 }, { rot: [0, 0, 30], time: 19 }, { rot: [0, 0, -30], time: 20 }, { rot: [0, 0, 0], time: 43 } ] },
// --- 21-27秒: 右脚 (UpperLeg, LowerLeg, Foot) ---
rightUpperLeg: { keys: [ { rot: [0, 0, 0], time: 21 }, { rot: [-90, 0, 0], time: 22 }, { rot: [0, 0, 0], time: 43 } ] },
rightLowerLeg: { keys: [ { rot: [0, 0, 0], time: 21 }, { rot: [90, 0, 0], time: 23 }, { rot: [0, 0, 0], time: 43 } ] },
rightFoot: { keys: [ { rot: [0, 0, 0], time: 21 }, { rot: [30, 0, 0], time: 24 }, { rot: [-30, 0, 0], time: 25 }, { rot: [0, 0, 0], time: 43 } ] },
// --- 27-33秒: 左脚 (UpperLeg, LowerLeg, Foot) ---
leftUpperLeg: { keys: [ { rot: [0, 0, 0], time: 27 }, { rot: [-90, 0, 0], time: 28 }, { rot: [0, 0, 0], time: 43 } ] },
leftLowerLeg: { keys: [ { rot: [0, 0, 0], time: 27 }, { rot: [90, 0, 0], time: 29 }, { rot: [0, 0, 0], time: 43 } ] },
leftFoot: { keys: [ { rot: [0, 0, 0], time: 27 }, { rot: [30, 0, 0], time: 30 }, { rot: [-30, 0, 0], time: 31 }, { rot: [0, 0, 0], time: 43 } ] },
// --- 33-38秒: 右手の指(グー、パー) ---
rightThumbProximal: { keys: [ { rot: [0,0,0], time: 33 }, { rot: [0, 0, -60], time: 34 }, { rot: [0, 0, 0], time: 43 } ] },
rightThumbIntermediate: { keys: [ { rot: [0,0,0], time: 33 }, { rot: [0, 0, -60], time: 34 }, { rot: [0, 0, 0], time: 43 } ] },
rightThumbDistal: { keys: [ { rot: [0,0,0], time: 33 }, { rot: [0, 0, -60], time: 34 }, { rot: [0, 0, 0], time: 43 } ] },
rightIndexProximal: { keys: [ { rot: [0,0,0], time: 33 }, { rot: [0, 0, -90], time: 34 }, { rot: [0, 0, 0], time: 43 } ] },
rightIndexIntermediate: { keys: [ { rot: [0,0,0], time: 33 }, { rot: [0, 0, -90], time: 34 }, { rot: [0, 0, 0], time: 43 } ] },
rightIndexDistal: { keys: [ { rot: [0,0,0], time: 33 }, { rot: [0, 0, -90], time: 34 }, { rot: [0, 0, 0], time: 43 } ] },
rightMiddleProximal: { keys: [ { rot: [0,0,0], time: 33 }, { rot: [0, 0, -90], time: 34 }, { rot: [0, 0, 0], time: 43 } ] },
rightMiddleIntermediate: { keys: [ { rot: [0,0,0], time: 33 }, { rot: [0, 0, -90], time: 34 }, { rot: [0, 0, 0], time: 43 } ] },
rightMiddleDistal: { keys: [ { rot: [0,0,0], time: 33 }, { rot: [0, 0, -90], time: 34 }, { rot: [0, 0, 0], time: 43 } ] },
rightRingProximal: { keys: [ { rot: [0,0,0], time: 33 }, { rot: [0, 0, -90], time: 34 }, { rot: [0, 0, 0], time: 43 } ] },
rightRingIntermediate: { keys: [ { rot: [0,0,0], time: 33 }, { rot: [0, 0, -90], time: 34 }, { rot: [0, 0, 0], time: 43 } ] },
rightRingDistal: { keys: [ { rot: [0,0,0], time: 33 }, { rot: [0, 0, -90], time: 34 }, { rot: [0, 0, 0], time: 43 } ] },
rightLittleProximal: { keys: [ { rot: [0,0,0], time: 33 }, { rot: [0, 0, -90], time: 34 }, { rot: [0, 0, 0], time: 43 } ] },
rightLittleIntermediate: { keys: [ { rot: [0,0,0], time: 33 }, { rot: [0, 0, -90], time: 34 }, { rot: [0, 0, 0], time: 43 } ] },
rightLittleDistal: { keys: [ { rot: [0,0,0], time: 33 }, { rot: [0, 0, -90], time: 34 }, { rot: [0, 0, 0], time: 43 } ] },
// --- 38-43秒: 左手の指(グー、パー) ---
leftThumbProximal: { keys: [ { rot: [0,0,0], time: 38 }, { rot: [0, 0, 60], time: 39 }, { rot: [0, 0, 0], time: 43 } ] },
leftThumbIntermediate: { keys: [ { rot: [0,0,0], time: 38 }, { rot: [0, 0, 60], time: 39 }, { rot: [0, 0, 0], time: 43 } ] },
leftThumbDistal: { keys: [ { rot: [0,0,0], time: 38 }, { rot: [0, 0, 60], time: 39 }, { rot: [0, 0, 0], time: 43 } ] },
leftIndexProximal: { keys: [ { rot: [0,0,0], time: 38 }, { rot: [0, 0, 90], time: 39 }, { rot: [0, 0, 0], time: 43 } ] },
leftIndexIntermediate: { keys: [ { rot: [0,0,0], time: 38 }, { rot: [0, 0, 90], time: 39 }, { rot: [0, 0, 0], time: 43 } ] },
leftIndexDistal: { keys: [ { rot: [0,0,0], time: 38 }, { rot: [0, 0, 90], time: 39 }, { rot: [0, 0, 0], time: 43 } ] },
leftMiddleProximal: { keys: [ { rot: [0,0,0], time: 38 }, { rot: [0, 0, 90], time: 39 }, { rot: [0, 0, 0], time: 43 } ] },
leftMiddleIntermediate: { keys: [ { rot: [0,0,0], time: 38 }, { rot: [0, 0, 90], time: 39 }, { rot: [0, 0, 0], time: 43 } ] },
leftMiddleDistal: { keys: [ { rot: [0,0,0], time: 38 }, { rot: [0, 0, 90], time: 39 }, { rot: [0, 0, 0], time: 43 } ] },
leftRingProximal: { keys: [ { rot: [0,0,0], time: 38 }, { rot: [0, 0, 90], time: 39 }, { rot: [0, 0, 0], time: 43 } ] },
leftRingIntermediate: { keys: [ { rot: [0,0,0], time: 38 }, { rot: [0, 0, 90], time: 39 }, { rot: [0, 0, 0], time: 43 } ] },
leftRingDistal: { keys: [ { rot: [0,0,0], time: 38 }, { rot: [0, 0, 90], time: 39 }, { rot: [0, 0, 0], time: 43 } ] },
leftLittleProximal: { keys: [ { rot: [0,0,0], time: 38 }, { rot: [0, 0, 90], time: 39 }, { rot: [0, 0, 0], time: 41 } ] },
leftLittleIntermediate: { keys: [ { rot: [0,0,0], time: 38 }, { rot: [0, 0, 90], time: 39 }, { rot: [0, 0, 0], time: 41 } ] },
leftLittleDistal: { keys: [ { rot: [0,0,0], time: 38 }, { rot: [0, 0, 90], time: 39 }, { rot: [0, 0, 0], time: 43 } ] },
};
document.addEventListener('DOMContentLoaded', () => {
const avatarEl = document.querySelector('#avatar');
avatarEl.setAttribute('vrm-anim', 'idleMotion', allBonesSamplerMotion);
});
</script>
</body>
</html>
使用変数
-------( Function ) | |
avatarEl | |
BonesSamplerMotion | |
cameraDirection | |
cameraEl | |
cameraObject | |
cameraRight | |
charset | |
color | |
controls | |
currentVelocity | |
data | |
desiredVelocity | |
dt | |
eftThumbstickInput | |
eraWorldQuaternion | |
ffectiveLerpFactor | |
ghtThumbstickInput | |
height | |
id | |
intensity | |
isInputting | |
keys | |
leftHand | |
lerpFactor | |
moveDirection | |
nextY | |
position | |
rigEl | |
rightHand | |
rotation | |
src | |
type | |
verticalMovement | |
vrm | |
width | |
x | |
y | |
番に動かすモーションデータ(完全版) |