MatchMaster/assets/test/script/TestLayoutPanel.ts

220 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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