a-farme-球に文字test14
<!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 }, 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); } if (this.moveDirection.lengthSq() > 0.0001) { this.moveDirection.normalize(); this.desiredVelocity.copy(this.moveDirection).multiplyScalar(data.targetSpeed); } else { this.desiredVelocity.copy(this.ZERO_VECTOR); } const lerpFactor = (this.moveDirection.lengthSq() > 0.0001) ? data.acceleration : 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: #ECECEC">
<a-entity id="rig" position="0 0 5"
camera-relative-controls="targetSpeed: 250; acceleration: 200; damping: 2">
<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 = 30; // 数は変えない
const spread = 2000; // ★★★ 範囲を 20 * 100 = 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');
// ★★★ 半径の計算式を変更 (範囲 2.0 〜 6.0) ★★★
const radius = Math.random() * 4.0 + 2.0;
// ★★★ 位置計算は新しい spread と radius を使う ★★★
const x = (Math.random() - 0.5) * spread;
const y = Math.random() * (spread / 2) + radius; // Y方向にも広がる
const z = (Math.random() - 0.5) * spread;
const color = `hsl(${Math.random() * 360}, 70%, 60%)`;
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>
使用変数
<!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 }, 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); } if (this.moveDirection.lengthSq() > 0.0001) { this.moveDirection.normalize(); this.desiredVelocity.copy(this.moveDirection).multiplyScalar(data.targetSpeed); } else { this.desiredVelocity.copy(this.ZERO_VECTOR); } const lerpFactor = (this.moveDirection.lengthSq() > 0.0001) ? data.acceleration : 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: #ECECEC">
<a-entity id="rig" position="0 0 5"
camera-relative-controls="targetSpeed: 250; acceleration: 200; damping: 2">
<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 = 30; // 数は変えない
const spread = 2000; // ★★★ 範囲を 20 * 100 = 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');
// ★★★ 半径の計算式を変更 (範囲 2.0 〜 6.0) ★★★
const radius = Math.random() * 4.0 + 2.0;
// ★★★ 位置計算は新しい spread と radius を使う ★★★
const x = (Math.random() - 0.5) * spread;
const y = Math.random() * (spread / 2) + radius; // Y方向にも広がる
const z = (Math.random() - 0.5) * spread;
const color = `hsl(${Math.random() * 360}, 70%, 60%)`;
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 ) | |
100 | |
ameraWorldPosition | |
argetWorldPosition | |
at | |
attribute | |
background | |
begin | |
camera | |
cameraEl | |
clickedSphere | |
color | |
controls | |
currentVelocity | |
cursor | |
data | |
deltaPosition | |
desiredVelocity | |
direction | |
dt | |
dur | |
easing | |
el | |
eraWorldQuaternion | |
ffectiveLerpFactor | |
fill | |
from | |
geometry | |
handleSphereClick -------( Function ) | |
height | |
i | |
id | |
infoPanelEl | |
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 |