class AdvancedSuspension {
private readonly MAX_SUSPENSION_TRAVEL = 0.2; // 서스펜션 최대 이동 거리 (미터)
private readonly SPRING_STIFFNESS = 50000; // 스프링 강성
private readonly DAMPING = 4500; // 댐핑 계수
constructor(private wheel: RigidBody, private chassisConnectionPoint: Vector3) {}
update() {
const rayStart = this.wheel.translation();
const rayDir = new Vector3(0, -1, 0);
const rayLength = this.MAX_SUSPENSION_TRAVEL * 2;
// 지면 감지를 위한 레이캐스트
const hit = world.castRay(
new Ray(rayStart, rayDir),
rayLength,
true,
undefined,
undefined,
this.wheel
);
if (hit) {
const compressionLength = rayLength - hit.toi;
this.applySuspensionForce(compressionLength);
}
}
private applySuspensionForce(compression: number) {
const springForce = compression * this.SPRING_STIFFNESS;
const velocity = this.wheel.linvel();
const dampingForce = velocity.y * this.DAMPING;
const totalForce = springForce + dampingForce;
this.wheel.applyImpulse(new Vector3(0, totalForce, 0), true);
}
}
enum TerrainType {
ASPHALT,
DIRT,
SAND,
SNOW,
MUD
}
class TerrainAwareSuspension extends AdvancedSuspension {
private terrainMultipliers = new Map<TerrainType, number>([
[TerrainType.ASPHALT, 1.0],
[TerrainType.DIRT, 0.8],
[TerrainType.SAND, 0.6],
[TerrainType.SNOW, 0.4],
[TerrainType.MUD, 0.5]
]);
protected override applySuspensionForce(compression: number) {
const terrainType = this.detectTerrainType();
const multiplier = this.terrainMultipliers.get(terrainType) || 1.0;
const adjustedSpringForce = compression * this.SPRING_STIFFNESS * multiplier;
const adjustedDampingForce = this.wheel.linvel().y * this.DAMPING * multiplier;
const totalForce = adjustedSpringForce + adjustedDampingForce;
this.wheel.applyImpulse(new Vector3(0, totalForce, 0), true);
}
private detectTerrainType(): TerrainType {
// 레이캐스트 결과로부터 지형 타입 감지
const hit = this.castTerrainRay();
if (hit) {
return this.analyzeTerrainMaterial(hit.collider);
}
return TerrainType.ASPHALT;
}
}
interface DamageZone {
position: Vector3;
radius: number;
maxHealth: number;
currentHealth: number;
}
class VehicleDamageSystem {
private damageZones: DamageZone[] = [];
private readonly DAMAGE_THRESHOLD = 50; // 최소 충격량
constructor() {
this.initializeDamageZones();
}
private initializeDamageZones() {
// 차량의 주요 부위별 손상 영역 정의
this.damageZones = [
{ position: new Vector3(0, 0, 2), radius: 1, maxHealth: 100, currentHealth: 100 }, // 전면부
{ position: new Vector3(0, 0, -2), radius: 1, maxHealth: 100, currentHealth: 100 }, // 후면부
{ position: new Vector3(1, 0, 0), radius: 0.8, maxHealth: 80, currentHealth: 80 }, // 우측면
{ position: new Vector3(-1, 0, 0), radius: 0.8, maxHealth: 80, currentHealth: 80 }, // 좌측면
];
}
handleCollision(impact: Vector3, force: number) {
if (force < this.DAMAGE_THRESHOLD) return;
this.damageZones.forEach(zone => {
const distance = impact.distanceTo(zone.position);
if (distance <= zone.radius) {
const damage = this.calculateDamage(force, distance, zone.radius);
this.applyDamage(zone, damage);
}
});
}
private calculateDamage(force: number, distance: number, radius: number): number {
const distanceFactor = 1 - (distance / radius);
return force * distanceFactor;
}
private applyDamage(zone: DamageZone, damage: number) {
zone.currentHealth = Math.max(0, zone.currentHealth - damage);
this.updateVehiclePerformance();
}
private updateVehiclePerformance() {
// 손상도에 따른 차량 성능 저하 적용
const averageHealth = this.calculateAverageHealth();
this.adjustVehicleParameters(averageHealth);
}
}
class VehicleDeformationSystem {
private originalVertices: Float32Array;
private deformedVertices: Float32Array;
private readonly MAX_DEFORMATION = 0.3;
constructor(private mesh: Mesh) {
this.initializeVertices();
}
private initializeVertices() {
const positions = this.mesh.geometry.getAttribute('position');
this.originalVertices = new Float32Array(positions.array);
this.deformedVertices = new Float32Array(positions.array);
}
applyDeformation(impactPoint: Vector3, force: number) {
const geometry = this.mesh.geometry;
const positionAttribute = geometry.getAttribute('position');
for (let i = 0; i < positionAttribute.count; i++) {
const vertex = new Vector3().fromBufferAttribute(positionAttribute, i);
const distance = impactPoint.distanceTo(vertex);
const deformation = this.calculateDeformation(force, distance);
const deformationVector = vertex.sub(impactPoint).normalize().multiplyScalar(deformation);
this.deformedVertices[i * 3] = this.originalVertices[i * 3] + deformationVector.x;
this.deformedVertices[i * 3 + 1] = this.originalVertices[i * 3 + 1] + deformationVector.y;
this.deformedVertices[i * 3 + 2] = this.originalVertices[i * 3 + 2] + deformationVector.z;
}
positionAttribute.needsUpdate = true;
geometry.computeVertexNormals();
}
private calculateDeformation(force: number, distance: number): number {
const deformation = (force / (1 + distance)) * this.MAX_DEFORMATION;
return Math.min(deformation, this.MAX_DEFORMATION);
}
}
enum WeatherCondition {
CLEAR,
RAIN,
SNOW,
ICE
}
class WeatherPhysicsSystem {
private currentWeather: WeatherCondition = WeatherCondition.CLEAR;
private frictionMultipliers = new Map<WeatherCondition, number>([
[WeatherCondition.CLEAR, 1.0],
[WeatherCondition.RAIN, 0.7],
[WeatherCondition.SNOW, 0.4],
[WeatherCondition.ICE, 0.2]
]);
setWeatherCondition(weather: WeatherCondition) {
this.currentWeather = weather;
this.updateVehiclePhysics();
}
private updateVehiclePhysics() {
const multiplier = this.frictionMultipliers.get(this.currentWeather) || 1.0;
// 타이어 마찰력 조정
this.updateTireFriction(multiplier);
// 브레이크 효과 조정
this.updateBrakeEffectiveness(multiplier);
// 조향 반응성 조정
this.updateSteeringResponse(multiplier);
}
private updateTireFriction(multiplier: number) {
const wheels = [this.wheelFLCollider, this.wheelFRCollider,
this.wheelBLCollider, this.wheelBRCollider];
wheels.forEach(wheel => {
wheel.setFriction(this.BASE_FRICTION * multiplier);
});
}
}
class VehicleLODSystem {
private readonly LOD_DISTANCES = [10, 30, 50]; // 미터 단위
private readonly PHYSICS_UPDATE_RATES = [60, 30, 15]; // Hz 단위
private currentLODLevel = 0;
private accumulator = 0;
update(deltaTime: number, distanceToCamera: number) {
this.updateLODLevel(distanceToCamera);
this.accumulator += deltaTime;
const updateRate = this.PHYSICS_UPDATE_RATES[this.currentLODLevel];
const timeStep = 1 / updateRate;
if (this.accumulator >= timeStep) {
this.accumulator -= timeStep;
this.updatePhysics(timeStep);
}
}
private updateLODLevel(distance: number) {
for (let i = 0; i < this.LOD_DISTANCES.length; i++) {
if (distance < this.LOD_DISTANCES[i]) {
this.currentLODLevel = i;
return;
}
}
this.currentLODLevel = this.LOD_DISTANCES.length;
}
}
class PhysicsOptimizer {
private readonly SLEEP_THRESHOLD = 0.1;
private readonly SLEEP_TIME = 3.0; // 초 단위
private sleepTimer = 0;
private isSimulating = true;
update(deltaTime: number) {
if (!this.isSimulating) return;
const velocity = this.getRigidBodyVelocity();
if (velocity.length() < this.SLEEP_THRESHOLD) {
this.sleepTimer += deltaTime;
if (this.sleepTimer >= this.SLEEP_TIME) {
this.pauseSimulation();
}
} else {
this.sleepTimer = 0;
}
}
private pauseSimulation() {
this.isSimulating = false;
this.rigidBody.setEnabled(false);
}
onCollision() {
if (!this.isSimulating) {
this.isSimulating = true;
this.rigidBody.setEnabled(true);
this.sleepTimer = 0;
}
}
}
interface VehicleState {
position: Vector3;
rotation: Quaternion;
velocity: Vector3;
angularVelocity: Vector3;
wheelStates: WheelState[];
damage: number;
}
class NetworkedVehicleController {
private readonly STATE_BUFFER_SIZE = 10;
private readonly INTERPOLATION_DELAY = 100; // ms
private stateBuffer: VehicleState[] = [];
private lastUpdateTime = 0;
update(currentTime: number) {
if (this.stateBuffer.length < 2) return;
const interpolationTime = currentTime - this.INTERPOLATION_DELAY;
let previousState: VehicleState | null = null;
let nextState: VehicleState | null = null;
// 보간할 상태 찾기
for (let i = 0; i < this.stateBuffer.length - 1; i++) {
if (this.stateBuffer[i].timestamp <= interpolationTime &&
this.stateBuffer[i + 1].timestamp >= interpolationTime) {
previousState = this.stateBuffer[i];
nextState = this.stateBuffer[i + 1];
break;
}
}
if (previousState && nextState) {
this.interpolateState(previousState, nextState, interpolationTime);
}
}
private interpolateWheelStates(previous: WheelState[], next: WheelState[], alpha: number): WheelState[] {
return previous.map((prevWheel, index) => ({
rotation: prevWheel.rotation.slerp(next[index].rotation, alpha),
suspensionLength: lerp(prevWheel.suspensionLength, next[index].suspensionLength, alpha),
groundContact: alpha < 0.5 ? prevWheel.groundContact : next[index].groundContact
}));
}
private applyState(state: VehicleState) {
// 물리 엔진에 보간된 상태 적용
this.rigidBody.setTranslation(state.position);
this.rigidBody.setRotation(state.rotation);
this.rigidBody.setLinvel(state.velocity);
this.rigidBody.setAngvel(state.angularVelocity);
// 휠 상태 업데이트
state.wheelStates.forEach((wheelState, index) => {
this.wheels[index].setRotation(wheelState.rotation);
this.suspensions[index].setLength(wheelState.suspensionLength);
});
}
}
class PredictionSystem {
private readonly PREDICTION_STEPS = 10;
private readonly CORRECTION_THRESHOLD = 0.5; // 미터 단위
private predictedStates: VehicleState[] = [];
predictNextState(currentState: VehicleState, input: VehicleInput): VehicleState {
let predictedState = this.cloneState(currentState);
// 물리 시뮬레이션 스텝
for (let i = 0; i < this.PREDICTION_STEPS; i++) {
predictedState = this.simulatePhysicsStep(predictedState, input);
this.predictedStates.push(predictedState);
}
return predictedState;
}
handleServerState(serverState: VehicleState) {
const predictedState = this.predictedStates[0];
const error = this.calculateError(predictedState, serverState);
if (error > this.CORRECTION_THRESHOLD) {
this.applySmoothedCorrection(serverState);
}
}
private calculateError(predicted: VehicleState, actual: VehicleState): number {
return predicted.position.distanceTo(actual.position);
}
private applySmoothedCorrection(serverState: VehicleState) {
const CORRECTION_FACTOR = 0.3;
const currentState = this.getCurrentState();
// 보간된 수정 적용
const correctedPosition = currentState.position.lerp(
serverState.position,
CORRECTION_FACTOR
);
const correctedRotation = currentState.rotation.slerp(
serverState.rotation,
CORRECTION_FACTOR
);
this.updateVehicleState({
...currentState,
position: correctedPosition,
rotation: correctedRotation
});
}
}
class InputDelayCompensation {
private readonly INPUT_BUFFER_SIZE = 30;
private readonly MAX_INPUT_DELAY = 200; // ms
private inputBuffer: VehicleInput[] = [];
private inputSequenceNumber = 0;
processInput(input: VehicleInput) {
const timestampedInput = {
...input,
sequence: this.inputSequenceNumber++,
timestamp: Date.now()
};
this.inputBuffer.push(timestampedInput);
this.trimBuffer();
return this.predictStateWithInput(timestampedInput);
}
private predictStateWithInput(input: TimestampedInput): VehicleState {
const currentState = this.getCurrentState();
const deltaTime = (Date.now() - input.timestamp) / 1000;
// 물리 시뮬레이션으로 예측된 상태 계산
return {
position: this.predictPosition(currentState, input, deltaTime),
rotation: this.predictRotation(currentState, input, deltaTime),
velocity: this.predictVelocity(currentState, input, deltaTime),
wheelStates: this.predictWheelStates(currentState, input, deltaTime)
};
}
private trimBuffer() {
const currentTime = Date.now();
this.inputBuffer = this.inputBuffer.filter(input =>
currentTime - input.timestamp <= this.MAX_INPUT_DELAY
);
}
}
class PerformanceMonitor {
private metrics: {
physicsTime: number[];
frameTime: number[];
networkLatency: number[];
} = {
physicsTime: [],
frameTime: [],
networkLatency: []
};
private readonly SAMPLE_SIZE = 100;
measurePhysicsStep(callback: () => void) {
const startTime = performance.now();
callback();
const endTime = performance.now();
this.metrics.physicsTime.push(endTime - startTime);
this.trimMetrics();
}
private trimMetrics() {
Object.keys(this.metrics).forEach(key => {
if (this.metrics[key].length > this.SAMPLE_SIZE) {
this.metrics[key].shift();
}
});
}
getPerformanceReport(): PerformanceReport {
return {
averagePhysicsTime: this.calculateAverage(this.metrics.physicsTime),
averageFrameTime: this.calculateAverage(this.metrics.frameTime),
averageLatency: this.calculateAverage(this.metrics.networkLatency),
physicsTimeP95: this.calculatePercentile(this.metrics.physicsTime, 95),
frameTimeP95: this.calculatePercentile(this.metrics.frameTime, 95),
latencyP95: this.calculatePercentile(this.metrics.networkLatency, 95)
};
}
private calculatePercentile(array: number[], percentile: number): number {
const sorted = [...array].sort((a, b) => a - b);
const index = Math.ceil((percentile / 100) * sorted.length) - 1;
return sorted[index];
}
}
이상의 구현을 통해 다음과 같은 이점을 얻을 수 있습니다
1. 현실적인 차량 물리 시뮬레이션
2. 최적화된 성능
3. 멀티플레이어 지원
이러한 기능들은 실제 게임이나 시뮬레이션에서 활용될 수 있으며, 필요에 따라 더 확장하거나 조정할 수 있습니다.
class AIPhysicsOptimizer {
private readonly modelWeights: Float32Array;
private readonly inputFeatures = 10;
constructor() {
// 사전 학습된 모델 가중치 로드
this.modelWeights = this.loadModelWeights();
}
predictOptimalParameters(state: VehicleState): PhysicsParameters {
// 현재 상태를 특징 벡터로 변환
const features = this.extractFeatures(state);
// 신경망을 통한 최적 파라미터 예측
return {
suspensionStiffness: this.predict(features, 'suspension'),
dampingCoefficient: this.predict(features, 'damping'),
frictionCoefficient: this.predict(features, 'friction')
};
}
private extractFeatures(state: VehicleState): number[] {
return [
state.velocity.length(),
state.angularVelocity.length(),
state.suspension.compression,
state.terrain.roughness,
state.weather.condition,
// ... 추가 특징들
];
}
}
class WasmPhysicsEngine {
private wasmInstance: WebAssembly.Instance;
private memory: WebAssembly.Memory;
async initialize() {
const response = await fetch('physics_engine.wasm');
const wasmModule = await WebAssembly.compile(await response.arrayBuffer());
this.memory = new WebAssembly.Memory({ initial: 256 });
this.wasmInstance = await WebAssembly.instantiate(wasmModule, {
env: {
memory: this.memory,
log_number: (n: number) => console.log(n)
}
});
}
updatePhysics(deltaTime: number) {
const ptr = this.allocateState();
this.copyStateToWasm(ptr);
(this.wasmInstance.exports.update_physics as Function)(ptr, deltaTime);
this.copyStateFromWasm(ptr);
this.deallocateState(ptr);
}
}
class MobileOptimizer {
private readonly MOBILE_QUALITY_LEVELS = {
LOW: {
physicsHz: 30,
maxParticles: 100,
shadowQuality: 0,
maxDeformation: 5
},
MEDIUM: {
physicsHz: 45,
maxParticles: 250,
shadowQuality: 1,
maxDeformation: 10
},
HIGH: {
physicsHz: 60,
maxParticles: 500,
shadowQuality: 2,
maxDeformation: 20
}
};
constructor(private quality: 'LOW' | 'MEDIUM' | 'HIGH') {
this.applyQualitySettings();
}
private applyQualitySettings() {
const settings = this.MOBILE_QUALITY_LEVELS[this.quality];
// 물리 엔진 설정 적용
PhysicsEngine.setUpdateRate(settings.physicsHz);
ParticleSystem.setMaxParticles(settings.maxParticles);
// 그래픽 설정 적용
this.setupGraphics(settings);
}
private setupGraphics(settings: any) {
const renderer = this.getRenderer();
renderer.shadowMap.enabled = settings.shadowQuality > 0;
renderer.setPixelRatio(window.devicePixelRatio * 0.8);
}
}
class VRVehicleController {
private readonly HAND_GRIP_THRESHOLD = 0.7;
private readonly STEERING_SENSITIVITY = 1.5;
constructor(private xrSession: XRSession) {
this.initializeVRControls();
}
private initializeVRControls() {
this.xrSession.addEventListener('inputsourceschange', this.onInputSourcesChange);
// VR 컨트롤러 설정
this.setupControllers();
}
private setupControllers() {
const leftController = this.xrSession.inputSources[0];
const rightController = this.xrSession.inputSources[1];
// 핸들링 컨트롤
this.setupSteeringWheel(leftController, rightController);
// 가속/제동 컨트롤
this.setupPedals(rightController);
}
private updateVRControls(frame: XRFrame) {
const pose = frame.getInputPose(this.xrSession.inputSources[0]);
if (pose) {
// 핸들 회전 계산
const rotation = this.calculateSteeringRotation(pose);
this.updateVehicleSteering(rotation);
// 가속/제동 상태 업데이트
this.updateThrottleAndBrake(frame);
}
}
}
class PhysicsTestSuite {
private testCases: PhysicsTestCase[] = [];
private results: TestResult[] = [];
async runAllTests() {
for (const testCase of this.testCases) {
const result = await this.runTest(testCase);
this.results.push(result);
if (!result.success) {
console.error(`Test failed: ${testCase.name}`, result.error);
}
}
this.generateReport();
}
private async runTest(testCase: PhysicsTestCase): Promise<TestResult> {
const vehicle = new Vehicle(testCase.initialState);
const startTime = performance.now();
try {
// 테스트 시나리오 실행
for (const action of testCase.actions) {
await this.executeAction(vehicle, action);
}
// 결과 검증
const endState = vehicle.getState();
const success = this.verifyResults(endState, testCase.expectedState);
return {
name: testCase.name,
success,
duration: performance.now() - startTime,
error: null
};
} catch (error) {
return {
name: testCase.name,
success: false,
duration: performance.now() - startTime,
error
};
}
}
}