Update particle interpolation and effect management
- 파티클 스케일 및 투명도에 다중 키프레임 보간 기능 추가 - 스케일 보간을 초기값 기준 배율 방식으로 변경 - 기존 이펙트 인스턴스의 설정을 실시간으로 업데이트하도록 개선 - 다중 구간 보간을 위한 lerpKeyframes 유틸리티 함수 구현 - SpriteParticle 인터페이스에 초기 스케일 상태 저장 추가
This commit is contained in:
parent
530e6d0396
commit
f3c5ae3669
@ -15,6 +15,21 @@ const randomRange = (min: number, max: number): number =>
|
|||||||
const lerp = (a: number, b: number, t: number): number =>
|
const lerp = (a: number, b: number, t: number): number =>
|
||||||
a + (b - a) * t;
|
a + (b - a) * t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 키프레임 보간 (2개: 선형, 3개: 전반/후반 분할)
|
||||||
|
*/
|
||||||
|
const lerpKeyframes = (values: number[], t: number): number => {
|
||||||
|
if (values.length === 2) {
|
||||||
|
return lerp(values[0], values[1], t);
|
||||||
|
}
|
||||||
|
// 3개 이상: 균등 구간 분할
|
||||||
|
const segments = values.length - 1;
|
||||||
|
const segT = t * segments;
|
||||||
|
const idx = Math.min(Math.floor(segT), segments - 1);
|
||||||
|
const localT = segT - idx;
|
||||||
|
return lerp(values[idx], values[idx + 1], localT);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 단일 SpriteEffectConfig에 대응하는 런타임 이펙트 인스턴스
|
* 단일 SpriteEffectConfig에 대응하는 런타임 이펙트 인스턴스
|
||||||
* 텍스처 로딩, 메쉬 풀링, 방출/업데이트 로직을 관리
|
* 텍스처 로딩, 메쉬 풀링, 방출/업데이트 로직을 관리
|
||||||
@ -67,6 +82,11 @@ export class SpriteEffectInstance {
|
|||||||
this.loadTexture(config.spriteUrl);
|
this.loadTexture(config.spriteUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 런타임 설정 업데이트 (maxParticles 제외) */
|
||||||
|
updateConfig(config: SpriteEffectConfig): void {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
/** 텍스처 로드 */
|
/** 텍스처 로드 */
|
||||||
private loadTexture(url: string): void {
|
private loadTexture(url: string): void {
|
||||||
const loader = new THREE.TextureLoader();
|
const loader = new THREE.TextureLoader();
|
||||||
@ -139,7 +159,8 @@ export class SpriteEffectInstance {
|
|||||||
particle.velocity.y = Math.sin(angleRad) * speed;
|
particle.velocity.y = Math.sin(angleRad) * speed;
|
||||||
|
|
||||||
// 초기 속성
|
// 초기 속성
|
||||||
particle.scale = randomRange(config.initialScale[0], config.initialScale[1]);
|
particle.initialScale = randomRange(config.initialScale[0], config.initialScale[1]);
|
||||||
|
particle.scale = particle.initialScale;
|
||||||
particle.rotation = 0;
|
particle.rotation = 0;
|
||||||
particle.opacity = 1;
|
particle.opacity = 1;
|
||||||
particle.lifetime = randomRange(config.lifetime[0], config.lifetime[1]);
|
particle.lifetime = randomRange(config.lifetime[0], config.lifetime[1]);
|
||||||
@ -212,10 +233,11 @@ export class SpriteEffectInstance {
|
|||||||
// overLifetime 보간 적용
|
// overLifetime 보간 적용
|
||||||
if (overLifetime) {
|
if (overLifetime) {
|
||||||
if (overLifetime.scale) {
|
if (overLifetime.scale) {
|
||||||
particle.scale = lerp(overLifetime.scale[0], overLifetime.scale[1], lifeRatio);
|
// overLifetime.scale은 initialScale에 대한 배율로 적용
|
||||||
|
particle.scale = particle.initialScale * lerpKeyframes(overLifetime.scale, lifeRatio);
|
||||||
}
|
}
|
||||||
if (overLifetime.opacity) {
|
if (overLifetime.opacity) {
|
||||||
particle.opacity = lerp(overLifetime.opacity[0], overLifetime.opacity[1], lifeRatio);
|
particle.opacity = lerpKeyframes(overLifetime.opacity, lifeRatio);
|
||||||
}
|
}
|
||||||
if (overLifetime.rotationSpeed) {
|
if (overLifetime.rotationSpeed) {
|
||||||
particle.rotation += overLifetime.rotationSpeed * deltaTime;
|
particle.rotation += overLifetime.rotationSpeed * deltaTime;
|
||||||
|
|||||||
@ -59,8 +59,12 @@ export class SpriteEffectManager {
|
|||||||
const key = `${area.id}::${effectConfig.id}`;
|
const key = `${area.id}::${effectConfig.id}`;
|
||||||
activeKeys.add(key);
|
activeKeys.add(key);
|
||||||
|
|
||||||
// 이미 존재하면 스킵
|
// 이미 존재하면 설정만 업데이트
|
||||||
if (this.instances.has(key)) continue;
|
const existing = this.instances.get(key);
|
||||||
|
if (existing) {
|
||||||
|
existing.updateConfig(effectConfig);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// 새 인스턴스 생성
|
// 새 인스턴스 생성
|
||||||
console.log('[SpriteEffectManager] 인스턴스 생성:', key, effectConfig.spriteUrl);
|
console.log('[SpriteEffectManager] 인스턴스 생성:', key, effectConfig.spriteUrl);
|
||||||
|
|||||||
@ -14,6 +14,8 @@ export interface SpriteParticle {
|
|||||||
velocity: Point;
|
velocity: Point;
|
||||||
/** 현재 스케일 */
|
/** 현재 스케일 */
|
||||||
scale: number;
|
scale: number;
|
||||||
|
/** 방출 시 초기 스케일 (overLifetime 배율 기준값) */
|
||||||
|
initialScale: number;
|
||||||
/** 현재 회전 (라디안) */
|
/** 현재 회전 (라디안) */
|
||||||
rotation: number;
|
rotation: number;
|
||||||
/** 현재 투명도 */
|
/** 현재 투명도 */
|
||||||
@ -48,6 +50,7 @@ export class SpriteParticlePool {
|
|||||||
position: { x: 0, y: 0 },
|
position: { x: 0, y: 0 },
|
||||||
velocity: { x: 0, y: 0 },
|
velocity: { x: 0, y: 0 },
|
||||||
scale: 1,
|
scale: 1,
|
||||||
|
initialScale: 1,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
age: 0,
|
age: 0,
|
||||||
|
|||||||
@ -10,10 +10,10 @@ export type SpriteBlendMode = 'normal' | 'additive';
|
|||||||
* 수명 기반 파티클 속성 보간 설정
|
* 수명 기반 파티클 속성 보간 설정
|
||||||
*/
|
*/
|
||||||
export interface SpriteParticleOverLifetime {
|
export interface SpriteParticleOverLifetime {
|
||||||
/** [시작, 끝] 스케일 */
|
/** 스케일 보간: [시작, 끝] 또는 [시작, 중간, 끝] */
|
||||||
scale?: [number, number];
|
scale?: number[];
|
||||||
/** [시작, 끝] 투명도 */
|
/** 투명도 보간: [시작, 끝] 또는 [시작, 중간, 끝] */
|
||||||
opacity?: [number, number];
|
opacity?: number[];
|
||||||
/** 회전 속도 (라디안/초) */
|
/** 회전 속도 (라디안/초) */
|
||||||
rotationSpeed?: number;
|
rotationSpeed?: number;
|
||||||
/** 속도 감쇠 (0-1, 매 프레임 속도에 곱해짐) */
|
/** 속도 감쇠 (0-1, 매 프레임 속도에 곱해짐) */
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user