import React, { useRef, useMemo } from 'react'; import { useFrame } from '@react-three/fiber'; import * as THREE from 'three'; // --- Galaxy Effect (skill) --- const GALAXY_COUNT = 600; const ARM_COUNT = 4; const MAX_RADIUS = 0.6; const GALAXY_PRESETS = { sun: { coreColor: [1.0, 0.8, 0.3], armColor: [1.0, 0.5, 0.1], tipColor: [1.0, 1.0, 0.7], lightColor: '#ffaa22', lightIntensity: 1.5, rotateSpeed: 0.8, spread: 1.0, }, }; function buildGalaxy(preset) { const positions = new Float32Array(GALAXY_COUNT * 3); const colors = new Float32Array(GALAXY_COUNT * 3); const randoms = new Float32Array(GALAXY_COUNT); for (let i = 0; i < GALAXY_COUNT; i++) { const i3 = i * 3; const arm = i % ARM_COUNT; const armAngle = (arm / ARM_COUNT) * Math.PI * 2; const t = Math.random(); const r = t * MAX_RADIUS; const twist = t * Math.PI * 2.5; const angle = armAngle + twist; const scatter = (1 - t * 0.5) * 0.08 * preset.spread; positions[i3] = Math.cos(angle) * r + (Math.random() - 0.5) * scatter; positions[i3 + 1] = (Math.random() - 0.5) * 0.04 * (1 - t * 0.5); positions[i3 + 2] = Math.sin(angle) * r + (Math.random() - 0.5) * scatter; const core = preset.coreColor; const arm_ = preset.armColor; const tip = preset.tipColor; const c = t < 0.3 ? core.map((v, j) => v + (arm_[j] - v) * (t / 0.3)) : arm_.map((v, j) => v + (tip[j] - v) * ((t - 0.3) / 0.7)); colors[i3] = c[0]; colors[i3 + 1] = c[1]; colors[i3 + 2] = c[2]; randoms[i] = Math.random(); } return { positions, colors, randoms }; } function GalaxyEffect({ position = [0, 1.0, 0], scale = 1 }) { const preset = GALAXY_PRESETS.sun; const groupRef = useRef(); const pointsRef = useRef(); const { positions, colors, randoms } = useMemo(() => buildGalaxy(preset), []); const basePositions = useRef(positions); useFrame(({ clock }) => { if (!groupRef.current || !pointsRef.current) return; const t = clock.getElapsedTime(); groupRef.current.rotation.y = t * preset.rotateSpeed; groupRef.current.rotation.x = Math.sin(t * 0.3) * 0.15; const pulse = 1 + Math.sin(t * 2.0) * 0.1; groupRef.current.scale.setScalar(scale * pulse); const pos = pointsRef.current.geometry.attributes.position.array; const base = basePositions.current; for (let i = 0; i < GALAXY_COUNT; i++) { const i3 = i * 3; const drift = Math.sin(t * 1.5 + randoms[i] * 10) * 0.02; pos[i3] = base[i3] + drift; pos[i3 + 1] = base[i3 + 1] + Math.sin(t * 2 + randoms[i] * 6) * 0.01; pos[i3 + 2] = base[i3 + 2] + drift * 0.7; } pointsRef.current.geometry.attributes.position.needsUpdate = true; }); return ( ); } // --- Sphere Effect (teleport) --- const SPHERE_PRESET = { layers: [ { radius: 0.2, segments: 48, size: 0.01, opacity: 0.4 }, { radius: 0.26, segments: 32, size: 0.006, opacity: 0.15 }, ], lightColor: '#aaccff', lightIntensity: 0.3, rotateSpeed: 0.8, pulseSpeed: 1.0, pulseRange: 0.12, colorFn: (normDist, layerIdx) => { if (layerIdx === 0) return [1.0, 0.8 + normDist * 0.15, 0.2]; return [1.0, 0.7, 0.1]; }, }; function buildSphereLayer(radius, segments, colorFn, layerIdx) { const geo = new THREE.SphereGeometry(radius, segments, segments); const posArr = new Float32Array(geo.attributes.position.array); const colArr = new Float32Array(posArr.length); for (let i = 0; i < posArr.length; i += 3) { const x = posArr[i], y = posArr[i + 1], z = posArr[i + 2]; const dist = Math.sqrt(x * x + y * y + z * z); const normDist = dist / (radius || 1); const [r, g, b] = colorFn(normDist, layerIdx); colArr[i] = r; colArr[i + 1] = g; colArr[i + 2] = b; } geo.dispose(); return { positions: posArr, colors: colArr }; } function SphereEffect({ position = [0, 1.0, 0], scale = 1 }) { const groupRef = useRef(); const layerRefs = useRef([]); const preset = SPHERE_PRESET; const layerData = useMemo(() => preset.layers.map((l, i) => buildSphereLayer(l.radius, l.segments, preset.colorFn, i)), []); useFrame(({ clock }) => { if (!groupRef.current) return; const t = clock.getElapsedTime(); const pulse = 1 + Math.sin(t * preset.pulseSpeed) * preset.pulseRange; groupRef.current.scale.setScalar(scale * pulse); layerRefs.current.forEach((ref, i) => { if (!ref) return; const speed = preset.rotateSpeed * (1 + i * 0.4); const dir = i % 2 === 0 ? 1 : -1; ref.rotation.y = t * speed * dir; ref.rotation.x = Math.sin(t * speed * 0.3 + i) * 0.4; ref.rotation.z = Math.cos(t * speed * 0.2 + i * 2) * 0.3; }); }); return ( {preset.layers.map((layer, i) => ( layerRefs.current[i] = el}> ))} ); } // --- Unified export --- function EnergySphere({ type = 'sun', position = [0, 1.0, 0], scale = 1 }) { if (type === 'earth' || type === 'moon') { return ; } return ; } export { EnergySphere }; export default EnergySphere;