space-game/src/sight.ts
Michael Mainguy f11005fdb6
All checks were successful
Build / build (push) Successful in 1m19s
Refactor scoreboard to use GLB mesh and improve ship mechanics
- Move scoreboard ownership from Level1 to Ship class for better encapsulation
- Refactor scoreboard.initialize() to accept GLB submesh (Screen material mesh)
- Dispose original material when applying AdvancedDynamicTexture to mesh
- Change scoreboard background to green for visibility testing
- Increase ship velocities: MAX_LINEAR_VELOCITY to 200, LINEAR_FORCE_MULTIPLIER to 1200
- Adjust ammo spawn position (y: 0.5, z: 7.1) and velocity (200000)
- Update sight reticle position to match new ammo spawn point (y: 0.5)
- Fix sight circle rotation and rendering group assignment
- Update ship2.glb, ship1.glb, and base.glb models
- Comment out ship position override in level initialization

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 13:41:17 -06:00

190 lines
5.8 KiB
TypeScript

import {
Color3,
Mesh,
MeshBuilder,
Scene,
StandardMaterial,
TransformNode,
Vector3
} from "@babylonjs/core";
/**
* Configuration options for the sight reticle
*/
export interface SightConfig {
/** Position relative to parent */
position?: Vector3;
/** Circle radius */
circleRadius?: number;
/** Crosshair line length */
crosshairLength?: number;
/** Line thickness */
lineThickness?: number;
/** Reticle color */
color?: Color3;
/** Rendering group ID */
renderingGroupId?: number;
/** Gap size in the center of the crosshair */
centerGap?: number;
}
/**
* Gun sight reticle with crosshair and circle
*/
export class Sight {
private reticleGroup: TransformNode;
private circle: Mesh;
private crosshairLines: Mesh[] = [];
private scene: Scene;
private config: Required<SightConfig>;
// Default configuration
private static readonly DEFAULT_CONFIG: Required<SightConfig> = {
position: new Vector3(0, 2, 125),
circleRadius: 2,
crosshairLength: 1.5,
lineThickness: 0.1,
color: Color3.Green(),
renderingGroupId: 3,
centerGap: 0.5
};
constructor(scene: Scene, parent: TransformNode, config?: SightConfig) {
this.scene = scene;
this.config = { ...Sight.DEFAULT_CONFIG, ...config };
this.createReticle(parent);
}
/**
* Create the reticle (circle + crosshair)
*/
private createReticle(parent: TransformNode): void {
// Create a parent node for the entire reticle
this.reticleGroup = new TransformNode("sightReticle", this.scene);
this.reticleGroup.parent = parent;
this.reticleGroup.position = this.config.position;
// Create material
const material = new StandardMaterial("sightMaterial", this.scene);
material.emissiveColor = this.config.color;
material.disableLighting = true;
material.alpha = 0.8;
// Create outer circle
this.circle = MeshBuilder.CreateTorus("sightCircle", {
diameter: this.config.circleRadius * 2,
thickness: this.config.lineThickness,
tessellation: 64
}, this.scene);
this.circle.parent = this.reticleGroup;
this.circle.rotation.x = -Math.PI / 2;
this.circle.material = material;
this.circle.renderingGroupId = this.config.renderingGroupId;
// Create crosshair lines (4 lines extending from center gap)
this.createCrosshairLines(material);
}
/**
* Create the crosshair lines (top, bottom, left, right)
*/
private createCrosshairLines(material: StandardMaterial): void {
const gap = this.config.centerGap;
const length = this.config.crosshairLength;
const thickness = this.config.lineThickness;
// Top line
const topLine = MeshBuilder.CreateBox("crosshairTop", {
width: thickness,
height: length,
depth: thickness
}, this.scene);
topLine.parent = this.reticleGroup;
topLine.position.y = gap + length / 2;
topLine.material = material;
// topLine.renderingGroupId = this.config.renderingGroupId;
this.crosshairLines.push(topLine);
// Bottom line
const bottomLine = MeshBuilder.CreateBox("crosshairBottom", {
width: thickness,
height: length,
depth: thickness
}, this.scene);
bottomLine.parent = this.reticleGroup;
bottomLine.position.y = -(gap + length / 2);
bottomLine.material = material;
// bottomLine.renderingGroupId = this.config.renderingGroupId;
this.crosshairLines.push(bottomLine);
// Left line
const leftLine = MeshBuilder.CreateBox("crosshairLeft", {
width: length,
height: thickness,
depth: thickness
}, this.scene);
leftLine.parent = this.reticleGroup;
leftLine.position.x = -(gap + length / 2);
leftLine.material = material;
// leftLine.renderingGroupId = this.config.renderingGroupId;
this.crosshairLines.push(leftLine);
// Right line
const rightLine = MeshBuilder.CreateBox("crosshairRight", {
width: length,
height: thickness,
depth: thickness
}, this.scene);
rightLine.parent = this.reticleGroup;
rightLine.position.x = gap + length / 2;
rightLine.material = material;
// rightLine.renderingGroupId = this.config.renderingGroupId;
this.crosshairLines.push(rightLine);
// Center dot (optional, very small)
const centerDot = MeshBuilder.CreateSphere("crosshairCenter", {
diameter: thickness * 1.5
}, this.scene);
centerDot.parent = this.reticleGroup;
centerDot.material = material;
// centerDot.renderingGroupId = this.config.renderingGroupId;
this.crosshairLines.push(centerDot);
}
/**
* Set visibility of the sight
*/
public setVisible(visible: boolean): void {
this.circle.isVisible = visible;
this.crosshairLines.forEach(line => line.isVisible = visible);
}
/**
* Change the sight color
*/
public setColor(color: Color3): void {
this.config.color = color;
const material = this.circle.material as StandardMaterial;
if (material) {
material.emissiveColor = color;
}
}
/**
* Get the reticle group transform node
*/
public getTransformNode(): TransformNode {
return this.reticleGroup;
}
/**
* Dispose of the sight
*/
public dispose(): void {
this.circle.dispose();
this.crosshairLines.forEach(line => line.dispose());
this.reticleGroup.dispose();
}
}