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>
82 lines
2.9 KiB
TypeScript
82 lines
2.9 KiB
TypeScript
import { _decorator, Component, director, game, Label, resources, SpriteFrame } from 'cc';
|
|
import { App } from './App';
|
|
import { CARDS_DIR } from '../ui/game/CardLibrary';
|
|
|
|
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
|
|
* to the lobby. Everything that must exist before any gameplay runs belongs
|
|
* 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')
|
|
export class Bootstrap extends Component {
|
|
@property({ tooltip: 'Scene to open once the modules are ready.' })
|
|
nextScene = 'lobby';
|
|
|
|
@property({ type: Label, tooltip: 'Shows what the loader is doing.' })
|
|
statusLabel: Label | null = null;
|
|
|
|
private _handedOver = false;
|
|
|
|
start(): void {
|
|
// 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.
|
|
game.frameRate = 60;
|
|
|
|
this._report('starting modules');
|
|
App.create();
|
|
|
|
// 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
|
|
// exactly where a player expects to wait for it.
|
|
this._report('loading cards');
|
|
resources.loadDir(CARDS_DIR, SpriteFrame, (error, frames) => {
|
|
if (error) {
|
|
console.error('[bootstrap] could not preload the card art', error);
|
|
} else {
|
|
console.log(`[bootstrap] ${frames.length} card sprites ready`);
|
|
}
|
|
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 {
|
|
if (this._handedOver) {
|
|
return;
|
|
}
|
|
this._handedOver = true;
|
|
|
|
this._report('opening lobby');
|
|
director.loadScene(this.nextScene, (error) => {
|
|
if (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;
|
|
}
|
|
}
|
|
}
|