Junkerposts
 a-farme-球に文字test32 

<!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];
}
}
});
// --- ここまでカメラ移動コンポーネント ---

// --- ランダム回転コンポーネント (変更なし) ---
AFRAME.registerComponent('random-rotate', {
schema: { maxSpeed: { type: 'number', default: 5 } },
init: function () { this.axis = new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).normalize(); const speed = ((Math.random() * 0.8) + 0.2) * this.data.maxSpeed * (Math.random() < 0.5 ? 1 : -1); this.speedRad = THREE.MathUtils.degToRad(speed); this.deltaRotationQuaternion = new THREE.Quaternion(); },
tick: function (time, timeDelta) { const dt = timeDelta / 1000; const angle = this.speedRad * dt; this.deltaRotationQuaternion.setFromAxisAngle(this.axis, angle); this.el.object3D.quaternion.multiplyQuaternions(this.deltaRotationQuaternion, this.el.object3D.quaternion); }
});
// --- ここまでランダム回転コンポーネント ---
</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: .clickableObject, .clickableButton; 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: #444"></a-entity>
<a-entity light="type: directional; color: #FFF; intensity: 0.8" position="-1 1.5 1"></a-entity>
<a-entity light="type: directional; color: #AAA; intensity: 0.4" position="1 1 -1"></a-entity>

<a-entity id="infoPanel" visible="false" position="0 -1000 0" look-at="[camera]">
<a-plane id="panelBackground" width="50" height="24" color="#333" opacity="0.9" side="double"></a-plane>
<a-entity id="panelText"
troika-text="value: Placeholder; color: white; fontSize: 0.72; maxWidth: 46; align: center; anchorX: center; anchorY: middle; baseline: middle;"
position="0 0 0.05">
</a-entity>
<a-triangle id="prevButton" class="clickableButton"
position="-20.0 0 0.01" rotation="0 0 90" scale="2.0 2.0 2.0"
material="color: #CCC; shader: flat; opacity: 0.8;">
</a-triangle>
<a-triangle id="nextButton" class="clickableButton"
position="20.0 0 0.01" rotation="0 0 -90" scale="2.0 2.0 2.0"
material="color: #CCC; shader: flat; opacity: 0.8;">
</a-triangle>
<a-circle id="closeButton" class="clickableButton"
position="24 11 0.05" radius="1.5" color="red" shader="flat">
</a-circle>
</a-entity>

<script>
const sceneEl = document.getElementById('myScene');
const infoPanelEl = document.getElementById('infoPanel');
const panelTextEl = document.getElementById('panelText');
const cameraEl = document.getElementById('camera');
const prevButtonEl = document.getElementById('prevButton');
const nextButtonEl = document.getElementById('nextButton');
const closeButtonEl = document.getElementById('closeButton');

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();

const PAGES = ['index', 'color', 'size'];
const TOTAL_PAGES = PAGES.length;

// 立方体生成ループ (変更なし)
for (let i = 0; i < numSpheres; i++) { const cubeEl = document.createElement('a-box'); const side = Math.random() * 10.0 + 0.5; const x = (Math.random() - 0.5) * spread; const y = Math.random() * (spread / 2) + side / 2; const z = (Math.random() - 0.5) * spread; const color = `hsl(${Math.random() * 360}, 50%, 75%)`; const sphereIndex = i + 1; cubeEl.setAttribute('width', side); cubeEl.setAttribute('height', side); cubeEl.setAttribute('depth', side); cubeEl.setAttribute('color', color); cubeEl.setAttribute('position', { x: x, y: y, z: z }); cubeEl.classList.add('clickableObject'); cubeEl.dataset.sphereIndex = sphereIndex; cubeEl.dataset.color = color; cubeEl.dataset.size = side.toFixed(2); cubeEl.setAttribute('random-rotate', {maxSpeed: 5}); cubeEl.addEventListener('click', handleSphereClick); sceneEl.appendChild(cubeEl); }

// パネル表示更新関数 (変更なし)
function updatePanelDisplay() { if (!infoPanelEl.dataset.sphereIndex) return; const index = parseInt(infoPanelEl.dataset.sphereIndex || '0', 10); const color = infoPanelEl.dataset.color || 'N/A'; const size = infoPanelEl.dataset.size || 'N/A'; const pageIndex = parseInt(infoPanelEl.dataset.currentPageIndex || '0', 10); let displayText = ''; const pageType = PAGES[pageIndex]; if (pageType === 'index') { displayText = `立方体: ${index}`; } else if (pageType === 'color') { displayText = `色: ${color}`; } else if (pageType === 'size') { displayText = `サイズ: ${size}`; } const pageIndicator = `(${pageIndex + 1}/${TOTAL_PAGES})`; const finalDisplayText = `${pageIndicator}\n${displayText}`; panelTextEl.setAttribute('troika-text', 'value', finalDisplayText); }

// handleSphereClick (変更なし)
function handleSphereClick(event) { event.stopPropagation(); console.log("--- handleSphereClick triggered --- Target:", event.target.id || event.target.tagName); const clickedCube = event.target; if (!clickedCube.dataset.sphereIndex || !clickedCube.dataset.color || !clickedCube.dataset.size) { console.error("Cube data missing from dataset!", clickedCube.dataset); return; } console.log("Cube data found:", clickedCube.dataset); infoPanelEl.dataset.sphereIndex = clickedCube.dataset.sphereIndex; infoPanelEl.dataset.color = clickedCube.dataset.color; infoPanelEl.dataset.size = clickedCube.dataset.size; infoPanelEl.dataset.currentPageIndex = '0'; console.log("Data stored in panel dataset."); try { updatePanelDisplay(); console.log("updatePanelDisplay completed."); } catch (e) { console.error("Error during updatePanelDisplay:", e); return; } try { clickedCube.object3D.getWorldPosition(targetWorldPosition); cameraEl.object3D.getWorldPosition(cameraWorldPosition); const cubeSide = parseFloat(clickedCube.dataset.size || 0); const offsetDistance = cubeSide / 2 + 0.5; direction.subVectors(cameraWorldPosition, targetWorldPosition).normalize(); panelPosition.copy(targetWorldPosition).addScaledVector(direction, offsetDistance); infoPanelEl.object3D.position.copy(panelPosition); console.log("Panel position calculated and applied."); } catch(e) { console.error("Error during position calculation:", e); return; } infoPanelEl.setAttribute('visible', true); console.log("Panel visibility set to true. --- handleSphereClick end ---"); }

// {タンクリック時の処理 (変更なし)
prevButtonEl.addEventListener('click', function (event) { event.stopPropagation(); if (!infoPanelEl.getAttribute('visible')) return; let pageIndex = parseInt(infoPanelEl.dataset.currentPageIndex || '0', 10); pageIndex = (pageIndex - 1 + TOTAL_PAGES) % TOTAL_PAGES; infoPanelEl.dataset.currentPageIndex = pageIndex.toString(); updatePanelDisplay(); console.log("Prev button clicked, page index:", pageIndex); });
nextButtonEl.addEventListener('click', function (event) { event.stopPropagation(); if (!infoPanelEl.getAttribute('visible')) return; let pageIndex = parseInt(infoPanelEl.dataset.currentPageIndex || '0', 10); pageIndex = (pageIndex + 1) % TOTAL_PAGES; infoPanelEl.dataset.currentPageIndex = pageIndex.toString(); updatePanelDisplay(); console.log("Next button clicked, page index:", pageIndex); });
closeButtonEl.addEventListener('click', function (event) { event.stopPropagation(); infoPanelEl.setAttribute('visible', false); delete infoPanelEl.dataset.sphereIndex; delete infoPanelEl.dataset.color; delete infoPanelEl.dataset.size; delete infoPanelEl.dataset.currentPageIndex; console.log("Close button clicked, panel hidden."); });

// 背景クリックリスナーは削除済み

</script>
</a-scene>
</body>
</html>


使用変数

-------( Function )
ameraWorldPosition
angle
argetWorldPosition
at
attribute
axis
background
begin
camera
cameraEl
class
clickedCube
closeButtonEl
color
controls
cubeEl
cubeSide
currentPageIndex
currentVelocity
cursor
data
deltaPosition
desiredVelocity
direction
displayText
dot
dt
dur
easing
el
eraWorldQuaternion
ffectiveLerpFactor
fill
finalDisplayText
from
geometry
handleSphereClick -------( Function )
height
i
id
index
infoPanelEl
isKeyPressed
isMoving
isOpposing
isReady
keys
lerpFactor
light
material
moveDirection
nextButtonEl
numSpheres
offsetDistance
onKeyDown
onKeyUp
opacity
pageIndex
pageIndicator
PAGES
pageType
panelPosition
panelTextEl
position
prevButtonEl
radius
raycaster
rightDirection
rotation
RotationQuaternion
scale
sceneEl
shader
side
size
speed
speedRad
sphereIndex
spread
src
text
to
TOTAL_PAGES
updatePanelDisplay -------( Function )
visible
width
x
y
z
ZERO_VECTOR