All checks were successful
Build / build (push) Successful in 1m21s
Major Refactoring: - Extracted input handling, physics, audio, and weapons into separate modules - Reduced Ship class from 542 lines to 305 lines (44% reduction) - Ship now acts as coordinator between modular systems New Modules Created: - src/shipPhysics.ts - Pure force calculation and application logic - No external dependencies, fully testable in isolation - Handles linear/angular forces with velocity caps - Returns force magnitudes for audio feedback - src/keyboardInput.ts - Keyboard and mouse input handling - Combines both input methods in unified interface - Exposes observables for shoot and camera change events - Clean getInputState() API - src/controllerInput.ts - VR controller input handling - Maps WebXR controllers to input state - Handles thumbstick and button events - Observables for shooting and camera adjustments - src/shipAudio.ts - Audio management system - Manages thrust sounds (primary and secondary) - Weapon fire sounds - Dynamic volume based on force magnitudes - src/weaponSystem.ts - Projectile creation and lifecycle - Creates and manages ammo instances - Physics setup for projectiles - Auto-disposal timer Ship Class Changes: - Removed all input handling code (keyboard, mouse, VR) - Removed force calculation logic - Removed audio management code - Removed weapon creation code - Now wires up modular systems via observables - Maintains same external API (backward compatible) Material Improvements: - Updated planet materials to use emissive texture - Enhanced sun material with better color settings - Set planets to unlit mode for better performance Benefits: - Each system independently testable - Clear separation of concerns - Easier to maintain and extend - Better code organization 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
144 lines
4.0 KiB
TypeScript
144 lines
4.0 KiB
TypeScript
import { FreeCamera, Observable, Scene, Vector2 } from "@babylonjs/core";
|
|
|
|
/**
|
|
* Handles keyboard and mouse input for ship control
|
|
* Combines both input methods into a unified interface
|
|
*/
|
|
export class KeyboardInput {
|
|
private _leftStick: Vector2 = Vector2.Zero();
|
|
private _rightStick: Vector2 = Vector2.Zero();
|
|
private _mouseDown: boolean = false;
|
|
private _mousePos: Vector2 = new Vector2(0, 0);
|
|
private _onShootObservable: Observable<void> = new Observable<void>();
|
|
private _onCameraChangeObservable: Observable<number> = new Observable<number>();
|
|
private _scene: Scene;
|
|
|
|
constructor(scene: Scene) {
|
|
this._scene = scene;
|
|
}
|
|
|
|
/**
|
|
* Get observable that fires when shoot key/button is pressed
|
|
*/
|
|
public get onShootObservable(): Observable<void> {
|
|
return this._onShootObservable;
|
|
}
|
|
|
|
/**
|
|
* Get observable that fires when camera change key is pressed
|
|
*/
|
|
public get onCameraChangeObservable(): Observable<number> {
|
|
return this._onCameraChangeObservable;
|
|
}
|
|
|
|
/**
|
|
* Get current input state (stick positions)
|
|
*/
|
|
public getInputState() {
|
|
return {
|
|
leftStick: this._leftStick.clone(),
|
|
rightStick: this._rightStick.clone(),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Setup keyboard and mouse event listeners
|
|
*/
|
|
public setup(): void {
|
|
this.setupKeyboard();
|
|
this.setupMouse();
|
|
}
|
|
|
|
/**
|
|
* Setup keyboard event listeners
|
|
*/
|
|
private setupKeyboard(): void {
|
|
document.onkeyup = () => {
|
|
this._leftStick.y = 0;
|
|
this._leftStick.x = 0;
|
|
this._rightStick.y = 0;
|
|
this._rightStick.x = 0;
|
|
};
|
|
|
|
document.onkeydown = (ev) => {
|
|
switch (ev.key) {
|
|
case '1':
|
|
this._onCameraChangeObservable.notifyObservers(1);
|
|
break;
|
|
case ' ':
|
|
this._onShootObservable.notifyObservers();
|
|
break;
|
|
case 'e':
|
|
break;
|
|
case 'w':
|
|
this._leftStick.y = -1;
|
|
break;
|
|
case 's':
|
|
this._leftStick.y = 1;
|
|
break;
|
|
case 'a':
|
|
this._leftStick.x = -1;
|
|
break;
|
|
case 'd':
|
|
this._leftStick.x = 1;
|
|
break;
|
|
case 'ArrowUp':
|
|
this._rightStick.y = -1;
|
|
break;
|
|
case 'ArrowDown':
|
|
this._rightStick.y = 1;
|
|
break;
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Setup mouse event listeners for drag-based rotation control
|
|
*/
|
|
private setupMouse(): void {
|
|
this._scene.onPointerDown = (evt) => {
|
|
this._mousePos.x = evt.x;
|
|
this._mousePos.y = evt.y;
|
|
this._mouseDown = true;
|
|
};
|
|
|
|
this._scene.onPointerUp = () => {
|
|
this._mouseDown = false;
|
|
};
|
|
|
|
this._scene.onPointerMove = (ev) => {
|
|
if (!this._mouseDown) {
|
|
return;
|
|
}
|
|
|
|
const xInc = (ev.x - this._mousePos.x) / 100;
|
|
const yInc = (ev.y - this._mousePos.y) / 100;
|
|
|
|
if (Math.abs(xInc) <= 1) {
|
|
this._rightStick.x = xInc;
|
|
} else {
|
|
this._rightStick.x = Math.sign(xInc);
|
|
}
|
|
|
|
if (Math.abs(yInc) <= 1) {
|
|
this._rightStick.y = yInc;
|
|
} else {
|
|
this._rightStick.y = Math.sign(yInc);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Cleanup event listeners
|
|
*/
|
|
public dispose(): void {
|
|
document.onkeydown = null;
|
|
document.onkeyup = null;
|
|
this._scene.onPointerDown = null;
|
|
this._scene.onPointerUp = null;
|
|
this._scene.onPointerMove = null;
|
|
this._onShootObservable.clear();
|
|
this._onCameraChangeObservable.clear();
|
|
}
|
|
}
|