All checks were successful
Build / build (push) Successful in 1m14s
Implemented Option 5: Integrate ship physics constants into centralized GameConfig Changes to gameConfig.ts: - Added shipPhysics object with 4 tunable parameters: - maxLinearVelocity (default: 200) - maxAngularVelocity (default: 1.4) - linearForceMultiplier (default: 800) - angularForceMultiplier (default: 15) - Updated save() to persist shipPhysics to localStorage - Updated loadFromStorage() to load shipPhysics with fallback defaults - Updated reset() to restore shipPhysics defaults Changes to shipPhysics.ts: - Removed hardcoded constants - Added GameConfig import - Updated applyForces() to read values from GameConfig.getInstance().shipPhysics - Now reads physics parameters dynamically at runtime Changes to index.html: - Added Ship Physics section in Settings UI - 4 number inputs with appropriate min/max/step values - Help text explaining each parameter's effect on ship handling - Section positioned after Developer settings Changes to settingsScreen.ts: - Added input element references for ship physics controls - Updated loadSettings() to populate ship physics inputs from config - Updated saveSettings() to save ship physics values to config - Values persist across sessions via localStorage Benefits: - Ship physics now tunable in real-time via settings UI - Parameters persist across sessions - Easy to reset to defaults - Centralized configuration management - No need to edit code to adjust ship handling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
108 lines
3.8 KiB
TypeScript
108 lines
3.8 KiB
TypeScript
import { PhysicsBody, TransformNode, Vector2, Vector3 } from "@babylonjs/core";
|
|
import { GameConfig } from "./gameConfig";
|
|
|
|
export interface InputState {
|
|
leftStick: Vector2;
|
|
rightStick: Vector2;
|
|
}
|
|
|
|
export interface ForceApplicationResult {
|
|
linearMagnitude: number;
|
|
angularMagnitude: number;
|
|
}
|
|
|
|
/**
|
|
* Handles physics force calculations and application for the ship
|
|
* Reads physics parameters from GameConfig for runtime tuning
|
|
*/
|
|
export class ShipPhysics {
|
|
/**
|
|
* Apply forces to the ship based on input state
|
|
* @param inputState - Current input state (stick positions)
|
|
* @param physicsBody - Physics body to apply forces to
|
|
* @param transformNode - Transform node for world space calculations
|
|
* @returns Force magnitudes for audio feedback
|
|
*/
|
|
public applyForces(
|
|
inputState: InputState,
|
|
physicsBody: PhysicsBody,
|
|
transformNode: TransformNode
|
|
): ForceApplicationResult {
|
|
if (!physicsBody) {
|
|
return { linearMagnitude: 0, angularMagnitude: 0 };
|
|
}
|
|
|
|
const { leftStick, rightStick } = inputState;
|
|
|
|
// Get physics config
|
|
const config = GameConfig.getInstance().shipPhysics;
|
|
|
|
// Get current velocities for velocity cap checks
|
|
const currentLinearVelocity = physicsBody.getLinearVelocity();
|
|
const currentAngularVelocity = physicsBody.getAngularVelocity();
|
|
const currentSpeed = currentLinearVelocity.length();
|
|
|
|
let linearMagnitude = 0;
|
|
let angularMagnitude = 0;
|
|
|
|
// Apply linear force from left stick Y (forward/backward)
|
|
if (Math.abs(leftStick.y) > 0.1) {
|
|
linearMagnitude = Math.abs(leftStick.y);
|
|
|
|
// Only apply force if we haven't reached max velocity
|
|
if (currentSpeed < config.maxLinearVelocity) {
|
|
// Get local direction (Z-axis for forward/backward thrust)
|
|
const localDirection = new Vector3(0, 0, -leftStick.y);
|
|
// Transform to world space
|
|
const worldDirection = Vector3.TransformNormal(
|
|
localDirection,
|
|
transformNode.getWorldMatrix()
|
|
);
|
|
const force = worldDirection.scale(config.linearForceMultiplier);
|
|
|
|
// Calculate thrust point: center of mass + offset (0, 1, 0) in world space
|
|
const thrustPoint = Vector3.TransformCoordinates(
|
|
physicsBody.getMassProperties().centerOfMass.add(new Vector3(0, 1, 0)),
|
|
transformNode.getWorldMatrix()
|
|
);
|
|
|
|
physicsBody.applyForce(force, thrustPoint);
|
|
}
|
|
}
|
|
|
|
// Calculate rotation magnitude for torque
|
|
angularMagnitude =
|
|
Math.abs(rightStick.y) +
|
|
Math.abs(rightStick.x) +
|
|
Math.abs(leftStick.x);
|
|
|
|
// Apply angular forces if any stick has significant rotation input
|
|
if (angularMagnitude > 0.1) {
|
|
const currentAngularSpeed = currentAngularVelocity.length();
|
|
|
|
// Only apply torque if we haven't reached max angular velocity
|
|
if (currentAngularSpeed < config.maxAngularVelocity) {
|
|
const yaw = -leftStick.x;
|
|
const pitch = rightStick.y;
|
|
const roll = rightStick.x;
|
|
|
|
// Create torque in local space, then transform to world space
|
|
const localTorque = new Vector3(pitch, yaw, roll).scale(
|
|
config.angularForceMultiplier
|
|
);
|
|
const worldTorque = Vector3.TransformNormal(
|
|
localTorque,
|
|
transformNode.getWorldMatrix()
|
|
);
|
|
|
|
physicsBody.applyAngularImpulse(worldTorque);
|
|
}
|
|
}
|
|
|
|
return {
|
|
linearMagnitude,
|
|
angularMagnitude,
|
|
};
|
|
}
|
|
}
|