Files
fool-cardgame/assets/scripts/app/Bootstrap.ts
T
ftcandClaude Opus 5 43f5bc5df3 Починен зелёный экран: все узлы были не на том слое
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>
2026-09-13 11:58:17 +03:00

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;
}
}
}