junkerstock
 vrb-test7 

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Babylon.js - Interactive Spheres Scene (v7 - Fine-tuned Controls)</title>
<script src="https://preview.babylonjs.com/babylon.js"></script>
<style>
html, body {
overflow: hidden;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#renderCanvas {
width: 100%;
height: 100%;
touch-action: none;
}
</style>
</head>
<body>
<canvas id="renderCanvas"></canvas>

<script>
window.addEventListener('DOMContentLoaded', function() {
const canvas = document.getElementById('renderCanvas');
const engine = new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true });

const createScene = async function() {
const scene = new BABYLON.Scene(engine);
scene.clearColor = new BABYLON.Color3.FromHexString("#ECECEC");

const pcCamera = new BABYLON.UniversalCamera("pcCamera", new BABYLON.Vector3(0, 1.6, 5), scene);
pcCamera.setTarget(BABYLON.Vector3.Zero());
pcCamera.attachControl(canvas, true);
pcCamera.speed = 0.15;
pcCamera.keysUp = [87, 38]; pcCamera.keysDown = [83, 40];
pcCamera.keysLeft = [65, 37]; pcCamera.keysRight = [68, 39];
pcCamera.angularSensibility = 2000;

const ambientLight = new BABYLON.HemisphericLight("ambientLight", new BABYLON.Vector3(0, 1, 0), scene);
ambientLight.intensity = 0.8;
const directionalLight = new BABYLON.DirectionalLight("dirLight", new BABYLON.Vector3(-1, 1.5, 1), scene);
directionalLight.intensity = 0.8;

const panelWidth = 2.5; const panelHeight = 1.2; const infoPanel = BABYLON.MeshBuilder.CreatePlane("infoPanel", { width: panelWidth, height: panelHeight }, scene); infoPanel.material = new BABYLON.StandardMaterial("panelMat", scene); infoPanel.material.backFaceCulling = false; infoPanel.billboardMode = BABYLON.Mesh.BILLBOARDMODE_ALL; infoPanel.isVisible = false; const textureResolution = 512; const panelTexture = new BABYLON.DynamicTexture("panelTexture", { width: textureResolution, height: Math.round(textureResolution * (panelHeight / panelWidth))}, scene, true); infoPanel.material.diffuseTexture = panelTexture; infoPanel.material.emissiveColor = new BABYLON.Color3(0.2, 0.2, 0.2); function updatePanelText(text) { const ctx = panelTexture.getContext(); const size = panelTexture.getSize(); const fontSize = 28; const font = `bold ${fontSize}px sans-serif`; const lines = text.split('\n'); ctx.fillStyle = "#333333"; ctx.fillRect(0, 0, size.width, size.height); ctx.font = font; ctx.fillStyle = "white"; lines.forEach((line, index) => { const y = 50 + index * (fontSize + 15); ctx.fillText(line, 25, y); }); panelTexture.update(); } const numSpheres = 30; const spread = 20; for (let i = 0; i < numSpheres; i++) { const radius = Math.random() * 0.4 + 0.2; const sphere = BABYLON.MeshBuilder.CreateSphere(`sphere${i}`, { diameter: radius * 2 }, scene); const x = (Math.random() - 0.5) * spread; const y = Math.random() * (spread / 2) + radius; const z = (Math.random() - 0.5) * spread; sphere.position = new BABYLON.Vector3(x, y, z); sphere.material = new BABYLON.StandardMaterial(`mat${i}`, scene); const hue = Math.random() * 360; const saturation = 0.7; const value = 0.95; sphere.material.diffuseColor = BABYLON.Color3.FromHSV(hue, saturation, value); sphere.metadata = { info: `球 ${i + 1}\n色: HSV(${Math.round(hue)}, ${Math.round(saturation*100)}%, ${Math.round(value*100)}%)\n半径: ${radius.toFixed(2)}`};} scene.onPointerDown = function (evt, pickResult) { if (pickResult.hit && pickResult.pickedMesh && pickResult.pickedMesh.metadata) { const clickedSphere = pickResult.pickedMesh; const targetWorldPosition = clickedSphere.getWorldMatrix().getTranslation(); const cameraWorldPosition = scene.activeCamera.globalPosition; const sphereRadius = clickedSphere.getBoundingInfo().boundingSphere.radius; const offsetDistance = sphereRadius + 0.8; const direction = cameraWorldPosition.subtract(targetWorldPosition).normalize(); const panelPosition = targetWorldPosition.add(direction.scale(offsetDistance)); infoPanel.position.copyFrom(panelPosition); updatePanelText(clickedSphere.metadata.info); infoPanel.isVisible = true; } else { infoPanel.isVisible = false; } };

const xr = await scene.createDefaultXRExperienceAsync({});

xr.baseExperience.onStateChangedObservable.add((state) => {
if (state === BABYLON.WebXRState.IN_XR) {
pcCamera.detachControl();
} else if (state === BABYLON.WebXRState.NOT_IN_XR) {
pcCamera.attachControl(canvas, true);
}
});

scene.onBeforeRenderObservable.add(() => {
if (xr.baseExperience.state !== BABYLON.WebXRState.IN_XR) return;

const deltaMillis = engine.getDeltaTime();
const xrCamera = xr.baseExperience.camera;

const leftController = xr.input.controllers.find(c => c.inputSource.handedness === 'left');
if (leftController?.motionController) {
const thumbstick = leftController.motionController.getComponent("xr-standard-thumbstick");
if (thumbstick?.axes) {
// ★★★ 修正点1: 移動速度を調整 (2.0 -> 1.2) ★★★
const moveSpeed = 1.2 * deltaMillis / 1000;

const forward = xrCamera.getDirection(BABYLON.Vector3.Forward());
forward.y = 0;
xrCamera.position.addInPlace(forward.normalize().scale(-thumbstick.axes.y * moveSpeed));

const right = xrCamera.getDirection(BABYLON.Vector3.Right());
xrCamera.position.addInPlace(right.scale(thumbstick.axes.x * moveSpeed));
}
}

const rightController = xr.input.controllers.find(c => c.inputSource.handedness === 'right');
if (rightController?.motionController) {
const thumbstick = rightController.motionController.getComponent("xr-standard-thumbstick");
if (thumbstick?.axes) {
const- rotationThreshold = 0.2;

if (Math.abs(thumbstick.axes.x) > rotationThreshold) {
// ★★★ 修正点2: 回転速度を調整 (0.7 -> 0.4) ★★★
const rotSpeed = 0.4 * deltaMillis / 1000;
if (xrCamera.parent) {
xrCamera.parent.rotate(BABYLON.Vector3.Up(), thumbstick.axes.x * rotSpeed, BABYLON.Space.WORLD);
}
}

if (Math.abs(thumbstick.axes.y) > rotationThreshold) {
const verticalSpeed = 2.0 * deltaMillis / 1000;
xrCamera.position.y += -thumbstick.axes.y * verticalSpeed;
}
}
}
});

return scene;
};

createScene().then(scene => {
engine.runRenderLoop(() => scene.render());
window.addEventListener('resize', () => engine.resize());
});
});
</script>
</body>
</html>


使用変数

) { const scene = new BABYLON.Scene -------( Function )
) { const canvas = document.getElementById -------( Function )
ambientLight
ameraWorldPosition
angularSensibility
argetWorldPosition
backFaceCulling
billboardMode
c
canvas
charset
clearColor
clickedSphere
createScene
ctx
deltaMillis
diffuseColor
diffuseTexture
direction
directionalLight
emissiveColor
engine
fillStyle
font
fontSize
forward
handedness
hue
i
id
infoPanel
intensity
isVisible
keysDown
keysLeft
keysRight
keysUp
leftController
lines
material
metadata
moveSpeed
numSpheres
offsetDistance
onPointerDown
panelHeight
panelPosition
panelTexture
panelWidth
pcCamera
position
radius
right
rightController
rotationThreshold
rotSpeed
saturation
scene
size
speed
sphere
sphereRadius
spread
src
state
textureResolution
thumbstick
updatePanelText -------( Function )
value
verticalSpeed
x
xr
xrCamera
y
z