Add background music selection and update game assets

Add configurable background music per level with song selector in the
level editor General tab. Update ship model, add base3 model, add
song2/song3 audio tracks, and clean up level config formatting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Michael Mainguy 2026-03-03 06:13:37 -06:00
parent a7799a9c71
commit 208d735ef4
29 changed files with 476 additions and 519 deletions

View File

@ -106,15 +106,15 @@
"applyGravity": false, "applyGravity": false,
"updateUpVectorFromRotation": false, "updateUpVectorFromRotation": false,
"rotation": [ "rotation": [
0.23466138679888465, 0.3870449848810084,
0.48668946590326523, 0.05980234407784223,
0 0
], ],
"speed": 2000, "speed": 10,
"position": [ "position": [
-19666.901457088374, -19.027983524409535,
17401.223711429564, 129.4532146827724,
-55872.0430330369 -317.01273902922264
], ],
"upVector": [ "upVector": [
0, 0,

View File

@ -1,279 +0,0 @@
{
"meshes": [
{
"name": "New Box",
"id": "1679685b-ca9c-4a1c-8161-9f6064cb1d7c",
"uniqueId": 1764787809874,
"type": "Mesh",
"position": [
0,
0,
0
],
"rotation": [
0,
0,
0
],
"scaling": [
1,
1,
1
],
"localMatrix": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
],
"isEnabled": true,
"isVisible": true,
"infiniteDistance": false,
"pickable": true,
"receiveShadows": true,
"billboardMode": 0,
"visibility": 1,
"alwaysSelectAsActiveMesh": false,
"checkCollisions": false,
"ellipsoid": [
0.5,
1,
0.5
],
"ellipsoidOffset": [
0,
0,
0
],
"doNotSyncBoundingInfo": false,
"isBlocker": false,
"sideOrientation": 1,
"isUnIndexed": false,
"geometryUniqueId": 1764787809875,
"geometryId": "76c7442d-fb7e-4a05-b1c5-9c27b0beb0dc",
"subMeshes": null,
"materialUniqueId": 46,
"materialId": "default material",
"metadata": {
"type": "Box",
"width": 100,
"depth": 100,
"height": 100,
"sideOrientation": 0
},
"instances": [],
"animations": [],
"ranges": [],
"layerMask": 268435455,
"alphaIndex": 1.7976931348623157e+308,
"hasVertexAlpha": false,
"overlayAlpha": 1,
"overlayColor": [
0,
0,
0
],
"applyFog": true,
"delayLoadingFile": "assets/example.scene/geometries/76c7442d-fb7e-4a05-b1c5-9c27b0beb0dc.babylonbinarymeshdata",
"boundingBoxMaximum": [
50,
50,
50
],
"boundingBoxMinimum": [
-50,
-50,
-50
],
"_binaryInfo": {
"positionsAttrDesc": {
"count": 72,
"stride": 3,
"offset": 0,
"dataType": 1
},
"normalsAttrDesc": {
"count": 72,
"stride": 3,
"offset": 288,
"dataType": 1
},
"uvsAttrDesc": {
"count": 48,
"stride": 2,
"offset": 576,
"dataType": 1
},
"indicesAttrDesc": {
"count": 36,
"stride": 1,
"offset": 768,
"dataType": 0
},
"subMeshesAttrDesc": {
"count": 1,
"stride": 5,
"offset": 912,
"dataType": 0
}
},
"positions": null,
"normals": null,
"uvs": null,
"hasUVs": true,
"indices": null
}
],
"transformNodes": [],
"cameras": [],
"lights": [],
"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": {
"boxes": [],
"spheres": [],
"cylinders": [],
"toruses": [],
"grounds": [],
"planes": [],
"torusKnots": [],
"vertexData": []
},
"metadata": {
"type": "Box",
"width": 100,
"depth": 100,
"height": 100,
"sideOrientation": 0
},
"basePoseMatrix": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
]
}

View File

@ -16,9 +16,9 @@
0 0
], ],
"scaling": [ "scaling": [
100.00000000000159, 1,
-100, -1,
100.00000000000159 1
], ],
"localMatrix": [ "localMatrix": [
1, 1,

View File

@ -17,9 +17,9 @@
0 0
], ],
"scaling": [ "scaling": [
100, 1,
100, 1,
-100 -1
], ],
"localMatrix": [ "localMatrix": [
1, 1,

View File

@ -17,9 +17,9 @@
0 0
], ],
"scaling": [ "scaling": [
100, 1,
100, 1,
-100 -1
], ],
"localMatrix": [ "localMatrix": [
1, 1,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 74 KiB

File diff suppressed because one or more lines are too long

View File

@ -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_BaseComponent from "./scripts/editorScripts/BaseComponent";
import * as scripts_editorScripts_ShipComponent from "./scripts/editorScripts/ShipComponent"; import * as scripts_editorScripts_ShipComponent from "./scripts/editorScripts/ShipComponent";
import * as scripts_editorScripts_BaseComponent from "./scripts/editorScripts/BaseComponent";
export const scriptsMap = { export const scriptsMap = {
"scripts/editorScripts/AsteroidComponent.ts": scripts_editorScripts_AsteroidComponent, "scripts/editorScripts/AsteroidComponent.ts": scripts_editorScripts_AsteroidComponent,
"scripts/editorScripts/BaseComponent.ts": scripts_editorScripts_BaseComponent, "scripts/editorScripts/ShipComponent.ts": scripts_editorScripts_ShipComponent,
"scripts/editorScripts/ShipComponent.ts": scripts_editorScripts_ShipComponent "scripts/editorScripts/BaseComponent.ts": scripts_editorScripts_BaseComponent
}; };
export { loadScene, scriptsDictionary, scriptAssetsCache, _applyScriptsForObject, _removeRegisteredScriptInstance, _preloadScriptsAssets }; export { loadScene, scriptsDictionary, scriptAssetsCache, _applyScriptsForObject, _removeRegisteredScriptInstance, _preloadScriptsAssets };

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -9,273 +9,270 @@
"type": "default" "type": "default"
}, },
"ship": { "ship": {
"position": [ "position": [0, 1, 0],
0, "rotation": [0, 0, 0]
1,
0
],
"rotation": [
0,
0,
0
],
"linearVelocity": [
0,
0,
0
],
"angularVelocity": [
0,
0,
0
]
}, },
"startBase": { "startBase": {
"position": [ "position": [0, 0, 0],
0,
0,
0
],
"baseGlbPath": "base.glb" "baseGlbPath": "base.glb"
}, },
"sun": { "sun": {
"position": [ "position": [0, 0, 400],
0, "diameter": 50
0,
400
],
"diameter": 50,
"intensity": 1000000
}, },
"planets": [], "planets": [],
"targets": [
{
"id": "ORBIT",
"name": "ORBIT",
"position": [0, 0, 0]
},
{
"id": "LINEAR",
"name": "LINEAR",
"position": [0, 0, 0]
}
],
"asteroids": [ "asteroids": [
{ {
"id": "asteroid-0", "id": "asteroid-0",
"position": [ "mass": 40,
242.60734209985543,
-114.56996058926651,
5.575229357062
],
"scale": 2, "scale": 2,
"linearVelocity": [ "position": [75, 85, -65],
-170.167175139332553, "linearVelocity": [-20, 15, 25],
80.177863609194048, "angularVelocity": [-0.8, 0.9, 0.8],
-0.39450965492725215 "targetId": "ORBIT",
], "targetMode": "orbit"
"angularVelocity": [
-0.834980024785148,
0.9648009938830251,
0.8185653748494373
]
}, },
{ {
"id": "asteroid-1", "id": "asteroid-1",
"position": [ "mass": 120,
145.90971366777896,
42.273817290099984,
-244.80503221456152
],
"scale": 6, "scale": 6,
"linearVelocity": [ "position": [-120, 110, -100],
-14.737555578618144, "linearVelocity": [18, -12, 22],
-42.168846343154079, "angularVelocity": [0.5, -2.8, -0.9],
240.72643991613985 "targetId": "ORBIT",
], "targetMode": "orbit"
"angularVelocity": [
0.575649251710729,
-2.8551046445434349,
-0.9477761112717422
]
}, },
{ {
"id": "asteroid-2", "id": "asteroid-2",
"position": [ "mass": 80,
195.05992969157123,
-311.0584087077698,
-22.40662780090249
],
"scale": 4, "scale": 4,
"linearVelocity": [ "position": [160, -140, 100],
-160.81570103491442, "linearVelocity": [-25, 20, -15],
9.660316715266058, "angularVelocity": [0.8, 0.2, -0.7],
160.9316276535952197 "targetId": "ORBIT",
], "targetMode": "orbit"
"angularVelocity": [
0.8587973467645904,
0.25620436829463733,
-0.7705721105608303
]
}, },
{ {
"id": "asteroid-3", "id": "asteroid-3",
"position": [ "mass": 347,
-0.9357515100775112,
85.76554222686204,
249.4670613777975
],
"scale": 17.34408913479813, "scale": 17.34408913479813,
"linearVelocity": [ "position": [-180, 200, -160],
0.07109432360434195, "linearVelocity": [12, -8, 15],
-6.440116659897093, "angularVelocity": [0.1, 0.4, -0.9],
-18.953420645560346 "targetId": "ORBIT",
], "targetMode": "orbit"
"angularVelocity": [
0.19650221972006143,
0.4226089665809898,
-0.9419176203015098
]
}, },
{ {
"id": "asteroid-4", "id": "asteroid-4",
"position": [ "mass": 80,
-254.14456477364413,
54.65967750105119,
82.65652287437858
],
"scale": 4, "scale": 4,
"linearVelocity": [ "position": [90, -80, 85],
22.372081486064396, "linearVelocity": [-22, 18, -20],
-400.723605553550473, "angularVelocity": [-0.2, 0.03, 0.3],
-7.2761676675924445 "targetId": "ORBIT",
], "targetMode": "orbit"
"angularVelocity": [
-0.22039903827783025,
0.03062354927084643,
0.3628209366655213
]
}, },
{ {
"id": "asteroid-5", "id": "asteroid-5",
"position": [ "mass": 240,
-257.7249224576784,
-112.97325792551102,
-92.25372143357285
],
"scale": 12, "scale": 12,
"linearVelocity": [ "position": [-200, 140, -120],
17.764361846647077, "linearVelocity": [15, -10, 18],
7.855903788127005, "angularVelocity": [-0.2, 0.2, -0.8],
6.358828139777149 "targetId": "ORBIT",
], "targetMode": "orbit"
"angularVelocity": [
-0.27982741337355455,
0.2465507084870353,
-0.8489416083688623
]
}, },
{ {
"id": "asteroid-6", "id": "asteroid-6",
"position": [ "mass": 289,
-61.74000302102928,
103.75532261403117,
-224.6843746923246
],
"scale": 14.438006716048399, "scale": 14.438006716048399,
"linearVelocity": [ "position": [220, -230, 150],
4.573571795825104, "linearVelocity": [-10, 8, -12],
-7.611901885044768, "angularVelocity": [-0.4, -0.5, 0.2],
16.644154013167135 "targetId": "ORBIT",
], "targetMode": "orbit"
"angularVelocity": [
-0.41949593751738457,
-0.5881266007071146,
0.2671577602439994
]
}, },
{ {
"id": "asteroid-7", "id": "asteroid-7",
"position": [ "mass": 200,
16.846663100767792,
72.36836836065181,
-271.36235273889974
],
"scale": 10, "scale": 10,
"linearVelocity": [ "position": [-100, 120, -85],
220.2776861733199087, "linearVelocity": [20, -15, 18],
-345.412726361379603, "angularVelocity": [-0.5, 0.8, -0.03],
-20.580688530433683 "targetId": "ORBIT",
], "targetMode": "orbit"
"angularVelocity": [
-0.5793176374486806,
0.8207961833131412,
-0.034658037798875885
]
}, },
{ {
"id": "asteroid-8", "id": "asteroid-8",
"position": [ "mass": 200,
129.11110725214024,
91.10691458736655,
205.0668479159754
],
"scale": 10, "scale": 10,
"linearVelocity": [ "position": [170, 150, -100],
-10.330594112594069, "linearVelocity": [-12, -18, 22],
-7.209743461671342, "angularVelocity": [-2.5, 0.6, -0.7],
160.4080567261488 "targetId": "ORBIT",
], "targetMode": "orbit"
"angularVelocity": [
-2.572098306083443,
0.6581860817605101,
-0.7141435682550208
]
}, },
{ {
"id": "asteroid-9", "id": "asteroid-9",
"position": [ "mass": 283,
-300.953057070289603,
225.21952155696817,
139.05608152400566
],
"scale": 14.151176153817078, "scale": 14.151176153817078,
"linearVelocity": [ "position": [-240, 180, 140],
1.9861965590557589, "linearVelocity": [8, -12, -10],
-314.387724003424648, "angularVelocity": [0.7, -4.8, -0.1],
-8.922954201633985 "targetId": "ORBIT",
], "targetMode": "orbit"
"angularVelocity": [
0.7016416714654072,
-4.8069811132136699,
-0.16093262088047533
]
}, },
{ {
"id": "asteroid-10", "id": "asteroid-10",
"position": [ "mass": 240,
300.953057070289603,
225.21952155696817,
139.05608152400566
],
"scale": 12, "scale": 12,
"linearVelocity": [ "position": [280, -200, 150],
100.9861965590557589, "linearVelocity": [-15, 10, -8],
-314.387724003424648, "angularVelocity": [0.7, -4.8, -0.1],
-240.922954201633985 "targetId": "ORBIT",
], "targetMode": "orbit"
"angularVelocity": [
0.7016416714654072,
-4.8069811132136699,
-0.16093262088047533
]
}, },
{ {
"id": "asteroid-11", "id": "asteroid-11",
"position": [ "mass": 600,
300.953057070289603,
-225.21952155696817,
69.05608152400566
],
"scale": 30, "scale": 30,
"linearVelocity": [ "position": [-300, 200, -150],
100.9861965590557589, "linearVelocity": [10, -6, 8],
-214.387724003424648, "angularVelocity": [0.7, -4.8, -0.1],
140.922954201633985 "targetId": "ORBIT",
], "targetMode": "orbit"
"angularVelocity": [ },
0.7016416714654072, {
-4.8069811132136699, "id": "asteroid-12",
-0.16093262088047533 "mass": 100,
] "scale": 5,
"position": [130, -95, 70],
"linearVelocity": [-55, 40, 65],
"angularVelocity": [1.2, -0.8, 0.5],
"targetId": "ORBIT",
"targetMode": "orbit"
},
{
"id": "asteroid-13",
"mass": 160,
"scale": 8,
"position": [-85, 160, 120],
"linearVelocity": [48, -35, 58],
"angularVelocity": [-0.6, 1.5, -1.2],
"targetId": "ORBIT",
"targetMode": "orbit"
},
{
"id": "asteroid-14",
"mass": 60,
"scale": 3,
"position": [210, 95, -180],
"linearVelocity": [-62, 50, -42],
"angularVelocity": [0.9, 0.4, -1.8],
"targetId": "ORBIT",
"targetMode": "orbit"
},
{
"id": "asteroid-15",
"mass": 180,
"scale": 9,
"position": [-145, -175, 85],
"linearVelocity": [38, 52, -45],
"angularVelocity": [-1.1, -0.7, 0.9],
"targetId": "ORBIT",
"targetMode": "orbit"
},
{
"id": "asteroid-16",
"mass": 140,
"scale": 7,
"position": [55, 230, -195],
"linearVelocity": [-45, -58, 52],
"angularVelocity": [0.3, 2.1, -0.6],
"targetId": "ORBIT",
"targetMode": "orbit"
},
{
"id": "asteroid-17",
"mass": 220,
"scale": 11,
"position": [-270, -110, -160],
"linearVelocity": [52, 38, 48],
"angularVelocity": [-0.8, 0.5, 1.4],
"targetId": "ORBIT",
"targetMode": "orbit"
},
{
"id": "asteroid-18",
"mass": 80,
"scale": 4,
"position": [185, -250, 95],
"linearVelocity": [-48, 62, -38],
"angularVelocity": [1.6, -1.2, -0.4],
"targetId": "ORBIT",
"targetMode": "orbit"
},
{
"id": "asteroid-19",
"mass": 260,
"scale": 13,
"position": [-110, 85, 280],
"linearVelocity": [35, -42, -55],
"angularVelocity": [-0.5, 0.9, 1.1],
"targetId": "ORBIT",
"targetMode": "orbit"
},
{
"id": "asteroid-20",
"mass": 120,
"scale": 6,
"position": [320, 140, -85],
"linearVelocity": [-58, -45, 40],
"angularVelocity": [0.7, -1.8, 0.3],
"targetId": "ORBIT",
"targetMode": "orbit"
},
{
"id": "asteroid-21",
"mass": 300,
"scale": 15,
"position": [-195, -290, 120],
"linearVelocity": [42, 55, -48],
"angularVelocity": [-1.3, 0.6, -0.9],
"targetId": "ORBIT",
"targetMode": "orbit"
},
{
"id": "asteroid-22",
"mass": 160,
"scale": 8,
"position": [95, -65, -310],
"linearVelocity": [-40, 58, 65],
"angularVelocity": [0.4, -0.3, 2.2],
"targetId": "ORBIT",
"targetMode": "orbit"
},
{
"id": "asteroid-23",
"mass": 200,
"scale": 10,
"position": [-350, 165, 90],
"linearVelocity": [55, -38, -52],
"angularVelocity": [-0.9, 1.7, 0.5],
"targetId": "ORBIT",
"targetMode": "orbit"
} }
] ]
} }

189
public/levels/template.json Normal file
View File

@ -0,0 +1,189 @@
{
"sun": {
"diameter": 50,
"position": [
0,
0,
400
]
},
"ship": {
"position": [
0,
2,
0
],
"rotation": [
0,
0,
0
]
},
"planets": [],
"version": "1.0",
"metadata": {
"author": "BabylonJS Editor",
"description": "Exported from Editor"
},
"targets": [
{
"id": "ORBIT",
"name": "ORBIT",
"position": [
0,
0,
0
]
},
{
"id": "LINEAR",
"name": "LINEAR",
"position": [
0,
-46.40267541850123,
0
]
}
],
"asteroids": [
{
"id": "Asteroid",
"mass": 100,
"scale": 5,
"position": [
0,
55.91356273231697,
-84.76095007459317
],
"linearVelocity": [
0,
0,
-30
],
"angularVelocity": [
0,
0,
5
],
"targetId": "ORBIT",
"targetMode": "orbit"
},
{
"id": "Asteroid (Mesh Instance)",
"mass": 100,
"scale": 5,
"position": [
-216.86571629860342,
6.395638981779044,
0
],
"linearVelocity": [
0,
0,
20
],
"angularVelocity": [
1,
0,
0
]
},
{
"id": "Asteroid (Mesh Instance)",
"mass": 100,
"scale": 5,
"position": [
125.92973388713749,
0,
0
],
"linearVelocity": [
0,
0,
30
],
"angularVelocity": [
0,
0,
0
],
"targetId": "ORBIT",
"targetMode": "orbit"
},
{
"id": "Asteroid (Mesh Instance)",
"mass": 1,
"scale": 25,
"position": [
121.55444252370417,
3.373869538878104,
-350.47951942991017
],
"linearVelocity": [
0,
0,
50
],
"angularVelocity": [
0,
0,
10
],
"targetId": "ORBIT",
"targetMode": "orbit"
},
{
"id": "Asteroid (Mesh Instance)",
"mass": 100,
"scale": 5,
"position": [
-50.27889543892182,
2.934664213442815,
57.70721855086853
],
"linearVelocity": [
100,
0,
10
],
"angularVelocity": [
0,
2,
0
]
},
{
"id": "Asteroid (Mesh Instance)",
"mass": 210,
"scale": 5,
"position": [
-154.4148281169747,
-133.4543443541567,
-230.29621395184932
],
"linearVelocity": [
0,
30,
0
],
"angularVelocity": [
0.1,
1,
-1
],
"targetId": "ORBIT",
"targetMode": "orbit"
}
],
"startBase": {
"position": [
0,
0,
0
],
"baseGlbPath": "base.glb"
},
"timestamp": "2025-12-05T14:49:29.176Z",
"difficulty": "rookie",
"useOrbitConstraints": true
}

View File

@ -22,7 +22,7 @@
let level: CloudLevelEntry | null = null; let level: CloudLevelEntry | null = null;
let config: LevelConfig | null = null; let config: LevelConfig | null = null;
let error = ''; let error = '';
let activeTab = 'ship'; let activeTab = 'general';
// Message state // Message state
let message = ''; let message = '';
@ -30,6 +30,7 @@
let showMessage = false; let showMessage = false;
const tabs = [ const tabs = [
{ id: 'general', label: '⚙️ General' },
{ id: 'ship', label: '🚀 Ship' }, { id: 'ship', label: '🚀 Ship' },
{ id: 'base', label: '🛬 Base' }, { id: 'base', label: '🛬 Base' },
{ id: 'sun', label: '☀️ Sun' }, { id: 'sun', label: '☀️ Sun' },
@ -39,6 +40,8 @@
{ id: 'planets', label: '🪐 Planets' } { id: 'planets', label: '🪐 Planets' }
]; ];
const musicOptions = ['song1.mp3', 'song2.mp3','song3.mp3'];
onMount(async () => { onMount(async () => {
await loadLevel(); await loadLevel();
}); });
@ -84,7 +87,12 @@
try { try {
const service = CloudLevelService.getInstance(); const service = CloudLevelService.getInstance();
const updated = await service.updateLevelAsAdmin(level.id, { config }); // Convert empty strings to undefined before saving
const cleanConfig = {
...config,
backgroundMusic: config.backgroundMusic || undefined
};
const updated = await service.updateLevelAsAdmin(level.id, { config: cleanConfig });
if (updated) { if (updated) {
level = updated; level = updated;
@ -170,7 +178,23 @@
<!-- Tab Content --> <!-- Tab Content -->
<div class="tab-content"> <div class="tab-content">
{#if activeTab === 'ship'} {#if activeTab === 'general'}
<Section title="General Settings">
<div class="field-row">
<label for="backgroundMusic">Background Music</label>
<select id="backgroundMusic" bind:value={config.backgroundMusic}>
<option value="">Default (song1.mp3)</option>
{#each musicOptions as song}
<option value={song}>{song}</option>
{/each}
</select>
</div>
<div class="field-row">
<label for="difficulty">Difficulty</label>
<input id="difficulty" type="text" bind:value={config.difficulty} />
</div>
</Section>
{:else if activeTab === 'ship'}
<ShipConfigEditor bind:config={config.ship} /> <ShipConfigEditor bind:config={config.ship} />
{:else if activeTab === 'base'} {:else if activeTab === 'base'}
<BaseConfigEditor config={config.startBase} onToggle={handleBaseToggle} /> <BaseConfigEditor config={config.startBase} onToggle={handleBaseToggle} />
@ -234,4 +258,27 @@
.tab-content { .tab-content {
min-height: 300px; min-height: 300px;
} }
.field-row {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 0.75rem;
}
.field-row label {
min-width: 150px;
color: var(--color-text-secondary, #888);
}
.field-row select,
.field-row input {
flex: 1;
padding: 0.5rem;
background: var(--color-bg-tertiary, #252540);
border: 1px solid var(--color-border, #333);
border-radius: 4px;
color: var(--color-text-primary, #fff);
max-width: 300px;
}
</style> </style>

View File

@ -9,14 +9,10 @@ if (queryLevel && ['debug', 'info', 'warn', 'error'].includes(queryLevel)) {
// Check localStorage for custom level (enables production debugging) // Check localStorage for custom level (enables production debugging)
const storedLevel = localStorage.getItem('log-level'); const storedLevel = localStorage.getItem('log-level');
console.log(storedLevel);
// Set level: localStorage override > environment default // Set level: localStorage override > environment default
if (storedLevel) { if (storedLevel) {
log.setLevel(storedLevel as log.LogLevelDesc); log.setLevel(storedLevel as log.LogLevelDesc);
} else {
const isDev = window.location.hostname === 'localhost' ||
window.location.hostname.includes('dev.');
log.setLevel(isDev ? 'debug' : 'warn');
} }
export default log; export default log;

View File

@ -64,6 +64,9 @@ function createMainScene(engine: Engine): void {
//DefaultScene.MainScene.performancePriority = ScenePerformancePriority.Intermediate; //DefaultScene.MainScene.performancePriority = ScenePerformancePriority.Intermediate;
DefaultScene.MainScene.autoClear = false; DefaultScene.MainScene.autoClear = false;
DefaultScene.MainScene.autoClearDepthAndStencil = false; DefaultScene.MainScene.autoClearDepthAndStencil = false;
//const hdrTexture = new HDRCubeTexture("/assets/untitled.hdr", DefaultScene.MainScene, 2048);
//DefaultScene.MainScene.environmentTexture = hdrTexture;
//DefaultScene.MainScene.createDefaultSkybox(hdrTexture, true, 1000);
} }
async function setupPhysics(): Promise<void> { async function setupPhysics(): Promise<void> {

View File

@ -144,6 +144,7 @@ export interface LevelConfig {
version: string; version: string;
difficulty: string; difficulty: string;
timestamp?: string; // ISO date string timestamp?: string; // ISO date string
backgroundMusic?: string; // Audio filename e.g., "song1.mp3" - defaults to "song1.mp3"
metadata?: { metadata?: {
author?: string; author?: string;
description?: string; description?: string;

View File

@ -358,7 +358,9 @@ export class Level1 implements Level {
this._hintSystem.setAudioEngine(audioEngine); this._hintSystem.setAudioEngine(audioEngine);
// Load background music (uses prefetched audio if available) // Load background music (uses prefetched audio if available)
const musicUrl = "/assets/themes/default/audio/song1.mp3"; const musicFile = this._levelConfig.backgroundMusic || "song1.mp3";
log.debug(musicFile);
const musicUrl = `/assets/themes/default/audio/${musicFile}`;
this._backgroundMusic = await audioEngine.createSoundAsync( this._backgroundMusic = await audioEngine.createSoundAsync(
"background", "background",
getAudioSource(musicUrl), getAudioSource(musicUrl),

View File

@ -9,6 +9,7 @@ const AUDIO_FILES = [
`${AUDIO_BASE}/shot.mp3`, `${AUDIO_BASE}/shot.mp3`,
`${AUDIO_BASE}/collision.mp3`, `${AUDIO_BASE}/collision.mp3`,
`${AUDIO_BASE}/song1.mp3`, `${AUDIO_BASE}/song1.mp3`,
`${AUDIO_BASE}/song2.mp3`,
]; ];
// Cache for prefetched audio buffers // Cache for prefetched audio buffers

BIN
themes/default/Untitled.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

BIN
themes/default/base3.blend Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.