Files
slide_block/assets/scripts/LevelBuilder.ts
1970-01-01 08:27:52 +08:00

268 lines
7.2 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 {
Node,
Prefab,
instantiate,
Sprite,
SpriteFrame,
UITransform,
Vec3,
} from "cc";
import { BoardShapes, BoardShapeName } from "./BoardShape";
export interface LevelConfig {
rows?: number;
cols?: number;
blockSize?: number;
tileTypes?: number;
currentShape?: BoardShapeName;
isTutorial?: boolean;
fixedBoard?: number[][];
}
export class LevelBuilder {
public board: number[][] = [];
public blockNodes: (Node | null)[][] = [];
public rows: number = 0;
public cols: number = 0;
private node: Node;
private blockPrefab: Prefab;
private spriteFrames: SpriteFrame[] = [];
private blockSize: number = 100;
private tileTypes: number = 5;
public currentShape: BoardShapeName = "rectangle";
private fixedBoard: number[][] | null = null;
constructor(
node: Node,
blockPrefab: Prefab,
spriteFrames: SpriteFrame[],
config?: LevelConfig,
) {
this.node = node;
this.blockPrefab = blockPrefab;
this.spriteFrames = spriteFrames;
if (config) {
this.blockSize = config.blockSize ?? this.blockSize;
this.tileTypes = config.tileTypes ?? this.tileTypes;
if (config.currentShape) this.currentShape = config.currentShape;
if (config.fixedBoard) this.fixedBoard = config.fixedBoard;
}
}
public buildBoard(): void {
if (this.fixedBoard) {
this.buildFixedBoard();
return;
}
const shapeTemplate = BoardShapes[this.currentShape];
if (!shapeTemplate) throw new Error(`Shape "${this.currentShape}" 未定义`);
const maxCols = Math.max(...shapeTemplate.map((row) => row.length));
const rows = shapeTemplate.length;
// 自动补齐矩形
const fixedTemplate: number[][] = [];
for (let r = 0; r < rows; r++) {
fixedTemplate[r] = [];
for (let c = 0; c < maxCols; c++) {
fixedTemplate[r][c] = shapeTemplate[r][c] ?? 0;
}
}
// 收集可用格子
const validCells = [];
for (let r = 0; r < rows; r++) {
for (let c = 0; c < maxCols; c++) {
if (fixedTemplate[r][c] === 1) validCells.push({ r, c });
}
}
if (validCells.length % 2 !== 0) throw new Error("有效格子数量必须为偶数");
// 随机生成配对
let tiles: number[] = [];
for (let i = 0; i < validCells.length / 2; i++) {
const id = i % this.tileTypes;
tiles.push(id, id);
}
for (let i = tiles.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[tiles[i], tiles[j]] = [tiles[j], tiles[i]];
}
let tileIndex = 0;
this.board = [];
this.blockNodes = [];
this.rows = rows;
this.cols = maxCols;
for (let r = 0; r < rows; r++) {
this.board[r] = [];
this.blockNodes[r] = [];
for (let c = 0; c < maxCols; c++) {
if (fixedTemplate[r][c] === 1) {
const tileId = tiles[tileIndex++];
this.board[r][c] = tileId;
const block = this.createNormalBlock(tileId);
block.setPosition(this.getPos(r, c));
this.node.addChild(block);
this.blockNodes[r][c] = block;
} else if (fixedTemplate[r][c] === -1) {
this.board[r][c] = -1;
this.blockNodes[r][c] = null;
} else {
this.board[r][c] = -2;
const stone = this.createStoneBlock();
stone.setPosition(this.getPos(r, c));
this.node.addChild(stone);
this.blockNodes[r][c] = stone;
}
}
}
}
private buildFixedBoard() {
this.rows = this.fixedBoard.length;
this.cols = this.fixedBoard[0].length;
this.board = [];
this.blockNodes = [];
for (let r = 0; r < this.rows; r++) {
this.board[r] = [];
this.blockNodes[r] = [];
for (let c = 0; c < this.cols; c++) {
const val = this.fixedBoard[r][c];
this.board[r][c] = val;
if (val >= 0) {
const block = this.createNormalBlock(val);
block.setPosition(this.getPos(r, c));
this.node.addChild(block);
this.blockNodes[r][c] = block;
} else if (val === -1) {
this.blockNodes[r][c] = null;
} else if (val === -2) {
const stone = this.createStoneBlock();
stone.setPosition(this.getPos(r, c));
this.node.addChild(stone);
this.blockNodes[r][c] = stone;
}
}
}
}
private createNormalBlock(tileId: number) {
const block = instantiate(this.blockPrefab);
const bgNode = block.getChildByName("Bg");
const bgUI =
bgNode.getComponent(UITransform) ?? bgNode.addComponent(UITransform);
bgUI.setContentSize(this.blockSize, this.blockSize);
const iconNode = block.getChildByName("Icon");
const iconUI =
iconNode.getComponent(UITransform) ?? iconNode.addComponent(UITransform);
const iconUI_sprite = iconNode.getComponent(Sprite)!;
iconUI_sprite.spriteFrame = this.spriteFrames[tileId];
iconUI.setContentSize(this.blockSize * 0.7, this.blockSize * 0.7);
iconNode.setPosition(0, 0, 0);
return block;
}
private createStoneBlock() {
const stone = instantiate(this.blockPrefab);
const bgNode = stone.getChildByName("Bg");
const bgUI =
bgNode.getComponent(UITransform) ?? bgNode.addComponent(UITransform);
bgUI.setContentSize(this.blockSize, this.blockSize);
const iconNode = stone.getChildByName("Icon");
iconNode.active = false;
return stone;
}
private getPos(row: number, col: number): Vec3 {
const x =
col * this.blockSize -
(this.cols * this.blockSize) / 2 +
this.blockSize / 2;
const y =
(this.rows * this.blockSize) / 2 -
row * this.blockSize -
this.blockSize / 2;
return new Vec3(x, y, 0);
}
/**
* 新建 UI 节点,并设置类型和位置
*/
public createBlockNode(type: number, r: number, c: number): Node {
let block: Node;
if (type >= 0) {
// 普通方块
block = this.createNormalBlock(type);
} else if (type === -2) {
// 石头障碍
block = this.createStoneBlock();
} else {
// 空格不需要节点
return null!;
}
block.setPosition(this.getPos(r, c));
this.node.addChild(block);
return block;
}
/**
* 更新节点的贴图/颜色/数字等显示
*/
public setBlockType(node: Node, type: number): void {
if (!node) return;
const iconNode = node.getChildByName("Icon");
if (!iconNode) return;
if (type >= 0) {
// 普通方块
iconNode.active = true;
const iconUI =
iconNode.getComponent(UITransform) ??
iconNode.addComponent(UITransform);
const iconUI_sprite = iconNode.getComponent(Sprite)!;
iconUI_sprite.spriteFrame = this.spriteFrames[type];
iconUI.setContentSize(this.blockSize * 0.7, this.blockSize * 0.7);
iconNode.setPosition(0, 0, 0);
} else if (type === -2) {
// 石头
iconNode.active = false;
} else {
// 空格
iconNode.active = false;
}
}
/**
* 更新节点的坐标位置UI 上)
*/
public setBlockPosition(node: Node, r: number, c: number): void {
if (!node) return;
node.setPosition(this.getPos(r, c));
}
/**
* 销毁节点或隐藏
*/
public destroyBlockNode(node: Node): void {
if (!node) return;
node.destroy(); // 如果想复用可改为 node.active = false
}
}