Junkerposts
 a-farme-球に文字test18 

<!DOCTYPE html>
<html>
<head>
<title>A-Frame - 黒背景とパステル球</title> <script src="https://aframe.io/releases/1.7.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-look-at-component@0.8.0/dist/aframe-look-at-component.min.js"></script>
<script src="https://unpkg.com/aframe-troika-text/dist/aframe-troika-text.min.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 } },
init: function () { this.keys = {}; this.currentVelocity = new THREE.Vector3(); this.ZERO_VECTOR = new THREE.Vector3(); this.direction = new THREE.Vector3(); this.rightDirection = new THREE.Vector3(); this.moveDirection = new THREE.Vector3(); this.desiredVelocity = new THREE.Vector3(); this.cameraWorldQuaternion = new THREE.Quaternion(); this.cameraEl = this.el.querySelector('[camera]'); this.isReady = false; if (!this.cameraEl) { console.error('camera-relative-controls requires a child entity with the [camera] component.'); } this.onKeyDown = this.onKeyDown.bind(this); this.onKeyUp = this.onKeyUp.bind(this); window.addEventListener('keydown', this.onKeyDown); window.addEventListener('keyup', this.onKeyUp); },
remove: function () { window.removeEventListener('keydown', this.onKeyDown); window.removeEventListener('keyup', this.onKeyUp); },
tick: function (time, timeDelta) { if (!this.data.enabled) return; if (!this.isReady) { if (this.cameraEl && this.cameraEl.object3D && this.cameraEl.object3D.matrixWorld) { this.isReady = true; } else { if (!this.cameraEl) { this.cameraEl = this.el.querySelector('[camera]'); } return; } } if (!this.cameraEl || !this.cameraEl.object3D) { return; } const el = this.el; const data = this.data; const position = el.object3D.position; const dt = timeDelta / 1000; this.cameraEl.object3D.getWorldQuaternion(this.cameraWorldQuaternion); this.direction.set(0, 0, -1); this.direction.applyQuaternion(this.cameraWorldQuaternion); if (this.direction.lengthSq() > 0.0001) this.direction.normalize(); this.rightDirection.set(1, 0, 0); this.rightDirection.applyQuaternion(this.cameraWorldQuaternion); this.rightDirection.y = 0; if (this.rightDirection.lengthSq() > 0.0001) this.rightDirection.normalize(); this.moveDirection.set(0, 0, 0); if (this.keys['KeyW'] || this.keys['ArrowUp']) { this.moveDirection.add(this.direction); } if (this.keys['KeyS'] || this.keys['ArrowDown']) { this.moveDirection.sub(this.direction); } if (this.keys['KeyA'] || this.keys['ArrowLeft']) { this.moveDirection.sub(this.rightDirection); } if (this.keys['KeyD'] || this.keys['ArrowRight']) { this.moveDirection.add(this.rightDirection); } const isKeyPressed = this.moveDirection.lengthSq() > 0.0001; if (isKeyPressed) { this.moveDirection.normalize(); } let lerpFactor = data.damping; const isMoving = this.currentVelocity.lengthSq() > 0.01; if (isKeyPressed) { let isOpposing = false; if (isMoving) { const dot = this.currentVelocity.dot(this.moveDirection); if (dot < -0.1) { isOpposing = true; } } if (isOpposing) { this.desiredVelocity.copy(this.ZERO_VECTOR); lerpFactor = data.brakingDeceleration; } else { this.desiredVelocity.copy(this.moveDirection).multiplyScalar(data.targetSpeed); lerpFactor = data.acceleration; } } else { this.desiredVelocity.copy(this.ZERO_VECTOR); lerpFactor = data.damping; } const effectiveLerpFactor = 1.0 - Math.exp(-lerpFactor * dt); this.currentVelocity.lerp(this.desiredVelocity, effectiveLerpFactor); if (this.currentVelocity.lengthSq() < 0.0001) { this.currentVelocity.copy(this.ZERO_VECTOR); } if (this.currentVelocity.lengthSq() > 0) { const deltaPosition = this.currentVelocity.clone().multiplyScalar(dt); position.add(deltaPosition); } },
onKeyDown: function (event) { if (!this.data.enabled) { return; } 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 id="myScene" background="color: #000000">

<a-entity id="rig" position="0 0 5"
camera-relative-controls="targetSpeed: 250; acceleration: 3; damping: 5; brakingDeceleration: 1;">
<a-entity id="camera" camera="far: 20000;" look-controls position="0 1.6 0">
<a-entity cursor="rayOrigin: mouse; fuse: false;" raycaster="objects:.clickableSphere; far: 3000;" position="0 0 -1" geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03;" material="color: black; shader: flat; opacity: 0.7"> <a-animation begin="click" easing="ease-in" attribute="scale" fill="backwards" from="0.1 0.1 0.1" to="1 1 1" dur="150"></a-animation> </a-entity>
</a-entity>
</a-entity>

<a-entity light="type: ambient; color: #888"></a-entity>
<a-entity light="type: directional; color: #FFF; intensity: 0.8" position="-1 1.5 1"></a-entity>

<a-entity id="infoPanel" visible="false" position="0 -1000 0" look-at="[camera]">
<a-plane id="panelBackground" width="2.5" height="1.2" color="#333" opacity="0.9" side="double"></a-plane>
<a-entity id="panelText"
troika-text="value: Placeholder; color: white; fontSize: 0.08; maxWidth: 2.3; align: left; anchorX: center; anchorY: middle; baseline: middle;"
position="0 0 0.05">
</a-entity>
</a-entity>

<script>
// --- 球体生成などのスクリプト (変更なし) ---
const sceneEl = document.getElementById('myScene');
const infoPanelEl = document.getElementById('infoPanel');
const panelTextEl = document.getElementById('panelText');
const cameraEl = document.getElementById('camera');
const numSpheres = 60;
const spread = 2000;
const targetWorldPosition = new THREE.Vector3();
const cameraWorldPosition = new THREE.Vector3();
const direction = new THREE.Vector3();
const panelPosition = new THREE.Vector3();
for (let i = 0; i < numSpheres; i++) {
const sphereEl = document.createElement('a-sphere');
const radius = Math.random() * 4.0 + 2.0;
const x = (Math.random() - 0.5) * spread;
const y = Math.random() * (spread / 2) + radius;
const z = (Math.random() - 0.5) * spread;
const color = `hsl(${Math.random() * 360}, 50%, 75%)`;
sphereEl.setAttribute('radius', radius);
sphereEl.setAttribute('color', color);
sphereEl.setAttribute('position', { x: x, y: y, z: z });
sphereEl.classList.add('clickableSphere');
sphereEl.setAttribute('data-info', `球 ${i + 1}\n色: ${color}\n半径: ${radius.toFixed(2)}`);
sphereEl.addEventListener('click', handleSphereClick);
sceneEl.appendChild(sphereEl);
}
function handleSphereClick(event) {
const clickedSphere = event.target;
const sphereInfo = clickedSphere.dataset.info;
clickedSphere.object3D.getWorldPosition(targetWorldPosition);
cameraEl.object3D.getWorldPosition(cameraWorldPosition);
const sphereRadius = clickedSphere.getAttribute('geometry').radius;
const offsetDistance = sphereRadius + 0.5;
direction.subVectors(cameraWorldPosition, targetWorldPosition).normalize();
panelPosition.copy(targetWorldPosition).addScaledVector(direction, offsetDistance);
infoPanelEl.object3D.position.copy(panelPosition);
panelTextEl.setAttribute('troika-text', 'value', sphereInfo);
infoPanelEl.setAttribute('visible', true);
}
</script>
</a-scene>
</body>
</html>


使用変数

-------( Function )
ameraWorldPosition
argetWorldPosition
at
attribute
background
begin
camera
cameraEl
clickedSphere
color
controls
currentVelocity
cursor
data
deltaPosition
desiredVelocity
direction
dot
dt
dur
easing
el
eraWorldQuaternion
ffectiveLerpFactor
fill
from
geometry
handleSphereClick -------( Function )
height
i
id
infoPanelEl
isKeyPressed
isMoving
isOpposing
isReady
keys
lerpFactor
light
material
moveDirection
numSpheres
offsetDistance
onKeyDown
onKeyUp
opacity
panelPosition
panelTextEl
position
radius
raycaster
rightDirection
sceneEl
side
sphereEl
sphereInfo
sphereRadius
spread
src
text
to
visible
width
x
y
z
ZERO_VECTOR