update spirit

This commit is contained in:
jakciehan
2026-06-07 22:10:03 +08:00
parent 427a33c55b
commit 9c57deff6d
82 changed files with 5465 additions and 149 deletions
@@ -1,9 +1,16 @@
import { _decorator, Component, director, Label, Node, Color, EventTouch, UITransform } from 'cc';
import { _decorator, Component, director, Label, Node, Color, EventTouch, UITransform, Sprite, SpriteFrame, Texture2D, ImageAsset, Rect, Size, resources } from 'cc';
import { ConfigMgr } from '../data/ConfigMgr';
import { CCJsonLoader } from './CCJsonLoader';
import { StorySceneCtrl } from '../ui/StorySceneCtrl';
import { ensureCanvasSize, createLabel, createButton } from './MainMenuEntry';
import {
ensureCanvasSize,
createLabel,
createButton,
createSprite,
createSolidRect,
} from './MainMenuEntry';
import { DESIGN_WIDTH, DESIGN_HEIGHT } from '../common/Constants';
import { IStoryPageConfig } from '../data/Interfaces';
const { ccclass, property } = _decorator;
@@ -30,8 +37,15 @@ export class StorySceneEntry extends Component {
public autoBuildUI: boolean = true;
private ctrl: StorySceneCtrl | undefined;
private bgNode: Node | null = null;
protected async onLoad(): Promise<void> {
// Always ensure the illustration bgNode exists (even if the scene
// was hand-built in the editor and labelNode is already bound).
if (!this.bgNode) {
this.ensureBgNode();
}
if (this.autoBuildUI && !this.labelNode) {
this.buildDefaultUI();
}
@@ -41,6 +55,7 @@ export class StorySceneEntry extends Component {
const cfg = await this.loadStoryConfig();
this.ctrl = new StorySceneCtrl(cfg, undefined, {
onTextChanged: (text) => this.updateLabel(text),
onPageEntered: (page) => this.swapIllustration(page),
onFinished: () => director.loadScene('Level_1_1'),
});
const outcome = this.ctrl.start();
@@ -75,16 +90,75 @@ export class StorySceneEntry extends Component {
return mgr.story(this.storyId);
}
private buildDefaultUI(): void {
/** Ensure the full-screen illustration layer exists. Called even when
* the scene was hand-built in the editor (labelNode already bound). */
private ensureBgNode(): void {
ensureCanvasSize(this.node);
// Ensure the root node can receive touch (size = design resolution).
// Central typewriter label.
this.labelNode = createLabel(this.node, '', 0, 0, 28, Color.WHITE);
this.bgNode = new Node('StoryIllustration');
this.bgNode.layer = this.node.layer;
// Added before any scrim/label nodes so it renders behind them.
this.node.addChild(this.bgNode);
const bgUt = this.bgNode.addComponent(UITransform);
bgUt.setContentSize(DESIGN_WIDTH, DESIGN_HEIGHT);
const bgSp = this.bgNode.addComponent(Sprite);
bgSp.sizeMode = Sprite.SizeMode.CUSTOM;
this.bgNode.setPosition(0, 0, 0);
}
private buildDefaultUI(): void {
// bgNode is already created by ensureBgNode() — just build the
// text overlay, scrim and skip button.
// Dark scrim below the text area for readability.
createSolidRect(
this.node,
0,
-DESIGN_HEIGHT / 2 + 110,
DESIGN_WIDTH,
180,
new Color(0, 0, 0, 170),
);
// Central typewriter label (sits on top of the scrim).
this.labelNode = createLabel(this.node, '', 0, -DESIGN_HEIGHT / 2 + 110, 26, Color.WHITE);
const ut = this.labelNode.getComponent(UITransform);
if (ut) ut.setContentSize(DESIGN_WIDTH - 80, DESIGN_HEIGHT - 120);
if (ut) ut.setContentSize(DESIGN_WIDTH - 80, 160);
const lb = this.labelNode.getComponent(Label);
if (lb) lb.enableWrapText = true;
// Skip button at bottom-right.
const skipX = DESIGN_WIDTH / 2 - 90;
const skipY = -DESIGN_HEIGHT / 2 + 50;
const skipY = -DESIGN_HEIGHT / 2 + 40;
createButton(this.node, 'Skip >>', skipX, skipY, 140, 50, () => this.onSkip());
}
/** Swap the full-screen illustration when the controller enters a new page. */
private swapIllustration(page: IStoryPageConfig): void {
if (!this.bgNode) return;
const sp = this.bgNode.getComponent(Sprite);
if (!sp) return;
const path = page.illustration;
// Strategy 1: direct SpriteFrame load (works with sprite-frame meta).
resources.load(path, SpriteFrame, (err, sf) => {
if (!err && sf) {
if (this.bgNode && this.bgNode.isValid) sp.spriteFrame = sf as SpriteFrame;
return;
}
// Strategy 2: ImageAsset → Texture2D → SpriteFrame fallback.
resources.load(path, ImageAsset, (err2, imgAsset) => {
if (err2 || !imgAsset) {
console.warn(`[StorySceneEntry] failed to load illustration '${path}':`, err, err2);
return;
}
if (!this.bgNode || !this.bgNode.isValid) return;
const tex = new Texture2D();
tex.image = imgAsset as ImageAsset;
const frame = new SpriteFrame();
frame.texture = tex;
frame.rect = new Rect(0, 0, tex.width, tex.height);
frame.originalSize = new Size(tex.width, tex.height);
sp.spriteFrame = frame;
});
});
}
}