Make ship physics constants configurable via GameConfig with UI
All checks were successful
Build / build (push) Successful in 1m14s
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>
This commit is contained in:
parent
72573054dd
commit
0988805652
40
index.html
40
index.html
@ -349,6 +349,46 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Ship Physics Settings -->
|
||||||
|
<div class="section">
|
||||||
|
<h2>🚀 Ship Physics</h2>
|
||||||
|
<p style="color: #aaa; font-size: 0.9em; margin-bottom: 20px;">
|
||||||
|
Advanced tuning parameters for ship movement and handling. Adjust these to customize how the ship responds to controls.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="maxLinearVelocity">Max Linear Velocity</label>
|
||||||
|
<input type="number" id="maxLinearVelocity" value="200" step="10" min="50" max="1000">
|
||||||
|
<div class="help-text">
|
||||||
|
Maximum forward/backward speed of the ship. Higher values allow faster movement.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="maxAngularVelocity">Max Angular Velocity</label>
|
||||||
|
<input type="number" id="maxAngularVelocity" value="1.4" step="0.1" min="0.5" max="5.0">
|
||||||
|
<div class="help-text">
|
||||||
|
Maximum rotation speed of the ship. Higher values allow faster turning.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="linearForceMultiplier">Linear Force Multiplier</label>
|
||||||
|
<input type="number" id="linearForceMultiplier" value="800" step="50" min="100" max="3000">
|
||||||
|
<div class="help-text">
|
||||||
|
Acceleration power for forward/backward thrust. Higher values = faster acceleration.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="angularForceMultiplier">Angular Force Multiplier</label>
|
||||||
|
<input type="number" id="angularForceMultiplier" value="15" step="1" min="5" max="50">
|
||||||
|
<div class="help-text">
|
||||||
|
Torque power for rotation. Higher values = faster rotational acceleration.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Info Section -->
|
<!-- Info Section -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>ℹ️ Quality Level Guide</h2>
|
<h2>ℹ️ Quality Level Guide</h2>
|
||||||
|
|||||||
@ -9,6 +9,14 @@ export class GameConfig {
|
|||||||
// Physics settings
|
// Physics settings
|
||||||
public physicsEnabled: boolean = true;
|
public physicsEnabled: boolean = true;
|
||||||
|
|
||||||
|
// Ship physics tuning parameters
|
||||||
|
public shipPhysics = {
|
||||||
|
maxLinearVelocity: 200,
|
||||||
|
maxAngularVelocity: 1.4,
|
||||||
|
linearForceMultiplier: 800,
|
||||||
|
angularForceMultiplier: 15
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private constructor for singleton pattern
|
* Private constructor for singleton pattern
|
||||||
*/
|
*/
|
||||||
@ -33,7 +41,8 @@ export class GameConfig {
|
|||||||
public save(): void {
|
public save(): void {
|
||||||
const config = {
|
const config = {
|
||||||
physicsEnabled: this.physicsEnabled,
|
physicsEnabled: this.physicsEnabled,
|
||||||
debug: this.debug
|
debug: this.debug,
|
||||||
|
shipPhysics: this.shipPhysics
|
||||||
};
|
};
|
||||||
localStorage.setItem('game-config', JSON.stringify(config));
|
localStorage.setItem('game-config', JSON.stringify(config));
|
||||||
}
|
}
|
||||||
@ -48,6 +57,16 @@ export class GameConfig {
|
|||||||
const config = JSON.parse(stored);
|
const config = JSON.parse(stored);
|
||||||
this.physicsEnabled = config.physicsEnabled ?? true;
|
this.physicsEnabled = config.physicsEnabled ?? true;
|
||||||
this.debug = config.debug ?? false;
|
this.debug = config.debug ?? false;
|
||||||
|
|
||||||
|
// Load ship physics with fallback to defaults
|
||||||
|
if (config.shipPhysics) {
|
||||||
|
this.shipPhysics = {
|
||||||
|
maxLinearVelocity: config.shipPhysics.maxLinearVelocity ?? 200,
|
||||||
|
maxAngularVelocity: config.shipPhysics.maxAngularVelocity ?? 1.4,
|
||||||
|
linearForceMultiplier: config.shipPhysics.linearForceMultiplier ?? 800,
|
||||||
|
angularForceMultiplier: config.shipPhysics.angularForceMultiplier ?? 15
|
||||||
|
};
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
@ -62,6 +81,12 @@ export class GameConfig {
|
|||||||
public reset(): void {
|
public reset(): void {
|
||||||
this.physicsEnabled = true;
|
this.physicsEnabled = true;
|
||||||
this.debug = false;
|
this.debug = false;
|
||||||
|
this.shipPhysics = {
|
||||||
|
maxLinearVelocity: 200,
|
||||||
|
maxAngularVelocity: 1.4,
|
||||||
|
linearForceMultiplier: 800,
|
||||||
|
angularForceMultiplier: 15
|
||||||
|
};
|
||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,12 @@ export function initializeSettingsScreen(): void {
|
|||||||
const physicsEnabledCheckbox = document.getElementById('physicsEnabled') as HTMLInputElement;
|
const physicsEnabledCheckbox = document.getElementById('physicsEnabled') as HTMLInputElement;
|
||||||
const debugEnabledCheckbox = document.getElementById('debugEnabled') as HTMLInputElement;
|
const debugEnabledCheckbox = document.getElementById('debugEnabled') as HTMLInputElement;
|
||||||
|
|
||||||
|
// Ship physics inputs
|
||||||
|
const maxLinearVelocityInput = document.getElementById('maxLinearVelocity') as HTMLInputElement;
|
||||||
|
const maxAngularVelocityInput = document.getElementById('maxAngularVelocity') as HTMLInputElement;
|
||||||
|
const linearForceMultiplierInput = document.getElementById('linearForceMultiplier') as HTMLInputElement;
|
||||||
|
const angularForceMultiplierInput = document.getElementById('angularForceMultiplier') as HTMLInputElement;
|
||||||
|
|
||||||
const saveBtn = document.getElementById('saveSettingsBtn');
|
const saveBtn = document.getElementById('saveSettingsBtn');
|
||||||
const resetBtn = document.getElementById('resetSettingsBtn');
|
const resetBtn = document.getElementById('resetSettingsBtn');
|
||||||
const messageDiv = document.getElementById('settingsMessage');
|
const messageDiv = document.getElementById('settingsMessage');
|
||||||
@ -38,6 +44,12 @@ export function initializeSettingsScreen(): void {
|
|||||||
function loadSettings(): void {
|
function loadSettings(): void {
|
||||||
if (physicsEnabledCheckbox) physicsEnabledCheckbox.checked = config.physicsEnabled;
|
if (physicsEnabledCheckbox) physicsEnabledCheckbox.checked = config.physicsEnabled;
|
||||||
if (debugEnabledCheckbox) debugEnabledCheckbox.checked = config.debug;
|
if (debugEnabledCheckbox) debugEnabledCheckbox.checked = config.debug;
|
||||||
|
|
||||||
|
// Load ship physics settings
|
||||||
|
if (maxLinearVelocityInput) maxLinearVelocityInput.value = config.shipPhysics.maxLinearVelocity.toString();
|
||||||
|
if (maxAngularVelocityInput) maxAngularVelocityInput.value = config.shipPhysics.maxAngularVelocity.toString();
|
||||||
|
if (linearForceMultiplierInput) linearForceMultiplierInput.value = config.shipPhysics.linearForceMultiplier.toString();
|
||||||
|
if (angularForceMultiplierInput) angularForceMultiplierInput.value = config.shipPhysics.angularForceMultiplier.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,6 +58,13 @@ export function initializeSettingsScreen(): void {
|
|||||||
function saveSettings(): void {
|
function saveSettings(): void {
|
||||||
config.physicsEnabled = physicsEnabledCheckbox.checked;
|
config.physicsEnabled = physicsEnabledCheckbox.checked;
|
||||||
config.debug = debugEnabledCheckbox.checked;
|
config.debug = debugEnabledCheckbox.checked;
|
||||||
|
|
||||||
|
// Save ship physics settings
|
||||||
|
config.shipPhysics.maxLinearVelocity = parseFloat(maxLinearVelocityInput.value);
|
||||||
|
config.shipPhysics.maxAngularVelocity = parseFloat(maxAngularVelocityInput.value);
|
||||||
|
config.shipPhysics.linearForceMultiplier = parseFloat(linearForceMultiplierInput.value);
|
||||||
|
config.shipPhysics.angularForceMultiplier = parseFloat(angularForceMultiplierInput.value);
|
||||||
|
|
||||||
config.save();
|
config.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,5 @@
|
|||||||
import { PhysicsBody, TransformNode, Vector2, Vector3 } from "@babylonjs/core";
|
import { PhysicsBody, TransformNode, Vector2, Vector3 } from "@babylonjs/core";
|
||||||
|
import { GameConfig } from "./gameConfig";
|
||||||
// Physics constants
|
|
||||||
const MAX_LINEAR_VELOCITY = 200;
|
|
||||||
const MAX_ANGULAR_VELOCITY = 1.4;
|
|
||||||
const LINEAR_FORCE_MULTIPLIER = 800;
|
|
||||||
const ANGULAR_FORCE_MULTIPLIER = 15;
|
|
||||||
|
|
||||||
export interface InputState {
|
export interface InputState {
|
||||||
leftStick: Vector2;
|
leftStick: Vector2;
|
||||||
@ -18,7 +13,7 @@ export interface ForceApplicationResult {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles physics force calculations and application for the ship
|
* Handles physics force calculations and application for the ship
|
||||||
* Pure calculation logic with no external dependencies
|
* Reads physics parameters from GameConfig for runtime tuning
|
||||||
*/
|
*/
|
||||||
export class ShipPhysics {
|
export class ShipPhysics {
|
||||||
/**
|
/**
|
||||||
@ -39,6 +34,9 @@ export class ShipPhysics {
|
|||||||
|
|
||||||
const { leftStick, rightStick } = inputState;
|
const { leftStick, rightStick } = inputState;
|
||||||
|
|
||||||
|
// Get physics config
|
||||||
|
const config = GameConfig.getInstance().shipPhysics;
|
||||||
|
|
||||||
// Get current velocities for velocity cap checks
|
// Get current velocities for velocity cap checks
|
||||||
const currentLinearVelocity = physicsBody.getLinearVelocity();
|
const currentLinearVelocity = physicsBody.getLinearVelocity();
|
||||||
const currentAngularVelocity = physicsBody.getAngularVelocity();
|
const currentAngularVelocity = physicsBody.getAngularVelocity();
|
||||||
@ -52,7 +50,7 @@ export class ShipPhysics {
|
|||||||
linearMagnitude = Math.abs(leftStick.y);
|
linearMagnitude = Math.abs(leftStick.y);
|
||||||
|
|
||||||
// Only apply force if we haven't reached max velocity
|
// Only apply force if we haven't reached max velocity
|
||||||
if (currentSpeed < MAX_LINEAR_VELOCITY) {
|
if (currentSpeed < config.maxLinearVelocity) {
|
||||||
// Get local direction (Z-axis for forward/backward thrust)
|
// Get local direction (Z-axis for forward/backward thrust)
|
||||||
const localDirection = new Vector3(0, 0, -leftStick.y);
|
const localDirection = new Vector3(0, 0, -leftStick.y);
|
||||||
// Transform to world space
|
// Transform to world space
|
||||||
@ -60,7 +58,7 @@ export class ShipPhysics {
|
|||||||
localDirection,
|
localDirection,
|
||||||
transformNode.getWorldMatrix()
|
transformNode.getWorldMatrix()
|
||||||
);
|
);
|
||||||
const force = worldDirection.scale(LINEAR_FORCE_MULTIPLIER);
|
const force = worldDirection.scale(config.linearForceMultiplier);
|
||||||
|
|
||||||
// Calculate thrust point: center of mass + offset (0, 1, 0) in world space
|
// Calculate thrust point: center of mass + offset (0, 1, 0) in world space
|
||||||
const thrustPoint = Vector3.TransformCoordinates(
|
const thrustPoint = Vector3.TransformCoordinates(
|
||||||
@ -83,14 +81,14 @@ export class ShipPhysics {
|
|||||||
const currentAngularSpeed = currentAngularVelocity.length();
|
const currentAngularSpeed = currentAngularVelocity.length();
|
||||||
|
|
||||||
// Only apply torque if we haven't reached max angular velocity
|
// Only apply torque if we haven't reached max angular velocity
|
||||||
if (currentAngularSpeed < MAX_ANGULAR_VELOCITY) {
|
if (currentAngularSpeed < config.maxAngularVelocity) {
|
||||||
const yaw = -leftStick.x;
|
const yaw = -leftStick.x;
|
||||||
const pitch = rightStick.y;
|
const pitch = rightStick.y;
|
||||||
const roll = rightStick.x;
|
const roll = rightStick.x;
|
||||||
|
|
||||||
// Create torque in local space, then transform to world space
|
// Create torque in local space, then transform to world space
|
||||||
const localTorque = new Vector3(pitch, yaw, roll).scale(
|
const localTorque = new Vector3(pitch, yaw, roll).scale(
|
||||||
ANGULAR_FORCE_MULTIPLIER
|
config.angularForceMultiplier
|
||||||
);
|
);
|
||||||
const worldTorque = Vector3.TransformNormal(
|
const worldTorque = Vector3.TransformNormal(
|
||||||
localTorque,
|
localTorque,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user