junkerstock
 vrq-test2Am1.001 

<!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