게임이나 3D 애플리케이션에서 상황에 따라 다양한 카메라 앵글을 자연스럽게 전환하는 것은 매우 중요합니다.
여러 시점을 부드럽게 전환하는 시스템을 구현해보겠습니다.
class CameraManager {
private cameras: Map<string, THREE.Camera>;
private currentCamera: THREE.Camera;
private transitionDuration: number = 1.0;
private isTransitioning: boolean = false;
private startPosition: THREE.Vector3 = new THREE.Vector3();
private startRotation: THREE.Quaternion = new THREE.Quaternion();
private targetPosition: THREE.Vector3 = new THREE.Vector3();
private targetRotation: THREE.Quaternion = new THREE.Quaternion();
private transitionStartTime: number = 0;
constructor() {
this.cameras = new Map();
}
addCamera(name: string, camera: THREE.Camera) {
this.cameras.set(name, camera);
if (!this.currentCamera) {
this.currentCamera = camera;
}
}
async transitionTo(cameraName: string, duration?: number) {
const targetCamera = this.cameras.get(cameraName);
if (!targetCamera || this.isTransitioning) return;
this.transitionDuration = duration || 1.0;
this.isTransitioning = true;
this.transitionStartTime = performance.now();
// 현재 카메라 상태 저장
this.startPosition.copy(this.currentCamera.position);
this.startRotation.copy(this.currentCamera.quaternion);
// 목표 카메라 상태 저장
this.targetPosition.copy(targetCamera.position);
this.targetRotation.copy(targetCamera.quaternion);
}
update() {
if (!this.isTransitioning) return;
const elapsed = (performance.now() - this.transitionStartTime) / 1000;
const progress = Math.min(elapsed / this.transitionDuration, 1.0);
// easeInOutCubic 이징 함수 적용
const t = progress < 0.5
? 4 * progress * progress * progress
: 1 - Math.pow(-2 * progress + 2, 3) / 2;
// 위치와 회전 보간
this.currentCamera.position.lerpVectors(
this.startPosition,
this.targetPosition,
t
);
this.currentCamera.quaternion.slerpQuaternions(
this.startRotation,
this.targetRotation,
t
);
if (progress >= 1.0) {
this.isTransitioning = false;
}
}
}
카메라가 벽이나 장애물을 통과하지 않도록 하는 충돌 처리 시스템을 구현합니다.
class CollisionAwareCamera {
private raycaster: THREE.Raycaster;
private originalDistance: number = 5;
private minDistance: number = 1;
private obstacles: THREE.Mesh[];
constructor(camera: THREE.PerspectiveCamera, scene: THREE.Scene) {
this.raycaster = new THREE.Raycaster();
this.obstacles = [];
// 씬에서 충돌 대상이 되는 메시들을 필터링
scene.traverse((object) => {
if (object instanceof THREE.Mesh && object.userData.collidable) {
this.obstacles.push(object);
}
});
}
update(target: THREE.Vector3, camera: THREE.PerspectiveCamera) {
const directionToCamera = camera.position.clone()
.sub(target).normalize();
this.raycaster.set(target, directionToCamera);
const intersects = this.raycaster.intersectObjects(this.obstacles);
if (intersects.length > 0) {
const collision = intersects[0];
const distanceToCollision = collision.distance;
// 충돌 지점보다 약간 앞에 카메라 위치 조정
if (distanceToCollision < this.originalDistance) {
const newDistance = Math.max(
this.minDistance,
distanceToCollision - 0.5
);
camera.position.copy(target)
.add(directionToCamera.multiplyScalar(newDistance));
}
} else {
// 충돌이 없으면 원래 거리로 복귀
const currentDistance = camera.position.distanceTo(target);
if (currentDistance < this.originalDistance) {
const newDistance = THREE.MathUtils.lerp(
currentDistance,
this.originalDistance,
0.1
);
camera.position.copy(target)
.add(directionToCamera.multiplyScalar(newDistance));
}
}
}
}
게임에서 충돌이나 폭발 등의 이벤트 발생 시 카메라에 흔들림 효과를 주는 시스템을 구현합니다.
class CameraShake {
private camera: THREE.Camera;
private decay: number;
private intensity: number;
private isShaking: boolean = false;
private originalPosition: THREE.Vector3 = new THREE.Vector3();
private originalRotation: THREE.Euler = new THREE.Euler();
constructor(camera: THREE.Camera) {
this.camera = camera;
this.decay = 0.9;
this.intensity = 0.0;
}
shake(intensity: number, duration: number = 0.5) {
this.intensity = intensity;
this.isShaking = true;
this.originalPosition.copy(this.camera.position);
this.originalRotation.copy(this.camera.rotation);
// duration 후에 자동으로 쉐이크 종료
setTimeout(() => {
this.isShaking = false;
// 원래 위치로 부드럽게 복귀
this.camera.position.lerp(this.originalPosition, 0.1);
this.camera.rotation.set(
this.originalRotation.x,
this.originalRotation.y,
this.originalRotation.z
);
}, duration * 1000);
}
update(deltaTime: number) {
if (!this.isShaking) return;
// 랜덤한 오프셋 생성
const offsetX = (Math.random() - 0.5) * this.intensity;
const offsetY = (Math.random() - 0.5) * this.intensity;
const offsetZ = (Math.random() - 0.5) * this.intensity;
// 카메라에 오프셋 적용
this.camera.position.set(
this.originalPosition.x + offsetX,
this.originalPosition.y + offsetY,
this.originalPosition.z + offsetZ
);
// 강도 감소
this.intensity *= this.decay;
}
}
상황에 따라 카메라의 시야각(FOV)을 동적으로 조정하고, 회전 각도를 제한하는 시스템을 구현합니다.
class AdvancedCameraControl {
private camera: THREE.PerspectiveCamera;
private minFOV: number = 45;
private maxFOV: number = 90;
private defaultFOV: number = 75;
private currentFOV: number = 75;
private targetFOV: number = 75;
private rotationLimits: {
minX: number;
maxX: number;
minY: number;
maxY: number;
};
constructor(camera: THREE.PerspectiveCamera) {
this.camera = camera;
this.rotationLimits = {
minX: -Math.PI / 3, // -60도
maxX: Math.PI / 3, // 60도
minY: -Math.PI, // -180도
maxY: Math.PI // 180도
};
}
setFOV(fov: number, immediate: boolean = false) {
this.targetFOV = THREE.MathUtils.clamp(
fov,
this.minFOV,
this.maxFOV
);
if (immediate) {
this.currentFOV = this.targetFOV;
this.camera.fov = this.currentFOV;
this.camera.updateProjectionMatrix();
}
}
update(deltaTime: number) {
// FOV 부드럽게 조정
if (this.currentFOV !== this.targetFOV) {
this.currentFOV = THREE.MathUtils.lerp(
this.currentFOV,
this.targetFOV,
deltaTime * 5
);
this.camera.fov = this.currentFOV;
this.camera.updateProjectionMatrix();
}
// 회전 각도 제한
const rotation = this.camera.rotation;
rotation.x = THREE.MathUtils.clamp(
rotation.x,
this.rotationLimits.minX,
this.rotationLimits.maxX
);
rotation.y = THREE.MathUtils.clamp(
rotation.y,
this.rotationLimits.minY,
this.rotationLimits.maxY
);
}
// 달리기와 같은 효과를 위한 FOV 조정
startSprintEffect() {
this.setFOV(this.defaultFOV * 1.2); // 20% 증가
}
endSprintEffect() {
this.setFOV(this.defaultFOV);
}
}
이러한 고급 카메라 시스템들을 실제로 적용하는 예시입니다.
class GameScene {
private scene: THREE.Scene;
private camera: THREE.PerspectiveCamera;
private cameraManager: CameraManager;
private collisionSystem: CollisionAwareCamera;
private cameraShake: CameraShake;
private cameraControl: AdvancedCameraControl;
private clock: THREE.Clock;
constructor() {
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.clock = new THREE.Clock();
// 카메라 시스템 초기화
this.cameraManager = new CameraManager();
this.collisionSystem = new CollisionAwareCamera(this.camera, this.scene);
this.cameraShake = new CameraShake(this.camera);
this.cameraControl = new AdvancedCameraControl(this.camera);
// 여러 카메라 뷰포인트 설정
this.setupCameras();
}
private setupCameras() {
// 메인 카메라
this.cameraManager.addCamera('main', this.camera);
// 탑뷰 카메라
const topCamera = this.camera.clone();
topCamera.position.set(0, 10, 0);
topCamera.lookAt(0, 0, 0);
this.cameraManager.addCamera('top', topCamera);
// 사이드뷰 카메라
const sideCamera = this.camera.clone();
sideCamera.position.set(10, 2, 0);
sideCamera.lookAt(0, 0, 0);
this.cameraManager.addCamera('side', sideCamera);
}
update() {
const deltaTime = this.clock.getDelta();
// 모든 카메라 시스템 업데이트
this.cameraManager.update();
this.collisionSystem.update(new THREE.Vector3(0, 0, 0), this.camera);
this.cameraShake.update(deltaTime);
this.cameraControl.update(deltaTime);
}
// 이벤트 핸들러 예시
onExplosion(position: THREE.Vector3) {
// 폭발 위치와 카메라의 거리에 따라 쉐이크 강도 조절
const distance = this.camera.position.distanceTo(position);
const intensity = Math.max(0, 1 - (distance / 10)) * 0.5;
this.cameraShake.shake(intensity);
}
onPlayerSprint(isSprinting: boolean) {
if (isSprinting) {
this.cameraControl.startSprintEffect();
} else {
this.cameraControl.endSprintEffect();
}
}
// 카메라 전환 예시
switchToTopView() {
this.cameraManager.transitionTo('top', 1.5);
}
}
이러한 고급 카메라 시스템은 게임이나 3D 애플리케이션에서 사용자 경험을 크게 향상시킬 수 있습니다.
특히 성능 최적화와 에러 처리는 실제 프로덕션 환경에서 안정적인 사용자 경험을 제공하는 데 큰 도움이 됩니다.
class RenderOptimizer {
private lastCameraPosition: THREE.Vector3;
private lastCameraRotation: THREE.Euler;
private renderThreshold: number = 0.001;
constructor() {
this.lastCameraPosition = new THREE.Vector3();
this.lastCameraRotation = new THREE.Euler();
}
shouldRender(camera: THREE.Camera): boolean {
const positionChanged = this.lastCameraPosition
.distanceTo(camera.position) > this.renderThreshold;
const rotationChanged =
Math.abs(this.lastCameraRotation.x - camera.rotation.x) > this.renderThreshold ||
Math.abs(this.lastCameraRotation.y - camera.rotation.y) > this.renderThreshold ||
Math.abs(this.lastCameraRotation.z - camera.rotation.z) > this.renderThreshold;
if (positionChanged || rotationChanged) {
this.lastCameraPosition.copy(camera.position);
this.lastCameraRotation.copy(camera.rotation);
return true;
}
return false;
}
}
class CameraDebugger {
private debugElement: HTMLElement;
private camera: THREE.Camera;
private stats: {
position: THREE.Vector3;
rotation: THREE.Euler;
fov: number;
renderCalls: number;
};
constructor(camera: THREE.Camera) {
this.camera = camera;
this.stats = {
position: new THREE.Vector3(),
rotation: new THREE.Euler(),
fov: 0,
renderCalls: 0
};
this.setupDebugUI();
}
private setupDebugUI() {
this.debugElement = document.createElement('div');
this.debugElement.style.position = 'fixed';
this.debugElement.style.top = '10px';
this.debugElement.style.left = '10px';
this.debugElement.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
this.debugElement.style.color = 'white';
this.debugElement.style.padding = '10px';
this.debugElement.style.fontFamily = 'monospace';
document.body.appendChild(this.debugElement);
}
update() {
this.stats.position.copy(this.camera.position);
this.stats.rotation.copy(this.camera.rotation);
this.stats.fov = (this.camera as THREE.PerspectiveCamera).fov;
this.debugElement.innerHTML = `
Camera Debug Info:
Position: ${this.formatVector3(this.stats.position)}
Rotation: ${this.formatEuler(this.stats.rotation)}
FOV: ${this.stats.fov.toFixed(2)}°
Render Calls: ${this.stats.renderCalls}
`;
}
private formatVector3(v: THREE.Vector3): string {
return `x: ${v.x.toFixed(2)}, y: ${v.y.toFixed(2)}, z: ${v.z.toFixed(2)}`;
}
private formatEuler(e: THREE.Euler): string {
return `x: ${THREE.MathUtils.radToDeg(e.x).toFixed(2)}°,
y: ${THREE.MathUtils.radToDeg(e.y).toFixed(2)}°,
z: ${THREE.MathUtils.radToDeg(e.z).toFixed(2)}°`;
}
}
class ThirdPersonCamera {
private camera: THREE.PerspectiveCamera;
private target: THREE.Object3D;
private offset: THREE.Vector3;
private smoothSpeed: number = 0.1;
private collisionSystem: CollisionAwareCamera;
constructor(
camera: THREE.PerspectiveCamera,
target: THREE.Object3D,
scene: THREE.Scene
) {
this.camera = camera;
this.target = target;
this.offset = new THREE.Vector3(0, 2, -5);
this.collisionSystem = new CollisionAwareCamera(camera, scene);
}
update(deltaTime: number) {
// 타겟의 월드 매트릭스를 고려한 목표 위치 계산
const targetPosition = new THREE.Vector3();
this.target.getWorldPosition(targetPosition);
// 오프셋을 타겟의 회전에 맞춰 변환
const offsetRotated = this.offset.clone()
.applyQuaternion(this.target.quaternion);
// 목표 카메라 위치 계산
const desiredPosition = targetPosition.clone().add(offsetRotated);
// 부드러운 카메라 이동
this.camera.position.lerp(desiredPosition, this.smoothSpeed);
// 충돌 처리
this.collisionSystem.update(targetPosition, this.camera);
// 카메라가 항상 타겟을 바라보도록 설정
this.camera.lookAt(targetPosition);
}
// 숄더뷰 전환
switchToShoulderView(rightShoulder: boolean = true) {
const shoulderOffset = rightShoulder
? new THREE.Vector3(1, 2, -3)
: new THREE.Vector3(-1, 2, -3);
this.offset.lerp(shoulderOffset, 0.1);
}
// 줌 기능
zoom(delta: number) {
const newOffset = this.offset.clone();
newOffset.z += delta;
// 줌 한계 설정
if (newOffset.z >= -10 && newOffset.z <= -2) {
this.offset.copy(newOffset);
}
}
}
class CinematicCamera {
private camera: THREE.PerspectiveCamera;
private waypoints: THREE.Vector3[];
private lookAtPoints: THREE.Vector3[];
private currentWaypoint: number = 0;
private transitionSpeed: number = 0.02;
private isPlaying: boolean = false;
constructor(camera: THREE.PerspectiveCamera) {
this.camera = camera;
this.waypoints = [];
this.lookAtPoints = [];
}
addKeyframe(
position: THREE.Vector3,
lookAt: THREE.Vector3
) {
this.waypoints.push(position.clone());
this.lookAtPoints.push(lookAt.clone());
}
play() {
this.isPlaying = true;
this.currentWaypoint = 0;
}
update() {
if (!this.isPlaying || this.waypoints.length < 2) return;
const currentPos = this.waypoints[this.currentWaypoint];
const currentLookAt = this.lookAtPoints[this.currentWaypoint];
const nextPos = this.waypoints[(this.currentWaypoint + 1) % this.waypoints.length];
const nextLookAt = this.lookAtPoints[(this.currentWaypoint + 1) % this.lookAtPoints.length];
// 위치 보간
this.camera.position.lerp(nextPos, this.transitionSpeed);
// 시선 보간
const currentLookAtPos = new THREE.Vector3();
currentLookAtPos.lerp(nextLookAt, this.transitionSpeed);
this.camera.lookAt(currentLookAtPos);
// 다음 웨이포인트로 전환 체크
if (this.camera.position.distanceTo(nextPos) < 0.1) {
this.currentWaypoint =
(this.currentWaypoint + 1) % this.waypoints.length;
}
}
}
class CameraErrorHandler {
private static readonly ERROR_TYPES = {
COLLISION: 'COLLISION_ERROR',
TRANSITION: 'TRANSITION_ERROR',
RENDER: 'RENDER_ERROR'
};
private static handleError(error: Error, type: string) {
console.error(`Camera System Error (${type}):`, error);
switch (type) {
case this.ERROR_TYPES.COLLISION:
// 충돌 처리 실패 시 안전한 위치로 리셋
this.resetToSafePosition();
break;
case this.ERROR_TYPES.TRANSITION:
// 전환 실패 시 기본 카메라 설정으로 복구
this.restoreDefaultCamera();
break;
case this.ERROR_TYPES.RENDER:
// 렌더링 에러 시 최소 설정으로 전환
this.switchToLowPerformanceMode();
break;
}
}
private static resetToSafePosition() {
// 안전한 초기 위치로 카메라 리셋 로직
}
private static restoreDefaultCamera() {
// 기본 카메라 설정 복구 로직
}
private static switchToLowPerformanceMode() {
// 저성능 모드 전환 로직
}
}
class CameraTestSuite {
private camera: THREE.PerspectiveCamera;
private tests: Map<string, () => boolean>;
constructor(camera: THREE.PerspectiveCamera) {
this.camera = camera;
this.tests = new Map();
this.setupTests();
}
private setupTests() {
this.tests.set('position_bounds', () => {
return this.camera.position.length() < 1000;
});
this.tests.set('fov_range', () => {
return this.camera.fov >= 45 && this.camera.fov <= 90;
});
this.tests.set('rotation_limits', () => {
return Math.abs(this.camera.rotation.x) <= Math.PI / 2;
});
}
runTests(): TestReport {
const results = new Map<string, boolean>();
this.tests.forEach((test, name) => {
try {
results.set(name, test());
} catch (error) {
results.set(name, false);
console.error(`Test failed: ${name}`, error);
}
});
return this.generateReport(results);
}
private generateReport(results: Map<string, boolean>): TestReport {
// 테스트 결과 리포트 생성
return {
totalTests: results.size,
passed: Array.from(results.values()).filter(r => r).length,
failed: Array.from(results.values()).filter(r => !r).length,
details: Object.fromEntries(results)
};
}
}
interface TestReport {
totalTests: number;
passed: number;
failed: number;
details: { [key: string]: boolean };
}
복잡한 카메라 움직임을 미리 정의하고 재생할 수 있는 시스템입니다.
class CameraPath {
private curve: THREE.CatmullRomCurve3;
private points: THREE.Vector3[];
private duration: number;
private startTime: number | null = null;
private isPlaying: boolean = false;
constructor(points: THREE.Vector3[], duration: number = 5) {
this.points = points;
this.duration = duration;
this.curve = new THREE.CatmullRomCurve3(points, true);
}
start() {
this.startTime = performance.now();
this.isPlaying = true;
}
update(camera: THREE.PerspectiveCamera) {
if (!this.isPlaying || !this.startTime) return;
const elapsedTime = (performance.now() - this.startTime) / 1000;
const progress = (elapsedTime % this.duration) / this.duration;
// 경로상의 현재 위치 계산
const currentPoint = this.curve.getPoint(progress);
camera.position.copy(currentPoint);
// 다음 포인트를 바라보도록 설정
const lookAtPoint = this.curve.getPoint((progress + 0.1) % 1);
camera.lookAt(lookAtPoint);
// 경로 완료 체크
if (elapsedTime >= this.duration) {
this.isPlaying = false;
this.startTime = null;
}
}
// 디버그용 시각화
createDebugLine(): THREE.Line {
const points = this.curve.getPoints(50);
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const material = new THREE.LineBasicMaterial({ color: 0xff0000 });
return new THREE.Line(geometry, material);
}
}
피사계 심도(Depth of Field) 효과를 구현하는 시스템입니다.
class AdvancedFocus {
private camera: THREE.PerspectiveCamera;
private focusTarget: THREE.Object3D | null = null;
private bokehPass: any; // THREE.BokehPass
private composer: any; // THREE.EffectComposer
constructor(
camera: THREE.PerspectiveCamera,
renderer: THREE.WebGLRenderer,
scene: THREE.Scene
) {
this.camera = camera;
this.setupPostProcessing(renderer, scene);
}
private setupPostProcessing(
renderer: THREE.WebGLRenderer,
scene: THREE.Scene
) {
const composer = new THREE.EffectComposer(renderer);
const renderPass = new THREE.RenderPass(scene, this.camera);
composer.addPass(renderPass);
const bokehPass = new THREE.BokehPass(scene, this.camera, {
focus: 1.0,
aperture: 0.025,
maxblur: 1.0
});
composer.addPass(bokehPass);
this.composer = composer;
this.bokehPass = bokehPass;
}
setFocusTarget(target: THREE.Object3D) {
this.focusTarget = target;
}
update() {
if (this.focusTarget) {
const distance = this.camera.position.distanceTo(
this.focusTarget.position
);
this.bokehPass.uniforms.focus.value = distance;
}
}
// 피사계 심도 효과 강도 조절
setAperture(value: number) {
this.bokehPass.uniforms.aperture.value = THREE.MathUtils.clamp(
value,
0,
0.05
);
}
}
부드러운 카메라 움직임을 위한 스프링 기반 모션 시스템입니다.
class CameraMotionSystem {
private position: THREE.Vector3;
private velocity: THREE.Vector3;
private target: THREE.Vector3;
private stiffness: number;
private damping: number;
private mass: number;
constructor(initialPosition: THREE.Vector3) {
this.position = initialPosition.clone();
this.velocity = new THREE.Vector3();
this.target = initialPosition.clone();
this.stiffness = 100;
this.damping = 10;
this.mass = 1;
}
setTarget(target: THREE.Vector3) {
this.target.copy(target);
}
update(deltaTime: number) {
// 스프링 힘 계산
const force = new THREE.Vector3()
.subVectors(this.target, this.position)
.multiplyScalar(this.stiffness);
// 감쇠력 계산
const dampingForce = this.velocity
.clone()
.multiplyScalar(-this.damping);
// 전체 힘
force.add(dampingForce);
// 가속도
const acceleration = force.multiplyScalar(1 / this.mass);
// 속도 업데이트
this.velocity.add(acceleration.multiplyScalar(deltaTime));
// 위치 업데이트
this.position.add(this.velocity.multiplyScalar(deltaTime));
return this.position.clone();
}
}
class CameraPerformanceMonitor {
private metrics: {
updateTime: number[];
renderCalls: number[];
memoryUsage: number[];
};
private maxSamples: number = 60;
constructor() {
this.metrics = {
updateTime: [],
renderCalls: [],
memoryUsage: []
};
}
addMetric(
updateTime: number,
renderCalls: number,
memoryUsage: number
) {
this.metrics.updateTime.push(updateTime);
this.metrics.renderCalls.push(renderCalls);
this.metrics.memoryUsage.push(memoryUsage);
// 샘플 수 제한
if (this.metrics.updateTime.length > this.maxSamples) {
this.metrics.updateTime.shift();
this.metrics.renderCalls.shift();
this.metrics.memoryUsage.shift();
}
}
getAverages() {
return {
updateTime: this.calculateAverage(this.metrics.updateTime),
renderCalls: this.calculateAverage(this.metrics.renderCalls),
memoryUsage: this.calculateAverage(this.metrics.memoryUsage)
};
}
private calculateAverage(array: number[]): number {
return array.reduce((a, b) => a + b, 0) / array.length;
}
generateReport(): string {
const averages = this.getAverages();
return `
카메라 시스템 성능 리포트:
- 평균 업데이트 시간: ${averages.updateTime.toFixed(2)}ms
- 평균 렌더 콜: ${averages.renderCalls.toFixed(2)}
- 평균 메모리 사용량: ${(averages.memoryUsage / 1024 / 1024).toFixed(2)}MB
`;
}
}
모든 시스템을 통합하여 사용하는 예제입니다.
class AdvancedCameraSystem {
private camera: THREE.PerspectiveCamera;
private motionSystem: CameraMotionSystem;
private focusSystem: AdvancedFocus;
private pathSystem: CameraPath;
private performanceMonitor: CameraPerformanceMonitor;
private debugger: CameraDebugger;
constructor(
camera: THREE.PerspectiveCamera,
renderer: THREE.WebGLRenderer,
scene: THREE.Scene
) {
this.camera = camera;
this.motionSystem = new CameraMotionSystem(camera.position);
this.focusSystem = new AdvancedFocus(camera, renderer, scene);
this.performanceMonitor = new CameraPerformanceMonitor();
this.debugger = new CameraDebugger(camera);
// 카메라 패스 초기화
const pathPoints = [
new THREE.Vector3(0, 2, 5),
new THREE.Vector3(5, 2, 0),
new THREE.Vector3(0, 2, -5),
new THREE.Vector3(-5, 2, 0)
];
this.pathSystem = new CameraPath(pathPoints, 10);
}
update(deltaTime: number) {
const startTime = performance.now();
// 모션 시스템 업데이트
const newPosition = this.motionSystem.update(deltaTime);
this.camera.position.copy(newPosition);
// 포커스 시스템 업데이트
this.focusSystem.update();
// 패스 시스템 업데이트
this.pathSystem.update(this.camera);
// 퍼포먼스 모니터링
const updateTime = performance.now() - startTime;
this.performanceMonitor.addMetric(
updateTime,
renderer.info.render.calls,
performance.memory?.usedJSHeapSize || 0
);
// 디버그 정보 업데이트
this.debugger.update();
}
// 카메라 설정 저장
saveState(): CameraState {
return {
position: this.camera.position.clone(),
rotation: this.camera.rotation.clone(),
fov: this.camera.fov,
target: this.motionSystem.getTarget().clone()
};
}
// 카메라 설정 복원
restoreState(state: CameraState) {
this.camera.position.copy(state.position);
this.camera.rotation.copy(state.rotation);
this.camera.fov = state.fov;
this.motionSystem.setTarget(state.target);
this.camera.updateProjectionMatrix();
}
}
interface CameraState {
position: THREE.Vector3;
rotation: THREE.Euler;
fov: number;
target: THREE.Vector3;
}
이 시스템들을 활용하면 전문적인 수준의 카메라 워크를 구현할 수 있습니다.
실제 게임이나 3D 애플리케이션에서 사용할 때는 프로젝트의 요구사항에 맞게 커스터마이징하여 사용해보면 좋을것같다는 생각이 든다.