1
0

fix effect

This commit is contained in:
2026-03-07 17:07:26 +09:00
parent 2bc35563a2
commit 9344b9ddc3
3 changed files with 140 additions and 105 deletions

View File

@@ -20,8 +20,8 @@ export default function App() {
const [timeScale, setTimeScale] = useState(100); const [timeScale, setTimeScale] = useState(100);
const [camSpeed, setCamSpeed] = useState(0.05); const [camSpeed, setCamSpeed] = useState(0.05);
const [vrmModel, setVrmModel] = useState('ai.vrm'); const [vrmModel, setVrmModel] = useState('ai.vrm');
const [lang, setLang] = useState('ja'); const [lang, setLang] = useState('en');
const langRef = useRef('ja'); const langRef = useRef('en');
const voiceIndexRef = useRef(0); const voiceIndexRef = useRef(0);
const voicePattern = ['normal','normal','normal','normal','normal','normal','normal','normal','skill','skill']; const voicePattern = ['normal','normal','normal','normal','normal','normal','normal','normal','skill','skill'];
const actionIndexRef = useRef(0); const actionIndexRef = useRef(0);

View File

@@ -124,7 +124,7 @@ export default function AvatarScene({ selectedAnimation: animState, onZoomOut, c
const timer = setTimeout(() => setEffect(null), SKILL_DURATION); const timer = setTimeout(() => setEffect(null), SKILL_DURATION);
return () => clearTimeout(timer); return () => clearTimeout(timer);
} else if (animName === 'fly_dodge') { } else if (animName === 'fly_dodge') {
setEffect({ type: 'moon' }); setEffect({ type: 'earth' });
const timer = setTimeout(() => setEffect(null), SKILL_DURATION); const timer = setTimeout(() => setEffect(null), SKILL_DURATION);
return () => clearTimeout(timer); return () => clearTimeout(timer);
} }

View File

@@ -2,113 +2,141 @@ import React, { useRef, useMemo } from 'react';
import { useFrame } from '@react-three/fiber'; import { useFrame } from '@react-three/fiber';
import * as THREE from 'three'; import * as THREE from 'three';
const SPHERE_PRESETS = { // --- Galaxy Effect (skill) ---
const GALAXY_COUNT = 600;
const ARM_COUNT = 4;
const MAX_RADIUS = 0.6;
const GALAXY_PRESETS = {
sun: { sun: {
layers: [ coreColor: [1.0, 0.8, 0.3],
{ radius: 0.3, segments: 64, size: 0.015, opacity: 0.5 }, armColor: [1.0, 0.5, 0.1],
{ radius: 0.22, segments: 48, size: 0.02, opacity: 0.3 }, tipColor: [1.0, 1.0, 0.7],
{ radius: 0.38, segments: 40, size: 0.008, opacity: 0.2 },
],
lightColor: '#ffaa22', lightColor: '#ffaa22',
lightIntensity: 1.5, lightIntensity: 1.5,
rotateSpeed: 1.5,
pulseSpeed: 2.0,
pulseRange: 0.25,
colorFn: (normDist, layerIdx) => {
if (layerIdx === 0) return [1.0, 0.7 + normDist * 0.3, 0.1];
if (layerIdx === 1) return [1.0, 0.4, 0.05];
return [1.0, 0.9, 0.5];
},
},
moon: {
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: 1.0,
rotateSpeed: 0.8, rotateSpeed: 0.8,
pulseSpeed: 1.0, spread: 1.0,
pulseRange: 0.12,
colorFn: (normDist, layerIdx) => {
if (layerIdx === 0) return [0.7, 0.8, 1.0];
return [0.5, 0.6, 1.0];
},
},
earth: {
layers: [
{ radius: 0.25, segments: 56, size: 0.012, opacity: 0.7 },
{ radius: 0.32, segments: 36, size: 0.007, opacity: 0.25 },
],
lightColor: '#44aaff',
lightIntensity: 2.5,
rotateSpeed: 1.0,
pulseSpeed: 1.5,
pulseRange: 0.18,
colorFn: (normDist, layerIdx) => {
if (layerIdx === 0) return [0.2, 0.6 + normDist * 0.4, 1.0];
return [0.1, 0.8, 0.6];
},
},
neutron: {
layers: [
{ radius: 0.15, segments: 72, size: 0.008, opacity: 0.9 },
{ radius: 0.1, segments: 48, size: 0.015, opacity: 0.6 },
{ radius: 0.22, segments: 32, size: 0.005, opacity: 0.3 },
],
lightColor: '#8844ff',
lightIntensity: 4,
rotateSpeed: 3.0,
pulseSpeed: 3.0,
pulseRange: 0.35,
colorFn: (normDist, layerIdx) => {
if (layerIdx === 0) return [0.9, 0.3, 1.0];
if (layerIdx === 1) return [1.0, 1.0, 1.0];
return [0.5, 0.1, 1.0];
},
}, },
}; };
function ParticleLayer({ radius, segments, size, opacity, colorFn, layerIdx }) { function buildGalaxy(preset) {
const data = useMemo(() => { const positions = new Float32Array(GALAXY_COUNT * 3);
const geo = new THREE.SphereGeometry(radius, segments, segments); const colors = new Float32Array(GALAXY_COUNT * 3);
const posArr = new Float32Array(geo.attributes.position.array); const randoms = new Float32Array(GALAXY_COUNT);
const colArr = new Float32Array(posArr.length);
for (let i = 0; i < posArr.length; i += 3) { for (let i = 0; i < GALAXY_COUNT; i++) {
const x = posArr[i], y = posArr[i + 1], z = posArr[i + 2]; const i3 = i * 3;
const dist = Math.sqrt(x * x + y * y + z * z); const arm = i % ARM_COUNT;
const normDist = dist / (radius || 1); const armAngle = (arm / ARM_COUNT) * Math.PI * 2;
const [r, g, b] = colorFn(normDist, layerIdx); const t = Math.random();
colArr[i] = r; const r = t * MAX_RADIUS;
colArr[i + 1] = g; const twist = t * Math.PI * 2.5;
colArr[i + 2] = b; 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;
} }
geo.dispose(); pointsRef.current.geometry.attributes.position.needsUpdate = true;
return { positions: posArr, colors: colArr }; });
}, [radius, segments, colorFn, layerIdx]);
return ( return (
<points> <group position={position} ref={groupRef}>
<bufferGeometry> <points ref={pointsRef}>
<bufferAttribute attach="attributes-position" array={data.positions} count={data.positions.length / 3} itemSize={3} /> <bufferGeometry>
<bufferAttribute attach="attributes-color" array={data.colors} count={data.colors.length / 3} itemSize={3} /> <bufferAttribute attach="attributes-position" array={positions.slice()} count={GALAXY_COUNT} itemSize={3} />
</bufferGeometry> <bufferAttribute attach="attributes-color" array={colors} count={GALAXY_COUNT} itemSize={3} />
<pointsMaterial </bufferGeometry>
transparent <pointsMaterial transparent depthWrite={false} vertexColors opacity={0.6} size={0.012} blending={THREE.AdditiveBlending} />
depthWrite={false} </points>
vertexColors <pointLight intensity={preset.lightIntensity} color={preset.lightColor} distance={3} />
opacity={opacity} </group>
size={size}
blending={THREE.AdditiveBlending}
/>
</points>
); );
} }
function EnergySphere({ type = 'sun', position = [0, 1.5, 0], scale = 1 }) { // --- 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 groupRef = useRef();
const layerRefs = useRef([]); const layerRefs = useRef([]);
const preset = SPHERE_PRESETS[type] || SPHERE_PRESETS.sun; const preset = SPHERE_PRESET;
const layerData = useMemo(() =>
preset.layers.map((l, i) => buildSphereLayer(l.radius, l.segments, preset.colorFn, i)),
[]);
useFrame(({ clock }) => { useFrame(({ clock }) => {
if (!groupRef.current) return; if (!groupRef.current) return;
@@ -130,20 +158,27 @@ function EnergySphere({ type = 'sun', position = [0, 1.5, 0], scale = 1 }) {
<group position={position} ref={groupRef}> <group position={position} ref={groupRef}>
{preset.layers.map((layer, i) => ( {preset.layers.map((layer, i) => (
<group key={i} ref={el => layerRefs.current[i] = el}> <group key={i} ref={el => layerRefs.current[i] = el}>
<ParticleLayer <points>
radius={layer.radius} <bufferGeometry>
segments={layer.segments} <bufferAttribute attach="attributes-position" array={layerData[i].positions} count={layerData[i].positions.length / 3} itemSize={3} />
size={layer.size} <bufferAttribute attach="attributes-color" array={layerData[i].colors} count={layerData[i].colors.length / 3} itemSize={3} />
opacity={layer.opacity} </bufferGeometry>
colorFn={preset.colorFn} <pointsMaterial transparent depthWrite={false} vertexColors opacity={layer.opacity} size={layer.size} blending={THREE.AdditiveBlending} />
layerIdx={i} </points>
/>
</group> </group>
))} ))}
<pointLight position={[0, 0, 0]} intensity={preset.lightIntensity} color={preset.lightColor} distance={3} />
</group> </group>
); );
} }
export { EnergySphere, SPHERE_PRESETS }; // --- Unified export ---
function EnergySphere({ type = 'sun', position = [0, 1.0, 0], scale = 1 }) {
if (type === 'earth' || type === 'moon') {
return <SphereEffect position={position} scale={scale} />;
}
return <GalaxyEffect position={position} scale={scale} />;
}
export { EnergySphere };
export default EnergySphere; export default EnergySphere;