220 lines
11 KiB
TypeScript
220 lines
11 KiB
TypeScript
import type GameplayIntro from "../../GameplayIntro/script/GameplayIntro";
|
||
import { getLevelGameplay } from "../../Script/module/Tool/LevelGameplay";
|
||
const { ccclass } = cc._decorator;
|
||
|
||
/** 独立测试面板:直接加载正式 GameplayIntro 预制体,不运行登录/对局,也不写入存档。 */
|
||
@ccclass
|
||
export default class TestLayoutPanel extends cc.Component {
|
||
private config: any[] = [];
|
||
// 配置准备好才能打开;loading 防止资源未返回时重复创建多个面板。
|
||
private ready = false;
|
||
private loading = false;
|
||
private popup: cc.Node = null;
|
||
private controller: any = null;
|
||
private stage: cc.Node = null;
|
||
private status: cc.Label = null;
|
||
private levelInput: cc.EditBox = null;
|
||
private level = 511;
|
||
private allGameplay = false;
|
||
private entries: any[] = [];
|
||
private levelRequest = 0;
|
||
private infoButton: cc.Node = null;
|
||
|
||
/** 创建测试按钮、关卡输入框和预览区域;这些辅助控件都属于可删除的 test 包。 */
|
||
onLoad() {
|
||
this.node.color = cc.Color.WHITE;
|
||
this.box(this.node, "Background", 0, 0, 1080, 2400, new cc.Color(27, 32, 43));
|
||
this.label(this.node, "本关玩法 · 问号按钮测试", 0, 867, 42);
|
||
this.label(this.node, "按关卡检查,或查看全部玩法 / 标题、道具、说明、分页", 0, 811, 25);
|
||
this.stage = new cc.Node("PreviewStage");
|
||
this.stage.parent = this.node;
|
||
this.stage.setPosition(0, 25);
|
||
this.stage.setContentSize(1080, 1600);
|
||
this.stage.scale = 0.78;
|
||
// 测试控件显示在遮罩上方,方便面板打开时继续切换测试关卡。
|
||
this.infoButton = this.createInfoButton(this.node, 440, 840, () => this.reopen());
|
||
this.infoButton.active = false;
|
||
const controls = new cc.Node("Controls");
|
||
controls.parent = this.node;
|
||
controls.zIndex = 10;
|
||
// 读取真实关卡,分别验证零项、单项、双项和多项。
|
||
[1, 7, 44, 511].forEach((level, index) => {
|
||
this.button(controls, level === 1 ? "无道具 · 1关" : level === 7 ? "1页 · 7关" : level === 44 ? "2页 · 44关" : "5页 · 511关",
|
||
-375 + index * 250, 735, 235, () => this.open(level));
|
||
});
|
||
const input = this.box(controls, "LevelInput", -375, 660, 205, 58, new cc.Color(65, 75, 92));
|
||
input.active = false;
|
||
this.levelInput = input.addComponent(cc.EditBox);
|
||
this.levelInput.textLabel = this.label(input, "511", 0, 0, 28);
|
||
this.levelInput.placeholderLabel = this.label(input, "输入关卡", 0, 0, 26);
|
||
[this.levelInput.textLabel, this.levelInput.placeholderLabel].forEach(label => {
|
||
label.node.setContentSize(185, 58);
|
||
label.node.setPosition(-92.5, 29);
|
||
});
|
||
this.levelInput.fontSize = 28;
|
||
this.levelInput.fontColor = cc.Color.WHITE;
|
||
this.levelInput.inputMode = cc.EditBox.InputMode.NUMERIC;
|
||
this.levelInput.string = "511";
|
||
input.active = true;
|
||
this.button(controls, "按关卡打开", -115, 660, 230, () => {
|
||
const level = Number(this.levelInput.string);
|
||
if (!Number.isInteger(level) || level < 1) { this.status.string = "请输入大于或等于 1 的整数关卡"; return; }
|
||
this.open(level);
|
||
});
|
||
this.button(controls, "重新打开", 140, 660, 230, () => this.reopen());
|
||
this.button(controls, "全部玩法", 395, 660, 230, () => this.openAllGameplay());
|
||
this.status = this.label(controls, "正在加载正式弹窗和介绍资源…", 0, -635, 28);
|
||
this.status.node.width = 1020;
|
||
this.status.overflow = cc.Label.Overflow.RESIZE_HEIGHT;
|
||
["第一页", "上一页", "下一页", "最后页"].forEach((title, index) => {
|
||
const actions = ["first", "previous", "next", "last"];
|
||
this.button(controls, title, -375 + index * 250, -730, 230, () => {
|
||
if (this.popup && this.popup.active && this.controller) this.controller.previewNavigate(actions[index]);
|
||
});
|
||
});
|
||
this.label(controls, "无道具隐藏问号,单页不可滑动,打开先看最左端最新玩法", 0, -812, 25);
|
||
this.label(controls, "点击面板外空白处关闭;读取本地关卡,不修改游戏进度", 0, -856, 25);
|
||
// 只读取正式解锁配置,输入的测试关卡保存在本组件,不修改玩家实际进度。
|
||
cc.resources.load("Json/NEW_LEVEL", cc.JsonAsset, (error, asset: cc.JsonAsset) => {
|
||
if (!cc.isValid(this, true)) return;
|
||
if (error) { this.status.string = "解锁配置加载失败:" + error.message; return; }
|
||
this.config = asset.json.NEW_LEVEL;
|
||
this.ready = true;
|
||
this.open(this.level);
|
||
|
||
});
|
||
}
|
||
|
||
/** 用指定测试关卡打开正式预制体;已有面板直接复用,否则异步加载并实例化。 */
|
||
private open(level: number) {
|
||
if (!this.ready) return;
|
||
this.allGameplay = false;
|
||
this.level = level;
|
||
this.levelInput.string = String(level);
|
||
this.infoButton.active = false;
|
||
if (this.popup && this.popup.active) this.controller.close();
|
||
this.entries = [];
|
||
this.status.string = "正在读取第 " + level + " 关…";
|
||
const token = ++this.levelRequest;
|
||
cc.assetManager.loadBundle("custom", (error, bundle) => {
|
||
if (!cc.isValid(this, true) || token !== this.levelRequest) return;
|
||
if (error) { this.status.string = "关卡包加载失败:" + error.message; return; }
|
||
bundle.load("Json/level" + level, cc.JsonAsset, (loadError, asset: cc.JsonAsset) => {
|
||
if (!cc.isValid(this, true) || token !== this.levelRequest) return;
|
||
if (loadError) { this.status.string = "本地关卡加载失败:" + loadError.message; return; }
|
||
this.entries = getLevelGameplay(this.config, asset.json);
|
||
this.infoButton.active = this.entries.length > 0;
|
||
if (!this.entries.length) { this.status.string = "本关没有特殊道具,问号已隐藏"; return; }
|
||
this.showEntries();
|
||
});
|
||
});
|
||
}
|
||
|
||
/** 策划验图入口:不按关卡过滤,仍使用正式面板按相邻三页加载资源。 */
|
||
private openAllGameplay() {
|
||
if (!this.ready) return;
|
||
++this.levelRequest; // 丢弃切换前尚未返回的关卡请求。
|
||
this.allGameplay = true;
|
||
const seen = new Set<string>();
|
||
this.entries = this.config.filter(entry => {
|
||
if (seen.has(entry.name)) return false;
|
||
seen.add(entry.name);
|
||
return true;
|
||
});
|
||
this.infoButton.active = this.entries.length > 0;
|
||
this.showEntries();
|
||
}
|
||
|
||
private reopen() {
|
||
if (this.allGameplay) this.openAllGameplay();
|
||
else this.open(this.level);
|
||
}
|
||
|
||
private showEntries() {
|
||
if (this.popup) {
|
||
this.controller.open(this.entries);
|
||
return;
|
||
}
|
||
if (this.loading) return;
|
||
this.loading = true;
|
||
cc.assetManager.loadBundle("gameplay_intro", (error, bundle) => {
|
||
if (!cc.isValid(this, true)) return;
|
||
if (error) { this.loading = false; this.status.string = "功能包加载失败:" + error.message; return; }
|
||
bundle.load("prefab/GameplayIntro", cc.Prefab, (prefabError, prefab: cc.Prefab) => {
|
||
if (!cc.isValid(this, true)) return;
|
||
this.loading = false;
|
||
if (prefabError) { this.status.string = "面板预制体加载失败:" + prefabError.message; return; }
|
||
// 测试挂在 stage 下;正式游戏由 SceneManager 挂在场景 tipPanel 下。
|
||
this.popup = cc.instantiate(prefab);
|
||
this.popup.parent = this.stage;
|
||
this.popup.active = false;
|
||
this.controller = this.popup.getComponent("GameplayIntro") as GameplayIntro;
|
||
this.popup.on("guide-page-changed", state => {
|
||
this.status.string = (this.allGameplay ? "全部玩法" : "测试关卡 " + this.level) + " | " + (state.index + 1) + " / " + state.total + " 页\n" +
|
||
(state.entry ? state.entry.name + " · " + state.entry.tips : "暂无玩法");
|
||
});
|
||
this.popup.on("intro-closed", () => this.status.string = this.entries.length ? "面板已关闭,点击右上角问号重新查看" : "本关没有特殊道具,问号已隐藏");
|
||
if (this.entries.length) this.controller.open(this.entries);
|
||
});
|
||
});
|
||
}
|
||
|
||
// 仅测试场景用代码创建问号;正式游戏入口应由 GameScene 中的 Button 节点提供。
|
||
private createInfoButton(parent: cc.Node, x: number, y: number, open: () => void): cc.Node {
|
||
const node = new cc.Node("GameplayInfoButton");
|
||
node.parent = parent;
|
||
node.setPosition(x, y);
|
||
node.setContentSize(88, 88);
|
||
const sprite = node.addComponent(cc.Sprite);
|
||
sprite.sizeMode = cc.Sprite.SizeMode.CUSTOM;
|
||
cc.assetManager.loadBundle("gameplay_intro", (error, bundle) => {
|
||
if (!cc.isValid(node, true)) return;
|
||
if (error) { cc.error("History button bundle", error); return; }
|
||
bundle.load("Texture/wenhao", cc.SpriteFrame, (imageError, frame: cc.SpriteFrame) => {
|
||
if (!cc.isValid(node, true)) return;
|
||
if (imageError) { cc.error("History button image", imageError); return; }
|
||
sprite.spriteFrame = frame;
|
||
const size = frame.getOriginalSize();
|
||
const scale = Math.min(88 / size.width, 88 / size.height);
|
||
node.setContentSize(size.width * scale, size.height * scale);
|
||
});
|
||
});
|
||
node.addComponent(cc.Button);
|
||
node.on("click", open);
|
||
return node;
|
||
}
|
||
|
||
|
||
// 以下三个工具方法用于绘制测试控件的底色、文字和按钮。
|
||
private box(parent: cc.Node, name: string, x: number, y: number, width: number, height: number, color: cc.Color) {
|
||
const node = new cc.Node(name);
|
||
node.parent = parent;
|
||
node.setPosition(x, y);
|
||
node.setContentSize(width, height);
|
||
const graphics = node.addComponent(cc.Graphics);
|
||
graphics.fillColor = color;
|
||
graphics.roundRect(-width / 2, -height / 2, width, height, 12);
|
||
graphics.fill();
|
||
return node;
|
||
}
|
||
|
||
private label(parent: cc.Node, text: string, x: number, y: number, size: number) {
|
||
const node = new cc.Node("Label");
|
||
node.parent = parent;
|
||
node.setPosition(x, y);
|
||
const label = node.addComponent(cc.Label);
|
||
label.string = text;
|
||
label.fontSize = size;
|
||
label.lineHeight = size + 10;
|
||
label.horizontalAlign = cc.Label.HorizontalAlign.CENTER;
|
||
return label;
|
||
}
|
||
|
||
private button(parent: cc.Node, text: string, x: number, y: number, width: number, action: () => void) {
|
||
const node = this.box(parent, text, x, y, width, 58, new cc.Color(55, 91, 143));
|
||
this.label(node, text, 0, 0, 26);
|
||
node.addComponent(cc.Button);
|
||
node.on("click", action, this);
|
||
}
|
||
}
|