<!DOCTYPE html>
<html>
<head>
<title>A-Frame Physics (AR/VR Selectable) - V2A+Movement V1.001</title>
<meta charset="utf-8">
<style>
#custom-buttons {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 10;
}
#custom-buttons button {
padding: 12px 20px;
background-color: #007AFF;
color: white;
border-radius: 8px;
border: none;
font-size: 16px;
font-weight: bold;
cursor: pointer;
margin-left: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: background-color 0.2s;
}
#custom-buttons button:hover {
background-color: #0056b3;
}
</style>
<script src="https://aframe.io/releases/1.5.0/aframe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/ammo-builds@1.0.0/ammo.wasm.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@c-frame/aframe-physics-system@4.2.2/dist/aframe-physics-system.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/c-frame/aframe-extras@7.2.0/dist/aframe-extras.min.js"></script>
<script>
AFRAME.registerComponent('laser-grab', {
init: function () {
this.grabbedEl = null;
this.sceneEl = document.querySelector('a-scene');
this.onTriggerDown = this.onTriggerDown.bind(this);
this.onTriggerUp = this.onTriggerUp.bind(this);
this.el.addEventListener('triggerdown', this.onTriggerDown);
this.el.addEventListener('triggerup', this.onTriggerUp);
},
remove: function () {
this.el.removeEventListener('triggerdown', this.onTriggerDown);
this.el.removeEventListener('triggerup', this.onTriggerUp);
},
onTriggerDown: function () {
// 既に何かを掴んでいたら何もしない
if (this.grabbedEl) { return; }
// Raycasterで指しているオブジェクトを取得
const intersectedEls = this.el.components.raycaster.intersectedEls;
if (!intersectedEls.length) { return; }
const targetEl = intersectedEls[0];
// 掴めるオブジェクトなら掴む処理を開始
if (targetEl.classList.contains('grabbable')) {
this.grabbedEl = targetEl;
// 物理挙動を一時的に無効化
this.grabbedEl.removeAttribute('dynamic-body');
// コントローラーの子要素にする
this.el.object3D.attach(this.grabbedEl.object3D);
}
},
onTriggerUp: function () {
// 何も掴んでいなければ何もしない
if (!this.grabbedEl) { return; }
// ワールド(シーン)にオブジェクトを戻す
this.sceneEl.object3D.attach(this.grabbedEl.object3D);
// 物理挙動を再度有効化
this.grabbedEl.setAttribute('dynamic-body', '');
// 掴んでいるオブジェクト情報をクリア
this.grabbedEl = null;
}
});
</script>
</head>
<body>
<a-scene
id="my-scene"
vr-mode-ui="enabled: false"
webxr="optionalFeatures: local-floor, bounded-floor, hit-test;"
renderer="colorManagement: true;"
physics="driver: ammo; debug: false;">
<a-assets>
<img id="woodTexture" src="https://cdn.glitch.global/6d445c79-374c-4713-a035-7925a69700a8/wood.jpg?v=1680197921935">
</a-assets>
<a-sky id="sky" color="#E2F0FF"></a-sky>
<a-entity id="world" position="0 0 -3">
<a-light type="ambient" color="#FFF" intensity="0.7"></a-light>
<a-light type="directional" position="0 5 2" intensity="0.8"></a-light>
<a-plane id="ground" position="0 0 0" rotation="-90 0 0" width="30" height="30" static-body color="#6B8E23"></a-plane>
<a-entity id="table">
<a-box position="0 1 0" width="2" height="0.1" depth="1.2" static-body material="src: #woodTexture"></a-box>
<a-box position="-0.9 0.5 -0.5" width="0.1" height="1" depth="0.1" static-body material="src: #woodTexture"></a-box>
<a-box position="0.9 0.5 -0.5" width="0.1" height="1" depth="0.1" static-body material="src: #woodTexture"></a-box>
<a-box position="-0.9 0.5 0.5" width="0.1" height="1" depth="0.1" static-body material="src: #woodTexture"></a-box>
<a-box position="0.9 0.5 0.5" width="0.1" height="1" depth="0.1" static-body material="src: #woodTexture"></a-box>
</a-entity>
<a-box class="grabbable" position="-0.3 1.15 0" width="0.4" height="0.4" depth="0.4" dynamic-body color="tomato" ></a-box>
<a-box class="grabbable" position="0.1 1.15 -0.2" width="0.4" height="0.4" depth="0.4" dynamic-body color="skyblue"></a-box>
<a-box class="grabbable" position="0.4 1.15 0.1" width="0.4" height="0.4" depth="0.4" dynamic-body color="gold"></a-box>
<a-box class="grabbable" position="0 1.55 -0.1" rotation="30 45 15" width="0.4" height="0.4" depth="0.4" dynamic-body color="#4CC3D9"></a-box>
</a-entity>
<a-entity id="rig" movement-controls>
<a-entity camera position="0 1.6 0" look-controls></a-entity>
<a-entity oculus-touch-controls="hand: left" super-hands></a-entity>
<a-entity oculus-touch-controls="hand: right"
laser-controls="hand: right"
raycaster="objects: .grabbable"
laser-grab>
</a-entity>
</a-entity>
</a-scene>
<div id="custom-buttons">
<button id="enter-ar-btn">ARで体験 (Passthrough)</button>
<button id="enter-vr-btn">VRで体験</button>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const sceneEl = document.querySelector('#my-scene');
const skyEl = document.querySelector('#sky');
const groundEl = document.querySelector('#ground');
// ARボタンがクリックされた時の処理
document.querySelector('#enter-ar-btn').addEventListener('click', () => {
skyEl.setAttribute('visible', 'false');
groundEl.setAttribute('material', 'visible', false);
sceneEl.enterVR(); // ARモードを開始
});
// VRボタンがクリックされた時の処理
document.querySelector('#enter-vr-btn').addEventListener('click', () => {
skyEl.setAttribute('visible', 'true');
groundEl.setAttribute('material', 'visible', true);
sceneEl.enterVR(); // VRモードを開始
});
});
</script>
</body>
</html>
使用変数
-------( Function ) | |
charset | |
class | |
color | |
controls | |
depth | |
grabbedEl | |
groundEl | |
height | |
id | |
intensity | |
intersectedEls | |
material | |
onTriggerDown | |
onTriggerUp | |
physics | |
position | |
raycaster | |
renderer | |
rotation | |
sceneEl | |
skyEl | |
src | |
targetEl | |
type | |
ui | |
v | |
webxr | |
width |