Fix position export and add target physics improvements
All checks were successful
Build / build (push) Successful in 2m0s
All checks were successful
Build / build (push) Successful in 2m0s
- Use getAbsolutePosition() in all config builders for correct world coords - Add TargetComponent case to meshCollector for target export - Add shared target body cache in RockFactory (asteroids can share targets) - Add debug logging throughout export and physics pipelines - Pass targetId to RockFactory for proper target body sharing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
4c9e1f65c0
commit
96bc3df51e
@ -14,9 +14,19 @@ function buildSingleAsteroid(mesh: AbstractMesh, index: number): AsteroidConfig
|
|||||||
const rotation = toVector3Array(mesh.rotation);
|
const rotation = toVector3Array(mesh.rotation);
|
||||||
const hasRotation = rotation[0] !== 0 || rotation[1] !== 0 || rotation[2] !== 0;
|
const hasRotation = rotation[0] !== 0 || rotation[1] !== 0 || rotation[2] !== 0;
|
||||||
|
|
||||||
|
// Debug: compare local vs absolute position
|
||||||
|
const localPos = mesh.position;
|
||||||
|
const absPos = mesh.getAbsolutePosition();
|
||||||
|
if (Math.abs(localPos.x - absPos.x) > 1 || Math.abs(localPos.y - absPos.y) > 1 || Math.abs(localPos.z - absPos.z) > 1) {
|
||||||
|
console.warn(`[AsteroidBuilder] Position mismatch for ${mesh.name}:`,
|
||||||
|
`local=(${localPos.x.toFixed(1)}, ${localPos.y.toFixed(1)}, ${localPos.z.toFixed(1)})`,
|
||||||
|
`absolute=(${absPos.x.toFixed(1)}, ${absPos.y.toFixed(1)}, ${absPos.z.toFixed(1)})`,
|
||||||
|
`parent=${mesh.parent?.name || 'none'}`);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: mesh.name || `asteroid-${index}`,
|
id: mesh.name || `asteroid-${index}`,
|
||||||
position: toVector3Array(mesh.position),
|
position: toVector3Array(mesh.getAbsolutePosition()), // Use absolute position
|
||||||
rotation: hasRotation ? rotation : undefined,
|
rotation: hasRotation ? rotation : undefined,
|
||||||
scale: mesh.scaling.x,
|
scale: mesh.scaling.x,
|
||||||
linearVelocity: extractVector3(script.linearVelocity, [0, 0, 0]),
|
linearVelocity: extractVector3(script.linearVelocity, [0, 0, 0]),
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export function buildBaseConfig(mesh: AbstractMesh | null): StartBaseConfig | un
|
|||||||
const hasRotation = rotation[0] !== 0 || rotation[1] !== 0 || rotation[2] !== 0;
|
const hasRotation = rotation[0] !== 0 || rotation[1] !== 0 || rotation[2] !== 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
position: toVector3Array(mesh.position),
|
position: toVector3Array(mesh.getAbsolutePosition()),
|
||||||
rotation: hasRotation ? rotation : undefined,
|
rotation: hasRotation ? rotation : undefined,
|
||||||
baseGlbPath: glbPath || undefined,
|
baseGlbPath: glbPath || undefined,
|
||||||
landingGlbPath: (script.landingGlbPath as string) || undefined,
|
landingGlbPath: (script.landingGlbPath as string) || undefined,
|
||||||
|
|||||||
@ -14,7 +14,7 @@ function buildSinglePlanet(mesh: AbstractMesh): PlanetConfig {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
name: mesh.name || "planet",
|
name: mesh.name || "planet",
|
||||||
position: toVector3Array(mesh.position),
|
position: toVector3Array(mesh.getAbsolutePosition()),
|
||||||
diameter: (script.diameter as number) ?? 100,
|
diameter: (script.diameter as number) ?? 100,
|
||||||
texturePath: (script.texturePath as string) || "planet_texture.jpg",
|
texturePath: (script.texturePath as string) || "planet_texture.jpg",
|
||||||
rotation: hasRotation(mesh) ? toVector3Array(mesh.rotation) : undefined
|
rotation: hasRotation(mesh) ? toVector3Array(mesh.rotation) : undefined
|
||||||
|
|||||||
@ -13,7 +13,7 @@ export function buildShipConfig(mesh: AbstractMesh | null): ShipConfig {
|
|||||||
const script = getScriptValues(mesh);
|
const script = getScriptValues(mesh);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
position: toVector3Array(mesh.position),
|
position: toVector3Array(mesh.getAbsolutePosition()),
|
||||||
rotation: mesh.rotation ? toVector3Array(mesh.rotation) : undefined,
|
rotation: mesh.rotation ? toVector3Array(mesh.rotation) : undefined,
|
||||||
linearVelocity: extractVector3OrUndefined(script.linearVelocity),
|
linearVelocity: extractVector3OrUndefined(script.linearVelocity),
|
||||||
angularVelocity: extractVector3OrUndefined(script.angularVelocity)
|
angularVelocity: extractVector3OrUndefined(script.angularVelocity)
|
||||||
|
|||||||
@ -21,7 +21,7 @@ export function buildSunConfig(mesh: AbstractMesh | null): SunConfig {
|
|||||||
const hasRotation = rotation[0] !== 0 || rotation[1] !== 0 || rotation[2] !== 0;
|
const hasRotation = rotation[0] !== 0 || rotation[1] !== 0 || rotation[2] !== 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
position: toVector3Array(mesh.position),
|
position: toVector3Array(mesh.getAbsolutePosition()),
|
||||||
rotation: hasRotation ? rotation : undefined,
|
rotation: hasRotation ? rotation : undefined,
|
||||||
diameter: (script.diameter as number) ?? 50,
|
diameter: (script.diameter as number) ?? 50,
|
||||||
intensity: (script.intensity as number) ?? 1000000,
|
intensity: (script.intensity as number) ?? 1000000,
|
||||||
|
|||||||
@ -15,7 +15,7 @@ function buildSingleTarget(node: TransformNode): TargetConfig {
|
|||||||
return {
|
return {
|
||||||
id: node.name || node.id,
|
id: node.name || node.id,
|
||||||
name: (script.displayName as string) || node.name || "Target",
|
name: (script.displayName as string) || node.name || "Target",
|
||||||
position: toVector3Array(node.position)
|
position: toVector3Array(node.getAbsolutePosition())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,11 @@ import { buildTargetConfigs } from "./configBuilders/targetBuilder";
|
|||||||
export function exportLevelConfig(scene: Scene): string {
|
export function exportLevelConfig(scene: Scene): string {
|
||||||
const meshes = collectMeshesByComponent(scene);
|
const meshes = collectMeshesByComponent(scene);
|
||||||
|
|
||||||
|
console.log(`[Exporter] Collected: ${meshes.asteroids.length} asteroids, ${meshes.targets.length} targets, ${meshes.planets.length} planets`);
|
||||||
|
if (meshes.targets.length > 0) {
|
||||||
|
console.log(`[Exporter] Target IDs: ${meshes.targets.map(t => t.name || t.id).join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
const config: LevelConfig = {
|
const config: LevelConfig = {
|
||||||
version: "1.0",
|
version: "1.0",
|
||||||
difficulty: "rookie",
|
difficulty: "rookie",
|
||||||
|
|||||||
@ -56,6 +56,10 @@ function categorizeByScript(
|
|||||||
case "BaseComponent":
|
case "BaseComponent":
|
||||||
result.base = mesh;
|
result.base = mesh;
|
||||||
break;
|
break;
|
||||||
|
case "TargetComponent":
|
||||||
|
// Targets can be Mesh or TransformNode - handle both
|
||||||
|
result.targets.push(mesh as unknown as TransformNode);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -64,6 +64,7 @@
|
|||||||
"geometryUniqueId": 1764787809875,
|
"geometryUniqueId": 1764787809875,
|
||||||
"geometryId": "76c7442d-fb7e-4a05-b1c5-9c27b0beb0dc",
|
"geometryId": "76c7442d-fb7e-4a05-b1c5-9c27b0beb0dc",
|
||||||
"subMeshes": null,
|
"subMeshes": null,
|
||||||
|
"materialUniqueId": 46,
|
||||||
"materialId": "default material",
|
"materialId": "default material",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"type": "Box",
|
"type": "Box",
|
||||||
@ -138,7 +139,108 @@
|
|||||||
"transformNodes": [],
|
"transformNodes": [],
|
||||||
"cameras": [],
|
"cameras": [],
|
||||||
"lights": [],
|
"lights": [],
|
||||||
"materials": [],
|
"materials": [
|
||||||
|
{
|
||||||
|
"tags": null,
|
||||||
|
"ambient": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"diffuse": [
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"specular": [
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"emissive": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"specularPower": 64,
|
||||||
|
"useAlphaFromDiffuseTexture": false,
|
||||||
|
"useEmissiveAsIllumination": false,
|
||||||
|
"linkEmissiveWithDiffuse": false,
|
||||||
|
"useSpecularOverAlpha": false,
|
||||||
|
"useReflectionOverAlpha": false,
|
||||||
|
"disableLighting": false,
|
||||||
|
"useObjectSpaceNormalMap": false,
|
||||||
|
"useParallax": false,
|
||||||
|
"useParallaxOcclusion": false,
|
||||||
|
"parallaxScaleBias": 0.05,
|
||||||
|
"roughness": 0,
|
||||||
|
"indexOfRefraction": 0.98,
|
||||||
|
"invertRefractionY": true,
|
||||||
|
"alphaCutOff": 0.4,
|
||||||
|
"useLightmapAsShadowmap": false,
|
||||||
|
"useReflectionFresnelFromSpecular": false,
|
||||||
|
"useGlossinessFromSpecularMapAlpha": false,
|
||||||
|
"maxSimultaneousLights": 32,
|
||||||
|
"invertNormalMapX": false,
|
||||||
|
"invertNormalMapY": false,
|
||||||
|
"twoSidedLighting": false,
|
||||||
|
"applyDecalMapAfterDetailMap": false,
|
||||||
|
"id": "default material",
|
||||||
|
"name": "default material",
|
||||||
|
"checkReadyOnEveryCall": false,
|
||||||
|
"checkReadyOnlyOnce": false,
|
||||||
|
"state": "",
|
||||||
|
"alpha": 1,
|
||||||
|
"backFaceCulling": true,
|
||||||
|
"cullBackFaces": true,
|
||||||
|
"_alphaMode": [
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"_needDepthPrePass": false,
|
||||||
|
"disableDepthWrite": false,
|
||||||
|
"disableColorWrite": false,
|
||||||
|
"forceDepthWrite": false,
|
||||||
|
"depthFunction": 0,
|
||||||
|
"separateCullingPass": false,
|
||||||
|
"fogEnabled": true,
|
||||||
|
"pointSize": 1,
|
||||||
|
"zOffset": 0,
|
||||||
|
"zOffsetUnits": 0,
|
||||||
|
"pointsCloud": false,
|
||||||
|
"fillMode": 0,
|
||||||
|
"_isVertexOutputInvariant": false,
|
||||||
|
"stencil": {
|
||||||
|
"tags": null,
|
||||||
|
"func": 519,
|
||||||
|
"backFunc": 519,
|
||||||
|
"funcRef": 1,
|
||||||
|
"funcMask": 255,
|
||||||
|
"opStencilFail": 7680,
|
||||||
|
"opDepthFail": 7680,
|
||||||
|
"opStencilDepthPass": 7681,
|
||||||
|
"backOpStencilFail": 7680,
|
||||||
|
"backOpDepthFail": 7680,
|
||||||
|
"backOpStencilDepthPass": 7681,
|
||||||
|
"mask": 255,
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"uniqueId": 46,
|
||||||
|
"plugins": {
|
||||||
|
"DetailMapConfiguration": {
|
||||||
|
"tags": null,
|
||||||
|
"diffuseBlendLevel": 1,
|
||||||
|
"roughnessBlendLevel": 1,
|
||||||
|
"bumpLevel": 1,
|
||||||
|
"normalBlendMethod": 0,
|
||||||
|
"isEnabled": false,
|
||||||
|
"name": "DetailMap",
|
||||||
|
"priority": 140,
|
||||||
|
"resolveIncludes": false,
|
||||||
|
"registerForExtraEvents": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"geometries": {
|
"geometries": {
|
||||||
"boxes": [],
|
"boxes": [],
|
||||||
"spheres": [],
|
"spheres": [],
|
||||||
|
|||||||
@ -109,146 +109,7 @@
|
|||||||
],
|
],
|
||||||
"parentId": 1764789858421
|
"parentId": 1764789858421
|
||||||
},
|
},
|
||||||
"instances": [
|
"instances": [],
|
||||||
{
|
|
||||||
"name": "Asteroid",
|
|
||||||
"id": "Asteroid",
|
|
||||||
"isEnabled": true,
|
|
||||||
"isVisible": true,
|
|
||||||
"isPickable": true,
|
|
||||||
"checkCollisions": false,
|
|
||||||
"position": [
|
|
||||||
66.2149304569587,
|
|
||||||
40.81207511231127,
|
|
||||||
-126.79009642287176
|
|
||||||
],
|
|
||||||
"scaling": [
|
|
||||||
5,
|
|
||||||
5,
|
|
||||||
5
|
|
||||||
],
|
|
||||||
"rotationQuaternion": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"metadata": {
|
|
||||||
"scripts": [
|
|
||||||
{
|
|
||||||
"key": "scripts/editorScripts/AsteroidComponent.ts",
|
|
||||||
"enabled": true,
|
|
||||||
"values": {
|
|
||||||
"linearVelocity": {
|
|
||||||
"type": "vector3",
|
|
||||||
"value": [
|
|
||||||
2,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"angularVelocity": {
|
|
||||||
"type": "vector3",
|
|
||||||
"value": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"mass": {
|
|
||||||
"type": "number",
|
|
||||||
"value": 1
|
|
||||||
},
|
|
||||||
"targetId": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Reference to a TargetComponent node",
|
|
||||||
"value": ""
|
|
||||||
},
|
|
||||||
"targetMode": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "orbit | moveToward | (empty)",
|
|
||||||
"value": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_id": "74563a74-be80-46fe-8dc3-189b03247c20"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parentId": 1764789858421
|
|
||||||
},
|
|
||||||
"animations": [],
|
|
||||||
"ranges": [],
|
|
||||||
"uniqueId": 71
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Asteroid",
|
|
||||||
"id": "Asteroid",
|
|
||||||
"isEnabled": true,
|
|
||||||
"isVisible": true,
|
|
||||||
"isPickable": true,
|
|
||||||
"checkCollisions": false,
|
|
||||||
"position": [
|
|
||||||
0,
|
|
||||||
-22.646529278627046,
|
|
||||||
-74.97825372352042
|
|
||||||
],
|
|
||||||
"scaling": [
|
|
||||||
5,
|
|
||||||
5,
|
|
||||||
5
|
|
||||||
],
|
|
||||||
"rotationQuaternion": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"metadata": {
|
|
||||||
"scripts": [
|
|
||||||
{
|
|
||||||
"key": "scripts/editorScripts/AsteroidComponent.ts",
|
|
||||||
"enabled": true,
|
|
||||||
"values": {
|
|
||||||
"linearVelocity": {
|
|
||||||
"type": "vector3",
|
|
||||||
"value": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
-2
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"angularVelocity": {
|
|
||||||
"type": "vector3",
|
|
||||||
"value": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"mass": {
|
|
||||||
"type": "number",
|
|
||||||
"value": 1
|
|
||||||
},
|
|
||||||
"targetId": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Reference to a TargetComponent node",
|
|
||||||
"value": ""
|
|
||||||
},
|
|
||||||
"targetMode": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "orbit | moveToward | (empty)",
|
|
||||||
"value": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_id": "79ae3cbb-7653-409c-9d0a-3e0b254e6731"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parentId": 1764789858421
|
|
||||||
},
|
|
||||||
"animations": [],
|
|
||||||
"ranges": [],
|
|
||||||
"uniqueId": 71
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"animations": [],
|
"animations": [],
|
||||||
"ranges": [],
|
"ranges": [],
|
||||||
"layerMask": 268435455,
|
"layerMask": 268435455,
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 41 KiB |
File diff suppressed because one or more lines are too long
@ -5,13 +5,13 @@
|
|||||||
|
|
||||||
import { loadScene, scriptsDictionary, scriptAssetsCache, _applyScriptsForObject, _removeRegisteredScriptInstance, _preloadScriptsAssets } from "babylonjs-editor-tools";
|
import { loadScene, scriptsDictionary, scriptAssetsCache, _applyScriptsForObject, _removeRegisteredScriptInstance, _preloadScriptsAssets } from "babylonjs-editor-tools";
|
||||||
import * as scripts_editorScripts_AsteroidComponent from "./scripts/editorScripts/AsteroidComponent";
|
import * as scripts_editorScripts_AsteroidComponent from "./scripts/editorScripts/AsteroidComponent";
|
||||||
import * as scripts_editorScripts_ShipComponent from "./scripts/editorScripts/ShipComponent";
|
|
||||||
import * as scripts_editorScripts_BaseComponent from "./scripts/editorScripts/BaseComponent";
|
import * as scripts_editorScripts_BaseComponent from "./scripts/editorScripts/BaseComponent";
|
||||||
|
import * as scripts_editorScripts_ShipComponent from "./scripts/editorScripts/ShipComponent";
|
||||||
|
|
||||||
export const scriptsMap = {
|
export const scriptsMap = {
|
||||||
"scripts/editorScripts/AsteroidComponent.ts": scripts_editorScripts_AsteroidComponent,
|
"scripts/editorScripts/AsteroidComponent.ts": scripts_editorScripts_AsteroidComponent,
|
||||||
"scripts/editorScripts/ShipComponent.ts": scripts_editorScripts_ShipComponent,
|
"scripts/editorScripts/BaseComponent.ts": scripts_editorScripts_BaseComponent,
|
||||||
"scripts/editorScripts/BaseComponent.ts": scripts_editorScripts_BaseComponent
|
"scripts/editorScripts/ShipComponent.ts": scripts_editorScripts_ShipComponent
|
||||||
};
|
};
|
||||||
|
|
||||||
export { loadScene, scriptsDictionary, scriptAssetsCache, _applyScriptsForObject, _removeRegisteredScriptInstance, _preloadScriptsAssets };
|
export { loadScene, scriptsDictionary, scriptAssetsCache, _applyScriptsForObject, _removeRegisteredScriptInstance, _preloadScriptsAssets };
|
||||||
|
|||||||
@ -42,6 +42,7 @@ interface RockConfig {
|
|||||||
angularVelocity: Vector3;
|
angularVelocity: Vector3;
|
||||||
scoreObservable: Observable<ScoreEvent>;
|
scoreObservable: Observable<ScoreEvent>;
|
||||||
useOrbitConstraint: boolean;
|
useOrbitConstraint: boolean;
|
||||||
|
targetId?: string;
|
||||||
targetPosition?: Vector3;
|
targetPosition?: Vector3;
|
||||||
targetMode?: 'orbit' | 'moveToward';
|
targetMode?: 'orbit' | 'moveToward';
|
||||||
}
|
}
|
||||||
@ -54,6 +55,9 @@ export class RockFactory {
|
|||||||
// Store created rocks for deferred physics initialization
|
// Store created rocks for deferred physics initialization
|
||||||
private static _createdRocks: Map<string, { mesh: InstancedMesh; config: RockConfig }> = new Map();
|
private static _createdRocks: Map<string, { mesh: InstancedMesh; config: RockConfig }> = new Map();
|
||||||
|
|
||||||
|
// Cache for target physics bodies (shared among asteroids with same targetId)
|
||||||
|
private static _targetBodies: Map<string, PhysicsAggregate> = new Map();
|
||||||
|
|
||||||
/** Public getter for explosion manager (used by WeaponSystem for shape-cast hits) */
|
/** Public getter for explosion manager (used by WeaponSystem for shape-cast hits) */
|
||||||
public static get explosionManager(): ExplosionManager | null {
|
public static get explosionManager(): ExplosionManager | null {
|
||||||
return this._explosionManager;
|
return this._explosionManager;
|
||||||
@ -124,6 +128,11 @@ export class RockFactory {
|
|||||||
log.debug('[RockFactory] Resetting static state');
|
log.debug('[RockFactory] Resetting static state');
|
||||||
this._asteroidMesh = null;
|
this._asteroidMesh = null;
|
||||||
this._createdRocks.clear();
|
this._createdRocks.clear();
|
||||||
|
// Dispose and clear target bodies
|
||||||
|
for (const targetBody of this._targetBodies.values()) {
|
||||||
|
targetBody.dispose();
|
||||||
|
}
|
||||||
|
this._targetBodies.clear();
|
||||||
if (this._explosionManager) {
|
if (this._explosionManager) {
|
||||||
this._explosionManager.dispose();
|
this._explosionManager.dispose();
|
||||||
this._explosionManager = null;
|
this._explosionManager = null;
|
||||||
@ -170,6 +179,7 @@ export class RockFactory {
|
|||||||
scoreObservable: Observable<ScoreEvent>,
|
scoreObservable: Observable<ScoreEvent>,
|
||||||
useOrbitConstraint: boolean = true,
|
useOrbitConstraint: boolean = true,
|
||||||
hidden: boolean = false,
|
hidden: boolean = false,
|
||||||
|
targetId?: string,
|
||||||
targetPosition?: Vector3,
|
targetPosition?: Vector3,
|
||||||
targetMode?: 'orbit' | 'moveToward',
|
targetMode?: 'orbit' | 'moveToward',
|
||||||
rotation?: Vector3
|
rotation?: Vector3
|
||||||
@ -197,12 +207,13 @@ export class RockFactory {
|
|||||||
angularVelocity,
|
angularVelocity,
|
||||||
scoreObservable,
|
scoreObservable,
|
||||||
useOrbitConstraint,
|
useOrbitConstraint,
|
||||||
|
targetId,
|
||||||
targetPosition,
|
targetPosition,
|
||||||
targetMode
|
targetMode
|
||||||
};
|
};
|
||||||
this._createdRocks.set(rock.id, { mesh: rock, config });
|
this._createdRocks.set(rock.id, { mesh: rock, config });
|
||||||
|
|
||||||
log.debug(`[RockFactory] Created rock mesh ${rock.id} (hidden: ${hidden}, target: ${targetMode || 'none'})`);
|
log.debug(`[RockFactory] Created rock mesh ${rock.id} (hidden: ${hidden}, target: ${targetId || 'none'}, mode: ${targetMode || 'none'})`);
|
||||||
return new Rock(rock);
|
return new Rock(rock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,14 +238,18 @@ export class RockFactory {
|
|||||||
|
|
||||||
// Handle target-based physics
|
// Handle target-based physics
|
||||||
if (config.targetPosition && config.targetMode) {
|
if (config.targetPosition && config.targetMode) {
|
||||||
|
log.debug(`[RockFactory] Applying ${config.targetMode} physics to ${rock.id} toward target at ${config.targetPosition}`);
|
||||||
this.applyTargetPhysics(body, config);
|
this.applyTargetPhysics(body, config);
|
||||||
} else if (config.useOrbitConstraint && this._orbitCenter) {
|
} else if (config.useOrbitConstraint && this._orbitCenter) {
|
||||||
// Legacy: orbit around origin if no specific target
|
// Legacy: orbit around origin if no specific target
|
||||||
|
log.debug(`[RockFactory] Using legacy orbit constraint for ${rock.id} (no target specified)`);
|
||||||
const constraint = new DistanceConstraint(
|
const constraint = new DistanceConstraint(
|
||||||
Vector3.Distance(config.position, this._orbitCenter.body.transformNode.position),
|
Vector3.Distance(config.position, this._orbitCenter.body.transformNode.position),
|
||||||
DefaultScene.MainScene
|
DefaultScene.MainScene
|
||||||
);
|
);
|
||||||
body.addConstraint(this._orbitCenter.body, constraint);
|
body.addConstraint(this._orbitCenter.body, constraint);
|
||||||
|
} else {
|
||||||
|
log.debug(`[RockFactory] No orbit constraint for ${rock.id} (useOrbitConstraint=${config.useOrbitConstraint})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent sleeping
|
// Prevent sleeping
|
||||||
@ -262,16 +277,8 @@ export class RockFactory {
|
|||||||
if (!config.targetPosition) return;
|
if (!config.targetPosition) return;
|
||||||
|
|
||||||
if (config.targetMode === 'orbit') {
|
if (config.targetMode === 'orbit') {
|
||||||
// Create distance constraint to target position
|
// Get or create shared target body for this targetId
|
||||||
// We need a static body at the target position for the constraint
|
const targetBody = this.getOrCreateTargetBody(config.targetId, config.targetPosition);
|
||||||
const targetNode = new TransformNode(`target-${body.transformNode.id}`, DefaultScene.MainScene);
|
|
||||||
targetNode.position = config.targetPosition;
|
|
||||||
const targetBody = new PhysicsAggregate(
|
|
||||||
targetNode, PhysicsShapeType.SPHERE,
|
|
||||||
{ radius: 0.1, mass: 0 },
|
|
||||||
DefaultScene.MainScene
|
|
||||||
);
|
|
||||||
targetBody.body.setMotionType(PhysicsMotionType.STATIC);
|
|
||||||
|
|
||||||
const distance = Vector3.Distance(config.position, config.targetPosition);
|
const distance = Vector3.Distance(config.position, config.targetPosition);
|
||||||
const constraint = new DistanceConstraint(distance, DefaultScene.MainScene);
|
const constraint = new DistanceConstraint(distance, DefaultScene.MainScene);
|
||||||
@ -291,9 +298,38 @@ export class RockFactory {
|
|||||||
// Final velocity = direction * speed
|
// Final velocity = direction * speed
|
||||||
const velocity = direction.scale(speed);
|
const velocity = direction.scale(speed);
|
||||||
body.setLinearVelocity(velocity);
|
body.setLinearVelocity(velocity);
|
||||||
|
} else {
|
||||||
|
log.warn(`Invalid targetMode ${config.targetMode}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or create a shared physics body for a target position
|
||||||
|
*/
|
||||||
|
private static getOrCreateTargetBody(targetId: string | undefined, position: Vector3): PhysicsAggregate {
|
||||||
|
const cacheKey = targetId || `pos-${position.x}-${position.y}-${position.z}`;
|
||||||
|
|
||||||
|
if (this._targetBodies.has(cacheKey)) {
|
||||||
|
log.debug(`[RockFactory] Reusing existing target body for "${cacheKey}"`);
|
||||||
|
return this._targetBodies.get(cacheKey)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new target body
|
||||||
|
const targetNode = new TransformNode(`target-${cacheKey}`, DefaultScene.MainScene);
|
||||||
|
targetNode.position = position;
|
||||||
|
const targetBody = new PhysicsAggregate(
|
||||||
|
targetNode, PhysicsShapeType.SPHERE,
|
||||||
|
{ radius: 0.1, mass: 0 },
|
||||||
|
DefaultScene.MainScene
|
||||||
|
);
|
||||||
|
targetBody.body.setMotionType(PhysicsMotionType.STATIC);
|
||||||
|
|
||||||
|
this._targetBodies.set(cacheKey, targetBody);
|
||||||
|
log.debug(`[RockFactory] Created new target body for "${cacheKey}" at ${position}`);
|
||||||
|
|
||||||
|
return targetBody;
|
||||||
|
}
|
||||||
|
|
||||||
private static setupCollisionHandler(body: PhysicsBody, scoreObservable: Observable<ScoreEvent>): void {
|
private static setupCollisionHandler(body: PhysicsBody, scoreObservable: Observable<ScoreEvent>): void {
|
||||||
body.getCollisionObservable().add((eventData) => {
|
body.getCollisionObservable().add((eventData) => {
|
||||||
if (eventData.type !== 'COLLISION_STARTED') return;
|
if (eventData.type !== 'COLLISION_STARTED') return;
|
||||||
|
|||||||
@ -248,10 +248,18 @@ export class LevelDeserializer {
|
|||||||
|
|
||||||
// Resolve target position if specified
|
// Resolve target position if specified
|
||||||
let targetPosition: Vector3 | undefined;
|
let targetPosition: Vector3 | undefined;
|
||||||
if (asteroidConfig.targetId && this.config.targets) {
|
if (asteroidConfig.targetId) {
|
||||||
const target = this.config.targets.find(t => t.id === asteroidConfig.targetId);
|
if (this.config.targets && this.config.targets.length > 0) {
|
||||||
if (target) {
|
const target = this.config.targets.find(t => t.id === asteroidConfig.targetId);
|
||||||
targetPosition = this.arrayToVector3(target.position);
|
if (target) {
|
||||||
|
targetPosition = this.arrayToVector3(target.position);
|
||||||
|
log.debug(`[LevelDeserializer] Asteroid ${asteroidConfig.id} linked to target ${target.id} at ${targetPosition}`);
|
||||||
|
} else {
|
||||||
|
const availableIds = this.config.targets.map(t => t.id).join(', ');
|
||||||
|
log.warn(`[LevelDeserializer] Asteroid ${asteroidConfig.id} has targetId "${asteroidConfig.targetId}" but no match found. Available: [${availableIds}]`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn(`[LevelDeserializer] Asteroid ${asteroidConfig.id} has targetId "${asteroidConfig.targetId}" but no targets array in config`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,6 +274,7 @@ export class LevelDeserializer {
|
|||||||
scoreObservable,
|
scoreObservable,
|
||||||
useOrbitConstraints,
|
useOrbitConstraints,
|
||||||
hidden,
|
hidden,
|
||||||
|
asteroidConfig.targetId,
|
||||||
targetPosition,
|
targetPosition,
|
||||||
asteroidConfig.targetMode,
|
asteroidConfig.targetMode,
|
||||||
rotation
|
rotation
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user