space-game/src/replay/ReplayCamera.ts
Michael Mainguy 0dc3c9d68d
All checks were successful
Build / build (push) Successful in 1m20s
Restructure codebase into logical subdirectories
## Major Reorganization

Reorganized all 57 TypeScript files from flat src/ directory into logical subdirectories for improved maintainability and discoverability.

## New Directory Structure

```
src/
├── core/ (4 files)
│   └── Foundation modules: defaultScene, gameConfig, debug, router
│
├── ship/ (10 files)
│   ├── Ship coordination and subsystems
│   └── input/ - VR controller and keyboard input
│
├── levels/ (10 files)
│   ├── config/ - Level schema, serialization, deserialization
│   ├── generation/ - Level generator and editor
│   └── ui/ - Level selector
│
├── environment/ (11 files)
│   ├── asteroids/ - Rock factory and explosions
│   ├── celestial/ - Suns, planets, textures
│   ├── stations/ - Star base loading
│   └── background/ - Stars, mirror, radar
│
├── ui/ (9 files)
│   ├── hud/ - Scoreboard and status screen
│   ├── screens/ - Login, settings, preloader
│   └── widgets/ - Discord integration
│
├── replay/ (7 files)
│   ├── Replay system components
│   └── recording/ - Physics recording and storage
│
├── game/ (3 files)
│   └── Game systems: stats, progression, demo
│
├── services/ (2 files)
│   └── External integrations: auth, social
│
└── utils/ (5 files)
    └── Shared utilities and helpers
```

## Changes Made

### File Moves (57 files)
- Core modules: 4 files → core/
- Ship system: 10 files → ship/ + ship/input/
- Level system: 10 files → levels/ (+ 3 subdirs)
- Environment: 11 files → environment/ (+ 4 subdirs)
- UI components: 9 files → ui/ (+ 3 subdirs)
- Replay system: 7 files → replay/ + replay/recording/
- Game systems: 3 files → game/
- Services: 2 files → services/
- Utilities: 5 files → utils/

### Import Path Updates
- Updated ~200 import statements across all files
- Fixed relative paths based on new directory structure
- Fixed case-sensitive import issues (physicsRecorder, physicsStorage)
- Ensured consistent lowercase filenames for imports

## Benefits

1. **Easy Navigation** - Related code grouped together
2. **Clear Boundaries** - Logical separation of concerns
3. **Scalability** - Easy pattern for adding new features
4. **Discoverability** - Find ship code in /ship, levels in /levels, etc.
5. **Maintainability** - Isolated modules easier to update
6. **No Circular Dependencies** - Clean dependency graph maintained

## Testing

- All TypeScript compilation errors resolved
- Build succeeds with new structure
- Import paths verified and corrected
- Case-sensitivity issues fixed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 12:53:18 -06:00

188 lines
5.0 KiB
TypeScript

import {
AbstractMesh,
ArcRotateCamera,
Scene,
Vector3
} from "@babylonjs/core";
import debugLog from "../core/debug";
/**
* Camera modes for replay viewing
*/
export enum CameraMode {
FREE = "free",
FOLLOW_SHIP = "follow_ship"
}
/**
* Manages camera for replay viewing with free and follow modes
*/
export class ReplayCamera {
private _camera: ArcRotateCamera;
private _scene: Scene;
private _mode: CameraMode = CameraMode.FREE;
private _followTarget: AbstractMesh | null = null;
constructor(scene: Scene) {
this._scene = scene;
// Create orbiting camera
this._camera = new ArcRotateCamera(
"replayCamera",
Math.PI / 2, // alpha (horizontal rotation)
Math.PI / 3, // beta (vertical rotation)
50, // radius (distance from target)
Vector3.Zero(),
scene
);
// Attach controls for user interaction
const canvas = scene.getEngine().getRenderingCanvas();
if (canvas) {
this._camera.attachControl(canvas, true);
}
// Set camera limits
this._camera.lowerRadiusLimit = 10;
this._camera.upperRadiusLimit = 500;
this._camera.lowerBetaLimit = 0.1;
this._camera.upperBetaLimit = Math.PI / 2;
// Set clipping planes for visibility
this._camera.minZ = 0.1; // Very close near plane
this._camera.maxZ = 5000; // Far plane for distant objects
// Mouse wheel zoom speed
this._camera.wheelPrecision = 20;
// Panning speed
this._camera.panningSensibility = 50;
scene.activeCamera = this._camera;
debugLog("ReplayCamera: Created with clipping planes minZ=0.1, maxZ=5000");
}
/**
* Get the camera instance
*/
public getCamera(): ArcRotateCamera {
return this._camera;
}
/**
* Set camera mode
*/
public setMode(mode: CameraMode): void {
this._mode = mode;
debugLog(`ReplayCamera: Mode set to ${mode}`);
}
/**
* Get current mode
*/
public getMode(): CameraMode {
return this._mode;
}
/**
* Toggle between free and follow modes
*/
public toggleMode(): void {
if (this._mode === CameraMode.FREE) {
this.setMode(CameraMode.FOLLOW_SHIP);
} else {
this.setMode(CameraMode.FREE);
}
}
/**
* Set target to follow (usually the ship)
*/
public setFollowTarget(mesh: AbstractMesh | null): void {
this._followTarget = mesh;
if (mesh) {
this._camera.setTarget(mesh.position);
debugLog("ReplayCamera: Follow target set");
}
}
/**
* Calculate optimal viewpoint to frame all objects
*/
public frameAllObjects(objects: AbstractMesh[]): void {
if (objects.length === 0) {
return;
}
// Calculate bounding box of all objects
let minX = Infinity, minY = Infinity, minZ = Infinity;
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
objects.forEach(obj => {
const pos = obj.position;
debugLog(`ReplayCamera: Framing object ${obj.name} at position ${pos.toString()}`);
minX = Math.min(minX, pos.x);
minY = Math.min(minY, pos.y);
minZ = Math.min(minZ, pos.z);
maxX = Math.max(maxX, pos.x);
maxY = Math.max(maxY, pos.y);
maxZ = Math.max(maxZ, pos.z);
});
// Calculate center
const center = new Vector3(
(minX + maxX) / 2,
(minY + maxY) / 2,
(minZ + maxZ) / 2
);
// Calculate size
const size = Math.max(
maxX - minX,
maxY - minY,
maxZ - minZ
);
// Position camera to frame everything
this._camera.setTarget(center);
this._camera.radius = Math.max(50, size * 1.5); // At least 50 units away
debugLog(`ReplayCamera: Framed ${objects.length} objects (radius: ${this._camera.radius.toFixed(1)})`);
}
/**
* Update camera (call every frame)
*/
public update(): void {
if (this._mode === CameraMode.FOLLOW_SHIP && this._followTarget) {
// Smooth camera following with lerp
Vector3.LerpToRef(
this._camera.target,
this._followTarget.position,
0.1, // Smoothing factor (0 = no follow, 1 = instant)
this._camera.target
);
}
}
/**
* Reset camera to default position
*/
public reset(): void {
this._camera.alpha = Math.PI / 2;
this._camera.beta = Math.PI / 3;
this._camera.radius = 50;
this._camera.setTarget(Vector3.Zero());
debugLog("ReplayCamera: Reset to default");
}
/**
* Dispose of camera
*/
public dispose(): void {
this._camera.dispose();
debugLog("ReplayCamera: Disposed");
}
}