first commit

This commit is contained in:
zzc
1970-01-01 08:27:52 +08:00
commit 3c80593c3d
135 changed files with 19822 additions and 0 deletions

View File

@@ -0,0 +1,267 @@
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
}
}