Починен зелёный экран: все узлы были не на том слое
nodeCreate кладёт узел на слой DEFAULT, а камера Canvas рисует только UI_2D. Поэтому сборка показывала цвет очистки камеры и больше ничего. В редакторе это не видно вообще: камера вида сцены рисует все слои, так что сцена выглядит правильной, а билд — пустым. - 45 узлов в обеих сценах и префабе переведены на слой UI_2D - scripts/check-scenes.mjs: headless-проверка сцен и префабов — слои, наличие камеры у Canvas, видимость UI_2D у камеры, незаполненные ссылки в наших компонентах. Проверено, что она действительно падает на подсунутом неверном слое - npm run check — всё разом (чистота core, сцены, типы, тесты) - Bootstrap теперь докладывает каждый шаг в лейбл и в консоль и через 10 секунд уходит в лобби даже если арт не догрузился: молча висящий загрузчик — худший вид бага в отчёте - ловушка со слоями описана в docs/CODE_MODE_SETUP.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -53,7 +53,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
|
|||||||
@@ -111,7 +111,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -159,7 +159,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -278,7 +278,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -417,7 +417,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -549,7 +549,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -691,6 +691,9 @@
|
|||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": null,
|
"__prefab": null,
|
||||||
"nextScene": "lobby",
|
"nextScene": "lobby",
|
||||||
|
"statusLabel": {
|
||||||
|
"__id__": 15
|
||||||
|
},
|
||||||
"_id": "dcSHYP80NEeZirk4SzzDBT"
|
"_id": "dcSHYP80NEeZirk4SzzDBT"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
+39
-39
@@ -108,7 +108,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -156,7 +156,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -275,7 +275,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -424,7 +424,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -488,7 +488,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -536,7 +536,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -668,7 +668,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -828,7 +828,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -876,7 +876,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -1008,7 +1008,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -1140,7 +1140,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -1272,7 +1272,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -1404,7 +1404,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -1536,7 +1536,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -1668,7 +1668,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -1800,7 +1800,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -1932,7 +1932,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -2132,7 +2132,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -2180,7 +2180,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -2545,7 +2545,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -2594,7 +2594,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -2642,7 +2642,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -2796,7 +2796,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -2928,7 +2928,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -3060,7 +3060,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -3164,7 +3164,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -3219,7 +3219,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -3267,7 +3267,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -3529,7 +3529,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -3577,7 +3577,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -3861,7 +3861,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -3909,7 +3909,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -4171,7 +4171,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -4225,7 +4225,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -4424,7 +4424,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -4472,7 +4472,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -4604,7 +4604,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -4743,7 +4743,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
@@ -4791,7 +4791,7 @@
|
|||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
"_layer": 1073741824,
|
"_layer": 33554432,
|
||||||
"_euler": {
|
"_euler": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
|
|||||||
@@ -1,42 +1,81 @@
|
|||||||
import { _decorator, Component, director, game, resources, SpriteFrame } from 'cc';
|
import { _decorator, Component, director, game, Label, resources, SpriteFrame } from 'cc';
|
||||||
import { App } from './App';
|
import { App } from './App';
|
||||||
import { CARDS_DIR } from '../ui/game/CardLibrary';
|
import { CARDS_DIR } from '../ui/game/CardLibrary';
|
||||||
|
|
||||||
const { ccclass, property } = _decorator;
|
const { ccclass, property } = _decorator;
|
||||||
|
|
||||||
|
/** If the art has not arrived by then, open the lobby anyway rather than hang. */
|
||||||
|
const PRELOAD_TIMEOUT = 10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The loader scene, per ARCHITECTURE.md: bring the modules up, then hand over
|
* The loader scene, per ARCHITECTURE.md: bring the modules up, then hand over
|
||||||
* to the lobby. Everything that must exist before any gameplay runs belongs
|
* to the lobby. Everything that must exist before any gameplay runs belongs
|
||||||
* here, and nowhere else.
|
* here, and nowhere else.
|
||||||
|
*
|
||||||
|
* Every step reports itself to the on-screen label and to the console, because
|
||||||
|
* a loader that stalls silently is the hardest kind of bug to diagnose from a
|
||||||
|
* bug report — "it shows a green screen" should never again be all we know.
|
||||||
*/
|
*/
|
||||||
@ccclass('Bootstrap')
|
@ccclass('Bootstrap')
|
||||||
export class Bootstrap extends Component {
|
export class Bootstrap extends Component {
|
||||||
@property({ tooltip: 'Scene to open once the modules are ready.' })
|
@property({ tooltip: 'Scene to open once the modules are ready.' })
|
||||||
nextScene = 'lobby';
|
nextScene = 'lobby';
|
||||||
|
|
||||||
|
@property({ type: Label, tooltip: 'Shows what the loader is doing.' })
|
||||||
|
statusLabel: Label | null = null;
|
||||||
|
|
||||||
|
private _handedOver = false;
|
||||||
|
|
||||||
start(): void {
|
start(): void {
|
||||||
// A phone that throttles to 30 fps makes the card animations stutter;
|
// A phone that throttles to 30 fps makes the card animations stutter;
|
||||||
// asking for 60 up front is cheap and the engine falls back on its own.
|
// asking for 60 up front is cheap and the engine falls back on its own.
|
||||||
game.frameRate = 60;
|
game.frameRate = 60;
|
||||||
|
|
||||||
|
this._report('starting modules');
|
||||||
App.create();
|
App.create();
|
||||||
|
|
||||||
// Warm the card art here rather than on the first deal: thirty-seven
|
// Warm the card art here rather than on the first deal: thirty-seven
|
||||||
// sprites are the only sizeable load in the game, and a loader scene is
|
// sprites are the only sizeable load in the game, and a loader scene is
|
||||||
// exactly where a player expects to wait for it.
|
// exactly where a player expects to wait for it.
|
||||||
resources.loadDir(CARDS_DIR, SpriteFrame, (error) => {
|
this._report('loading cards');
|
||||||
|
resources.loadDir(CARDS_DIR, SpriteFrame, (error, frames) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('[bootstrap] could not preload the card art', error);
|
console.error('[bootstrap] could not preload the card art', error);
|
||||||
|
} else {
|
||||||
|
console.log(`[bootstrap] ${frames.length} card sprites ready`);
|
||||||
}
|
}
|
||||||
this._openLobby();
|
this._openLobby();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// A failed bundle can leave that callback unfired. The lobby does not
|
||||||
|
// need the art to show, so never let the loader be a dead end.
|
||||||
|
this.scheduleOnce(() => {
|
||||||
|
if (!this._handedOver) {
|
||||||
|
console.warn('[bootstrap] card art did not arrive in time, opening the lobby anyway');
|
||||||
|
this._openLobby();
|
||||||
|
}
|
||||||
|
}, PRELOAD_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _openLobby(): void {
|
private _openLobby(): void {
|
||||||
|
if (this._handedOver) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._handedOver = true;
|
||||||
|
|
||||||
|
this._report('opening lobby');
|
||||||
director.loadScene(this.nextScene, (error) => {
|
director.loadScene(this.nextScene, (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(`[bootstrap] could not open '${this.nextScene}'`, error);
|
console.error(`[bootstrap] could not open '${this.nextScene}'`, error);
|
||||||
|
this._report(`could not open ${this.nextScene}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _report(what: string): void {
|
||||||
|
console.log(`[bootstrap] ${what}`);
|
||||||
|
if (this.statusLabel) {
|
||||||
|
this.statusLabel.string = what;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,3 +120,26 @@ CocosEditor.inspectorSetSettingsProperties({
|
|||||||
values: [1080, true],
|
values: [1080, true],
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Known issue: `nodeCreate` puts nodes on the wrong layer for UI
|
||||||
|
|
||||||
|
A node created with `nodeCreate` lands on the **DEFAULT** layer
|
||||||
|
(`1073741824`). A Canvas camera is normally set to render **UI_2D**
|
||||||
|
(`33554432`) only, so every node authored through the tools is invisible at
|
||||||
|
runtime — the camera draws its clear colour and nothing else, which looks like
|
||||||
|
the game has frozen on a blank coloured screen.
|
||||||
|
|
||||||
|
The editor's own "create node" menu assigns UI_2D, so this only bites
|
||||||
|
automation, and nothing in the editor viewport reveals it: the scene-view camera
|
||||||
|
renders every layer, so the scene looks perfectly fine while the build does not.
|
||||||
|
|
||||||
|
**Always set the layer after creating a UI node:**
|
||||||
|
|
||||||
|
```js
|
||||||
|
const UI_2D = 33554432;
|
||||||
|
CocosEditor.inspectorSetInstanceProperties({
|
||||||
|
reference: newNode, propertyPaths: ['layer'], values: [UI_2D],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
To check a whole scene, walk `nodeGetTree` and assert every node's `layer`.
|
||||||
|
|||||||
@@ -159,3 +159,15 @@ rules leave only one unanswered attack at a time. Putting it in the core means
|
|||||||
the one piece of interface logic that can silently reject a legal move is covered
|
the one piece of interface logic that can silently reject a legal move is covered
|
||||||
by headless tests, including a property test that it never proposes an action the
|
by headless tests, including a property test that it never proposes an action the
|
||||||
rules would refuse.
|
rules would refuse.
|
||||||
|
|
||||||
|
## 2026-09-13 · Presentation · The loader reports every step it takes
|
||||||
|
|
||||||
|
**Decision.** `Bootstrap` writes each stage to the on-screen label and to the
|
||||||
|
console, and falls through to the lobby after ten seconds even if the art
|
||||||
|
preload never calls back.
|
||||||
|
|
||||||
|
**Why.** The first build ran but showed nothing but a green screen, because
|
||||||
|
every node authored through the editor tools had been left on the DEFAULT layer
|
||||||
|
while the camera only renders UI_2D (see CODE_MODE_SETUP.md). A blank screen
|
||||||
|
carries no information about which step failed, and a loader that can hang
|
||||||
|
forever on an asset the lobby does not even need is a dead end for no reason.
|
||||||
|
|||||||
+3
-1
@@ -18,6 +18,8 @@
|
|||||||
"typecheck": "tsc -p tsconfig.test.json",
|
"typecheck": "tsc -p tsconfig.test.json",
|
||||||
"check:core-purity": "node scripts/check-core-purity.mjs",
|
"check:core-purity": "node scripts/check-core-purity.mjs",
|
||||||
"stats:bot": "vitest run --config vitest.stats.mts",
|
"stats:bot": "vitest run --config vitest.stats.mts",
|
||||||
"build:art": "node scripts/build-art.mjs"
|
"build:art": "node scripts/build-art.mjs",
|
||||||
|
"check:scenes": "node scripts/check-scenes.mjs",
|
||||||
|
"check": "npm run check:core-purity && npm run check:scenes && npm run typecheck && npm test"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
// Checks the scenes and prefabs for the mistakes that only show up at runtime,
|
||||||
|
// and that the editor viewport actively hides from you.
|
||||||
|
//
|
||||||
|
// npm run check:scenes
|
||||||
|
//
|
||||||
|
// The one that cost a whole debugging round: a node created through the editor
|
||||||
|
// tool server lands on the DEFAULT layer, but a Canvas camera renders UI_2D
|
||||||
|
// only. The scene view draws every layer, so it looks perfect in the editor and
|
||||||
|
// the build shows nothing but the camera's clear colour.
|
||||||
|
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
|
||||||
|
const UI_2D = 33554432;
|
||||||
|
const ROOTS = ['assets/scenes', 'assets/prefabs'];
|
||||||
|
|
||||||
|
const problems = [];
|
||||||
|
|
||||||
|
function collect(dir, out = []) {
|
||||||
|
for (const entry of readdirSync(dir)) {
|
||||||
|
const path = join(dir, entry);
|
||||||
|
if (statSync(path).isDirectory()) collect(path, out);
|
||||||
|
else if (path.endsWith('.scene') || path.endsWith('.prefab')) out.push(path);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Scene files are a flat array of objects that reference each other by index. */
|
||||||
|
function load(path) {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
||||||
|
return Array.isArray(parsed) ? parsed : [];
|
||||||
|
} catch (error) {
|
||||||
|
problems.push(`${path}: could not be parsed — ${error.message}`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkLayers(path, objects) {
|
||||||
|
const wrong = objects
|
||||||
|
.filter((o) => o && o.__type__ === 'cc.Node' && o._layer !== UI_2D)
|
||||||
|
.map((o) => `${o._name ?? '?'} (layer ${o._layer})`);
|
||||||
|
|
||||||
|
if (wrong.length > 0) {
|
||||||
|
problems.push(
|
||||||
|
`${path}: ${wrong.length} node(s) are not on the UI_2D layer, so the `
|
||||||
|
+ `Canvas camera will not draw them: ${wrong.slice(0, 6).join(', ')}`
|
||||||
|
+ `${wrong.length > 6 ? ', …' : ''}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkCanvas(path, objects) {
|
||||||
|
const canvases = objects.filter((o) => o && o.__type__ === 'cc.Canvas');
|
||||||
|
if (path.endsWith('.scene') && canvases.length === 0) {
|
||||||
|
problems.push(`${path}: no cc.Canvas, so nothing 2D will render`);
|
||||||
|
}
|
||||||
|
for (const canvas of canvases) {
|
||||||
|
if (!canvas._cameraComponent) {
|
||||||
|
problems.push(`${path}: a Canvas has no camera assigned`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkCameras(path, objects) {
|
||||||
|
for (const camera of objects.filter((o) => o && o.__type__ === 'cc.Camera')) {
|
||||||
|
if (typeof camera._visibility === 'number' && (camera._visibility & UI_2D) === 0) {
|
||||||
|
problems.push(`${path}: a Camera cannot see the UI_2D layer (visibility ${camera._visibility})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Any script property left holding an empty uuid is an unwired inspector slot. */
|
||||||
|
function checkReferences(path, objects) {
|
||||||
|
for (const object of objects) {
|
||||||
|
if (!object || typeof object !== 'object') continue;
|
||||||
|
const type = object.__type__;
|
||||||
|
// Engine types legitimately leave optional slots empty; our own scripts
|
||||||
|
// are the ones whose every reference is deliberate.
|
||||||
|
if (typeof type !== 'string' || type.startsWith('cc.')) continue;
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(object)) {
|
||||||
|
if (value && typeof value === 'object' && value.__uuid__ === '') {
|
||||||
|
problems.push(`${path}: ${type}.${key} is an empty reference`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const root of ROOTS) {
|
||||||
|
for (const path of collect(root)) {
|
||||||
|
const objects = load(path);
|
||||||
|
checkLayers(path, objects);
|
||||||
|
checkCanvas(path, objects);
|
||||||
|
checkCameras(path, objects);
|
||||||
|
checkReferences(path, objects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (problems.length > 0) {
|
||||||
|
console.error(`Scene problems:\n - ${problems.join('\n - ')}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('scenes OK');
|
||||||
Reference in New Issue
Block a user