feat: migrate simulation to physically-based unit system
Replace arbitrary game units with real-world measurements throughout the simulation and viewer: planet orbits in AU, sizes in km, galaxy positions in light-years. Add SimulationUnits helpers for conversions, separate WarpSpeed from FtlSpeed for ships, fix FTL transit progress to use galaxy-space distances, overhaul Lagrange point placement with Hill sphere approximation, and update the viewer to scale and format all distances correctly. Ships in FTL transit now render in galaxy view. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import * as THREE from "three";
|
||||
import { ACTIVE_SYSTEM_CAPTURE_RADIUS, ACTIVE_SYSTEM_DETAIL_SCALE, GALAXY_PARALLAX_FACTOR } from "./viewerConstants";
|
||||
import { computePlanetLocalPosition, currentWorldTimeSeconds, toThreeVector } from "./viewerMath";
|
||||
import { KILOMETERS_PER_AU, computePlanetLocalPosition, currentWorldTimeSeconds, scaleGalaxyVector, scaleLocalVector, toThreeVector } from "./viewerMath";
|
||||
import { resolveSelectableSystemId } from "./viewerSelection";
|
||||
import type {
|
||||
BubbleVisual,
|
||||
@@ -13,6 +13,7 @@ import type {
|
||||
SpatialNodeVisual,
|
||||
StructureVisual,
|
||||
WorldState,
|
||||
ZoomLevel,
|
||||
} from "./viewerTypes";
|
||||
|
||||
interface ResolveSelectionPositionParams {
|
||||
@@ -75,6 +76,7 @@ export function updatePanFromKeyboard(
|
||||
keyState: Set<string>,
|
||||
orbitYaw: number,
|
||||
currentDistance: number,
|
||||
zoomLevel: ZoomLevel,
|
||||
activeSystemId: string | undefined,
|
||||
systemFocusLocal: THREE.Vector3,
|
||||
galaxyFocus: THREE.Vector3,
|
||||
@@ -103,12 +105,15 @@ export function updatePanFromKeyboard(
|
||||
const forward = new THREE.Vector3(Math.cos(orbitYaw), 0, Math.sin(orbitYaw));
|
||||
const right = new THREE.Vector3(-forward.z, 0, forward.x);
|
||||
const pan = right.multiplyScalar(move.x).add(forward.multiplyScalar(move.z));
|
||||
const speed = THREE.MathUtils.mapLinear(currentDistance, minimumDistance, maximumDistance, 320, 6800);
|
||||
if (activeSystemId) {
|
||||
systemFocusLocal.addScaledVector(pan, speed * delta);
|
||||
const speedKilometers = zoomLevel === "system"
|
||||
? THREE.MathUtils.mapLinear(currentDistance, 80, 4000, KILOMETERS_PER_AU * 0.002, KILOMETERS_PER_AU * 0.35)
|
||||
: THREE.MathUtils.mapLinear(currentDistance, minimumDistance, 120, 40, 180000);
|
||||
systemFocusLocal.addScaledVector(pan, speedKilometers * delta);
|
||||
return;
|
||||
}
|
||||
|
||||
const speed = THREE.MathUtils.mapLinear(currentDistance, minimumDistance, maximumDistance, 320, 6800);
|
||||
galaxyFocus.addScaledVector(pan, speed * delta);
|
||||
}
|
||||
|
||||
@@ -127,7 +132,14 @@ export function determineActiveSystemId(params: DetermineActiveSystemParams): st
|
||||
}
|
||||
|
||||
if (cameraMode === "follow" && cameraTargetShipId) {
|
||||
return world.ships.get(cameraTargetShipId)?.systemId;
|
||||
const followedShip = world.ships.get(cameraTargetShipId);
|
||||
if (!followedShip) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return followedShip.spatialState.movementRegime === "ftl-transit"
|
||||
? undefined
|
||||
: followedShip.systemId;
|
||||
}
|
||||
|
||||
if (currentDistance >= 12000) {
|
||||
@@ -152,7 +164,7 @@ export function determineActiveSystemId(params: DetermineActiveSystemParams): st
|
||||
let nearestSystemId: string | undefined;
|
||||
let nearestDistance = Number.POSITIVE_INFINITY;
|
||||
for (const system of world.systems.values()) {
|
||||
const center = toThreeVector(system.galaxyPosition);
|
||||
const center = scaleGalaxyVector(toThreeVector(system.galaxyPosition));
|
||||
const distance = center.distanceTo(galaxyFocus);
|
||||
if (distance < nearestDistance) {
|
||||
nearestDistance = distance;
|
||||
@@ -222,7 +234,7 @@ export function resolveSelectionPosition(params: ResolveSelectionPositionParams)
|
||||
}
|
||||
|
||||
const system = world.systems.get(selection.id);
|
||||
return system ? toThreeVector(system.galaxyPosition) : undefined;
|
||||
return system ? scaleGalaxyVector(toThreeVector(system.galaxyPosition)) : undefined;
|
||||
}
|
||||
|
||||
export function focusOnSelection(params: FocusOnSelectionParams) {
|
||||
@@ -249,7 +261,7 @@ export function focusOnSelection(params: FocusOnSelectionParams) {
|
||||
if (selectionSystemId && world) {
|
||||
const system = world.systems.get(selectionSystemId);
|
||||
if (system) {
|
||||
galaxyFocus.copy(toThreeVector(system.galaxyPosition));
|
||||
galaxyFocus.copy(scaleGalaxyVector(toThreeVector(system.galaxyPosition)));
|
||||
systemFocusLocal.copy(nextFocus);
|
||||
return;
|
||||
}
|
||||
@@ -325,8 +337,8 @@ export function getCameraFocusWorldPosition(params: CameraFocusParams): THREE.Ve
|
||||
|
||||
const system = world.systems.get(activeSystemId);
|
||||
return system
|
||||
? toThreeVector(system.galaxyPosition).add(
|
||||
systemFocusLocal.clone().multiplyScalar(ACTIVE_SYSTEM_DETAIL_SCALE * GALAXY_PARALLAX_FACTOR),
|
||||
? scaleGalaxyVector(toThreeVector(system.galaxyPosition)).add(
|
||||
scaleLocalVector(systemFocusLocal).multiplyScalar(ACTIVE_SYSTEM_DETAIL_SCALE * GALAXY_PARALLAX_FACTOR),
|
||||
)
|
||||
: galaxyFocus;
|
||||
}
|
||||
@@ -341,18 +353,20 @@ export function toDisplayLocalPosition(params: DisplayLocalPositionParams): THRE
|
||||
} = params;
|
||||
|
||||
if (!world || !systemId) {
|
||||
return localPosition.clone();
|
||||
return scaleLocalVector(localPosition);
|
||||
}
|
||||
|
||||
const system = world.systems.get(systemId);
|
||||
if (!system) {
|
||||
return localPosition.clone();
|
||||
return scaleLocalVector(localPosition);
|
||||
}
|
||||
|
||||
const center = toThreeVector(system.galaxyPosition);
|
||||
const center = scaleGalaxyVector(toThreeVector(system.galaxyPosition));
|
||||
const scaledLocalPosition = scaleLocalVector(localPosition);
|
||||
const scaledSystemFocus = scaleLocalVector(systemFocusLocal);
|
||||
if (systemId !== activeSystemId) {
|
||||
return center.clone().add(localPosition);
|
||||
return center.clone().add(scaledLocalPosition);
|
||||
}
|
||||
|
||||
return center.clone().add(localPosition.clone().sub(systemFocusLocal).multiplyScalar(ACTIVE_SYSTEM_DETAIL_SCALE));
|
||||
return center.clone().add(scaledLocalPosition.sub(scaledSystemFocus).multiplyScalar(ACTIVE_SYSTEM_DETAIL_SCALE));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user