update spirit
This commit is contained in:
@@ -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;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user