first commmit
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
import { _decorator, Component, director, Node, Label, Button, UITransform, Color, Vec3, Graphics } from 'cc';
|
||||
import { UIFlowMgr, ISceneEnter } from '../ui/UIFlowMgr';
|
||||
import { DESIGN_WIDTH, DESIGN_HEIGHT } from '../common/Constants';
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/** Maps abstract SceneId → physical Cocos Creator scene name. */
|
||||
const SCENE_MAP: Record<string, string> = {
|
||||
boot: 'Boot',
|
||||
story_intro: 'StoryIntro',
|
||||
main_menu: 'MainMenu',
|
||||
// level_select currently reuses MainMenu until a dedicated LevelSelect scene exists.
|
||||
level_select: 'MainMenu',
|
||||
// `gameplay` is dispatched by levelId — resolved at runtime.
|
||||
gameplay: 'Level_1_1',
|
||||
settlement: 'Settlement',
|
||||
// Settings panel is overlayed on MainMenu for the MVP.
|
||||
settings: 'MainMenu',
|
||||
};
|
||||
|
||||
/** levelId → physical scene name mapping (chapter 1 only). */
|
||||
const LEVEL_SCENE_MAP: Record<string, string> = {
|
||||
'1-1': 'Level_1_1',
|
||||
'1-2': 'Level_1_2',
|
||||
'1-3': 'Level_1_3',
|
||||
'1-4': 'Level_1_4',
|
||||
'1-5': 'Level_1_5',
|
||||
'1-5-boss': 'Boss_ShuangHuanFang',
|
||||
};
|
||||
|
||||
/**
|
||||
* MainMenu scene entry (task 9.2 hookup).
|
||||
*
|
||||
* Owns a `UIFlowMgr` instance and translates each abstract `onSceneEnter`
|
||||
* callback into a concrete `director.loadScene` call. Attach this component
|
||||
* to the root node of `MainMenu.scene`.
|
||||
*
|
||||
* When `autoBuildUI` is enabled (default), two centered buttons (Start /
|
||||
* Settings) and a title label are created programmatically so the scene is
|
||||
* usable out of the box even before any art pass.
|
||||
*/
|
||||
@ccclass('MainMenuEntry')
|
||||
export class MainMenuEntry extends Component {
|
||||
@property({ tooltip: '是否自动生成 Start / Settings 按钮 (方便没美术时就能跑通)' })
|
||||
public autoBuildUI: boolean = true;
|
||||
|
||||
private flow: UIFlowMgr | undefined;
|
||||
|
||||
protected onLoad(): void {
|
||||
this.flow = new UIFlowMgr(undefined, {
|
||||
onSceneEnter: (ev) => this.handleSceneEnter(ev),
|
||||
});
|
||||
if (this.autoBuildUI) this.buildDefaultUI();
|
||||
}
|
||||
|
||||
/** Bind this to the "Start" button's click event in the Inspector. */
|
||||
public onPressStart(): void {
|
||||
this.flow?.onPressStartGame();
|
||||
}
|
||||
|
||||
/** Bind this to the "Settings" button's click event. */
|
||||
public onPressSettings(): void {
|
||||
this.flow?.onOpenSettings();
|
||||
}
|
||||
|
||||
private handleSceneEnter(ev: ISceneEnter): void {
|
||||
const payload = ev.payload ?? {};
|
||||
if (ev.scene === 'gameplay' && typeof payload.levelId === 'string') {
|
||||
const physical = LEVEL_SCENE_MAP[payload.levelId];
|
||||
if (physical) {
|
||||
director.loadScene(physical);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const physical = SCENE_MAP[ev.scene];
|
||||
if (physical) director.loadScene(physical);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Auto-built UI (development affordance; art pass will replace it)
|
||||
// ------------------------------------------------------------------
|
||||
private buildDefaultUI(): void {
|
||||
// Ensure the host node has a UITransform matching the design resolution.
|
||||
ensureCanvasSize(this.node);
|
||||
|
||||
// Title.
|
||||
createLabel(this.node, '影 之 传 说', 0, 140, 44, Color.WHITE);
|
||||
|
||||
// Start button (centered, 40 above origin).
|
||||
createButton(this.node, 'Start', 0, 40, 220, 60, () => this.onPressStart());
|
||||
|
||||
// Settings button (centered, 40 below origin).
|
||||
createButton(this.node, 'Settings', 0, -40, 220, 60, () => this.onPressSettings());
|
||||
|
||||
// Hint line at the bottom.
|
||||
createLabel(this.node, 'Chapter 1 · MVP', 0, -200, 20, new Color(180, 180, 180, 255));
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// Shared UI helpers — intentionally kept inline (no external module) so
|
||||
// each Scene Entry stays self-contained and easy to remove once real UI is
|
||||
// authored in the editor.
|
||||
// ======================================================================
|
||||
|
||||
function ensureCanvasSize(host: Node): void {
|
||||
let ut = host.getComponent(UITransform);
|
||||
if (!ut) ut = host.addComponent(UITransform);
|
||||
ut.setContentSize(DESIGN_WIDTH, DESIGN_HEIGHT);
|
||||
host.setPosition(0, 0, 0);
|
||||
}
|
||||
|
||||
function createLabel(parent: Node, text: string, x: number, y: number, fontSize: number, color: Color): Node {
|
||||
const n = new Node('AutoLabel');
|
||||
n.layer = parent.layer;
|
||||
parent.addChild(n);
|
||||
const ut = n.addComponent(UITransform);
|
||||
ut.setContentSize(DESIGN_WIDTH, fontSize * 1.6);
|
||||
const lb = n.addComponent(Label);
|
||||
lb.useSystemFont = true;
|
||||
lb.string = text;
|
||||
lb.fontSize = fontSize;
|
||||
lb.lineHeight = Math.floor(fontSize * 1.2);
|
||||
lb.color = color;
|
||||
lb.horizontalAlign = 1; // CENTER
|
||||
lb.verticalAlign = 1; // CENTER
|
||||
n.setPosition(new Vec3(x, y, 0));
|
||||
return n;
|
||||
}
|
||||
|
||||
function createButton(
|
||||
parent: Node,
|
||||
text: string,
|
||||
x: number,
|
||||
y: number,
|
||||
w: number,
|
||||
h: number,
|
||||
onClick: () => void
|
||||
): Node {
|
||||
const n = new Node(`Btn_${text}`);
|
||||
n.layer = parent.layer;
|
||||
parent.addChild(n);
|
||||
const ut = n.addComponent(UITransform);
|
||||
ut.setContentSize(w, h);
|
||||
|
||||
// Background drawn via Graphics — avoids needing any texture asset.
|
||||
const g = n.addComponent(Graphics);
|
||||
g.fillColor = new Color(40, 40, 60, 230);
|
||||
g.rect(-w / 2, -h / 2, w, h);
|
||||
g.fill();
|
||||
g.strokeColor = new Color(200, 200, 220, 255);
|
||||
g.lineWidth = 2;
|
||||
g.rect(-w / 2, -h / 2, w, h);
|
||||
g.stroke();
|
||||
|
||||
// Label child for the text.
|
||||
const labelNode = new Node('Label');
|
||||
labelNode.layer = parent.layer;
|
||||
n.addChild(labelNode);
|
||||
const lut = labelNode.addComponent(UITransform);
|
||||
lut.setContentSize(w, h);
|
||||
const lb = labelNode.addComponent(Label);
|
||||
lb.useSystemFont = true;
|
||||
lb.string = text;
|
||||
lb.fontSize = 24;
|
||||
lb.lineHeight = 28;
|
||||
lb.color = Color.WHITE;
|
||||
lb.horizontalAlign = 1;
|
||||
lb.verticalAlign = 1;
|
||||
|
||||
const btn = n.addComponent(Button);
|
||||
btn.transition = Button.Transition.SCALE;
|
||||
btn.target = n;
|
||||
btn.zoomScale = 0.95;
|
||||
n.on(Node.EventType.TOUCH_END, onClick, n);
|
||||
|
||||
n.setPosition(new Vec3(x, y, 0));
|
||||
return n;
|
||||
}
|
||||
|
||||
// Re-export helpers so sibling Scene Entries can reuse them.
|
||||
export { ensureCanvasSize, createLabel, createButton };
|
||||
Reference in New Issue
Block a user