Refactor sight to crosshair reticle and rename starfield to rockFactory
Some checks failed
Build / build (push) Failing after 19s
Some checks failed
Build / build (push) Failing after 19s
Created new Sight class: - Crosshair design with circle, lines, and center dot - Configurable radius, line length, thickness, and colors - Green reticle color (traditional gun sight) - Center gap and proper rendering group - Dispose method for cleanup Refactored Ship class: - Replaced disc sight with new Sight class - Changed ammo mesh to IcoSphere for better performance - Added dispose method to clean up sight resources - Integrated sight with configuration options Renamed starfield.ts to rockFactory.ts: - Better reflects the class purpose (RockFactory) - Updated all imports across codebase - Updated CLAUDE.md documentation Updated asteroid model: - Changed from asteroid2.glb to asteroid3.glb - Added new asteroid3.blend and asteroid3.glb assets Fixed background stars material: - Added proper material type casting for StandardMaterial - Fixed emissiveColor setting 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
9df64b7dd9
commit
cb96b4ea6c
@ -119,7 +119,7 @@ src/
|
||||
level.ts - Level interface
|
||||
level1.ts - Main game level implementation
|
||||
ship.ts - Player ship, controls, weapons
|
||||
starfield.ts - Rock factory and collision handling
|
||||
rockFactory.ts - Rock factory and collision handling
|
||||
scoreboard.ts - In-cockpit HUD display
|
||||
createSun.ts - Sun mesh generation
|
||||
createPlanets.ts - Procedural planet generation
|
||||
|
||||
BIN
public/asteroid3.blend
Normal file
BIN
public/asteroid3.blend
Normal file
Binary file not shown.
BIN
public/asteroid3.glb
Normal file
BIN
public/asteroid3.glb
Normal file
Binary file not shown.
@ -1,4 +1,4 @@
|
||||
import {Color3, Color4, PointsCloudSystem, Scene, Vector3} from "@babylonjs/core";
|
||||
import {Color3, Color4, PointsCloudSystem, Scene, StandardMaterial, Vector3} from "@babylonjs/core";
|
||||
|
||||
/**
|
||||
* Configuration options for background stars
|
||||
@ -95,11 +95,12 @@ export class BackgroundStars {
|
||||
const mesh = this.pcs.mesh;
|
||||
if (mesh) {
|
||||
// Stars should not receive lighting
|
||||
mesh.material.disableLighting = true;
|
||||
mesh.material.emissiveColor = new Color3(1,1,1);
|
||||
const mat = (mesh.material as StandardMaterial)
|
||||
mat.disableLighting = true;
|
||||
mat.emissiveColor = new Color3(1,1,1);
|
||||
|
||||
// Disable depth write so stars don't occlude other objects
|
||||
mesh.material.disableDepthWrite = true;
|
||||
mat.disableDepthWrite = true;
|
||||
|
||||
// Stars should be in the background
|
||||
mesh.renderingGroupId = 0;
|
||||
|
||||
@ -14,7 +14,7 @@ import {
|
||||
import type {AudioEngineV2} from "@babylonjs/core";
|
||||
import {Ship} from "./ship";
|
||||
|
||||
import {RockFactory} from "./starfield";
|
||||
import {RockFactory} from "./rockFactory";
|
||||
import Level from "./level";
|
||||
import {Scoreboard} from "./scoreboard";
|
||||
import setLoadingMessage from "./setLoadingMessage";
|
||||
|
||||
@ -13,7 +13,7 @@ import {
|
||||
Vector3
|
||||
} from "@babylonjs/core";
|
||||
import { DefaultScene } from "./defaultScene";
|
||||
import { RockFactory } from "./starfield";
|
||||
import { RockFactory } from "./rockFactory";
|
||||
import { ScoreEvent } from "./scoreboard";
|
||||
import {
|
||||
LevelConfig,
|
||||
|
||||
@ -19,7 +19,7 @@ import {Level1} from "./level1";
|
||||
import Demo from "./demo";
|
||||
import Level from "./level";
|
||||
import setLoadingMessage from "./setLoadingMessage";
|
||||
import {RockFactory} from "./starfield";
|
||||
import {RockFactory} from "./rockFactory";
|
||||
import {ControllerDebug} from "./controllerDebug";
|
||||
import {router, showView} from "./router";
|
||||
import {populateLevelSelector, hasSavedLevels} from "./levelSelector";
|
||||
|
||||
@ -59,7 +59,7 @@ export class RockFactory {
|
||||
}
|
||||
private static async loadMesh() {
|
||||
console.log('loading mesh');
|
||||
const importMesh = await SceneLoader.ImportMeshAsync(null, "./", "asteroid2.glb", DefaultScene.MainScene);
|
||||
const importMesh = await SceneLoader.ImportMeshAsync(null, "./", "asteroid3.glb", DefaultScene.MainScene);
|
||||
this._rockMesh = importMesh.meshes[1].clone("asteroid", null, false);
|
||||
this._rockMesh.setParent(null);
|
||||
this._rockMesh.setEnabled(false);
|
||||
34
src/ship.ts
34
src/ship.ts
@ -22,6 +22,7 @@ import {
|
||||
import type {AudioEngineV2, StaticSound} from "@babylonjs/core";
|
||||
import {DefaultScene} from "./defaultScene";
|
||||
import { GameConfig } from "./gameConfig";
|
||||
import { Sight } from "./sight";
|
||||
const MAX_FORWARD_THRUST = 40;
|
||||
|
||||
const controllerComponents = [
|
||||
@ -69,6 +70,7 @@ export class Ship {
|
||||
private _controllerMode: ControllerStickMode;
|
||||
private _active = false;
|
||||
private _audioEngine: AudioEngineV2;
|
||||
private _sight: Sight;
|
||||
constructor(mode: ControllerStickMode = ControllerStickMode.BEGINNER, audioEngine?: AudioEngineV2) {
|
||||
this._controllerMode = mode;
|
||||
this._audioEngine = audioEngine;
|
||||
@ -154,7 +156,7 @@ export class Ship {
|
||||
}
|
||||
this._ammoMaterial = new StandardMaterial("ammoMaterial", DefaultScene.MainScene);
|
||||
this._ammoMaterial.emissiveColor = new Color3(1, 1, 0);
|
||||
this._ammoBaseMesh = MeshBuilder.CreateSphere("bullet", {diameter: .2}, DefaultScene.MainScene);
|
||||
this._ammoBaseMesh = MeshBuilder.CreateIcoSphere("bullet", {radius: .1, subdivisions: 2}, DefaultScene.MainScene);
|
||||
this._ammoBaseMesh.material = this._ammoMaterial;
|
||||
this._ammoBaseMesh.setEnabled(false);
|
||||
|
||||
@ -180,17 +182,17 @@ export class Ship {
|
||||
|
||||
DefaultScene.MainScene.setActiveCameraByName("Flat Camera");
|
||||
|
||||
//const sightPos = this._forwardNode.position.scale(30);
|
||||
const sight = MeshBuilder.CreateDisc("sight", {radius: 2 }, DefaultScene.MainScene);
|
||||
// Create sight reticle
|
||||
this._sight = new Sight(DefaultScene.MainScene, this._ship, {
|
||||
position: new Vector3(0, 2, 125),
|
||||
circleRadius: 2,
|
||||
crosshairLength: 1.5,
|
||||
lineThickness: 0.1,
|
||||
color: Color3.Green(),
|
||||
renderingGroupId: 3,
|
||||
centerGap: 0.5
|
||||
});
|
||||
|
||||
sight.parent = this._ship
|
||||
//sight.rotation.x = -Math.PI / 2;
|
||||
const signtMaterial = new StandardMaterial("sightMaterial", DefaultScene.MainScene);
|
||||
signtMaterial.emissiveColor = Color3.Yellow();
|
||||
signtMaterial.ambientColor = Color3.Yellow();
|
||||
sight.material = signtMaterial;
|
||||
sight.position = new Vector3(0, 2, 125);
|
||||
sight.renderingGroupId = 3;
|
||||
let i = 0;
|
||||
DefaultScene.MainScene.onBeforeRenderObservable.add(() => {
|
||||
if (i++ % 10 == 0) {
|
||||
@ -527,6 +529,16 @@ export class Ship {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose of ship resources
|
||||
*/
|
||||
public dispose(): void {
|
||||
if (this._sight) {
|
||||
this._sight.dispose();
|
||||
}
|
||||
// Add other cleanup as needed
|
||||
}
|
||||
}
|
||||
function decrementValue(value: number, increment: number = .8): number {
|
||||
if (Math.abs(value) < .01) {
|
||||
|
||||
188
src/sight.ts
Normal file
188
src/sight.ts
Normal file
@ -0,0 +1,188 @@
|
||||
|
||||
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.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();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user