| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- import { _decorator, Component, Node, Prefab, instantiate, Vec3, EventTouch, Vec2, UITransform, find, BoxCollider2D, Collider2D, Contact2DType, IPhysics2DContact, Intersection2D, Rect, PhysicsSystem2D } from 'cc';
- const { ccclass, property } = _decorator;
- @ccclass('BlockManager')
- export class BlockManager extends Component {
- // 预制体数组,存储5个预制体
- @property([Prefab])
- public blockPrefabs: Prefab[] = [];
-
- // 网格容器节点
- @property({
- type: Node,
- tooltip: '拖拽GridContainer节点到这里'
- })
- public gridContainer: Node = null;
-
- // 是否启用物理碰撞检测
- @property({
- tooltip: '是否启用物理碰撞检测'
- })
- public usePhysicsCollision: boolean = true;
-
- // 已经生成的块
- private blocks: Node[] = [];
- // 当前拖拽的块
- private currentDragBlock: Node | null = null;
- // 拖拽起始位置
- private startPos = new Vec2();
- // 块的起始位置
- private blockStartPos: Vec3 = new Vec3();
- // 已占用的网格位置
- private occupiedPositions: Set<string> = new Set();
-
- start() {
- // 如果没有指定GridContainer,尝试找到它
- if (!this.gridContainer) {
- this.gridContainer = find('Canvas/GameLevelUI/GameArea/GridContainer');
- if (!this.gridContainer) {
- console.error('找不到GridContainer节点');
- }
- }
-
- // 启用物理系统
- if (this.usePhysicsCollision) {
- PhysicsSystem2D.instance.enable = true;
- }
-
- // 生成随机的三个块
- this.generateRandomBlocks();
- }
-
- generateRandomBlocks() {
- // 清除现有的块
- this.clearBlocks();
-
- // 检查是否有预制体可用
- if (this.blockPrefabs.length === 0) {
- console.error('没有可用的预制体');
- return;
- }
-
- // 位置偏移量
- const offsets = [
- new Vec3(-200, 0, 0),
- new Vec3(0, 0, 0),
- new Vec3(200, 0, 0)
- ];
-
- // 生成三个随机块
- for (let i = 0; i < 3; i++) {
- // 随机选择一个预制体
- const randomIndex = Math.floor(Math.random() * this.blockPrefabs.length);
- const prefab = this.blockPrefabs[randomIndex];
-
- // 实例化预制体
- const block = instantiate(prefab);
- this.node.addChild(block);
-
- // 设置位置
- block.position = offsets[i];
-
- // 添加到块数组
- this.blocks.push(block);
-
- // 添加碰撞组件
- if (this.usePhysicsCollision) {
- this.addColliders(block);
- }
-
- // 添加触摸事件
- this.setupDragEvents(block);
- }
- }
-
- // 为方块的B1、B2、B3等节点添加碰撞组件
- addColliders(block: Node) {
- const blockParts = this.getBlockParts(block);
-
- for (const part of blockParts) {
- // 检查是否已有碰撞组件
- let collider = part.getComponent(BoxCollider2D);
- if (!collider) {
- collider = part.addComponent(BoxCollider2D);
- }
-
- // 设置碰撞组件属性
- const uiTransform = part.getComponent(UITransform);
- if (uiTransform) {
- collider.size.width = uiTransform.contentSize.width * 0.8; // 稍微小一点,避免边缘碰撞
- collider.size.height = uiTransform.contentSize.height * 0.8;
- } else {
- collider.size.width = 40; // 默认大小
- collider.size.height = 40;
- }
-
- // 设置碰撞组和掩码
- collider.group = 1 << 0; // 默认组
- collider.sensor = true; // 设为传感器,不影响物理行为
-
- // 添加碰撞回调
- collider.on(Contact2DType.BEGIN_CONTACT, this.onCollisionEnter, this);
- }
- }
-
- // 碰撞开始回调
- onCollisionEnter(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
- // 如果当前正在拖拽方块,标记碰撞状态
- if (this.currentDragBlock) {
- const block = this.findBlockByPart(selfCollider.node);
- if (block === this.currentDragBlock) {
- block['isColliding'] = true;
- }
- }
- }
-
- // 根据部件节点找到所属的方块
- findBlockByPart(partNode: Node): Node | null {
- for (const block of this.blocks) {
- const blockParts = this.getBlockParts(block);
- for (const part of blockParts) {
- if (part === partNode) {
- return block;
- }
- }
- }
- return null;
- }
-
- setupDragEvents(block: Node) {
- // 添加触摸开始事件
- block.on(Node.EventType.TOUCH_START, (event: EventTouch) => {
- // 记录当前拖拽的块
- this.currentDragBlock = block;
- // 记录触摸起始位置
- this.startPos = event.getUILocation();
- // 记录块的起始位置
- this.blockStartPos.set(block.position);
-
- // 将块置于顶层
- block.setSiblingIndex(this.node.children.length - 1);
-
- // 如果方块已经放置在网格中,移除占用状态
- this.removeBlockFromGrid(block);
-
- // 重置碰撞状态
- block['isColliding'] = false;
- }, this);
-
- // 添加触摸移动事件
- block.on(Node.EventType.TOUCH_MOVE, (event: EventTouch) => {
- if (!this.currentDragBlock) return;
-
- // 计算触摸点位移
- const location = event.getUILocation();
- const deltaX = location.x - this.startPos.x;
- const deltaY = location.y - this.startPos.y;
-
- // 更新块的位置
- this.currentDragBlock.position = new Vec3(
- this.blockStartPos.x + deltaX,
- this.blockStartPos.y + deltaY,
- this.blockStartPos.z
- );
- }, this);
-
- // 添加触摸结束事件
- block.on(Node.EventType.TOUCH_END, (event: EventTouch) => {
- if (this.currentDragBlock) {
- // 检查是否有碰撞
- if (this.usePhysicsCollision && this.currentDragBlock['isColliding']) {
- // 有碰撞,返回原位
- this.currentDragBlock.position = this.blockStartPos.clone();
- console.log('方块放置失败:与其他方块碰撞');
- } else {
- // 尝试将方块放置到网格中
- if (!this.tryPlaceBlockToGrid(this.currentDragBlock)) {
- // 放置失败,返回原位
- this.currentDragBlock.position = this.blockStartPos.clone();
- }
- }
- this.currentDragBlock = null;
- }
- }, this);
-
- // 添加触摸取消事件
- block.on(Node.EventType.TOUCH_CANCEL, () => {
- if (this.currentDragBlock) {
- // 触摸取消,返回原位
- this.currentDragBlock.position = this.blockStartPos.clone();
- this.currentDragBlock = null;
- }
- }, this);
- }
-
- // 尝试将方块放置到网格中
- tryPlaceBlockToGrid(block: Node): boolean {
- if (!this.gridContainer) return false;
-
- // 找到B1节点(吸附点)
- const b1Node = this.findB1Node(block);
- if (!b1Node) return false;
-
- // 获取B1节点在世界坐标中的位置
- const blockWorldPos = block.parent.getComponent(UITransform).convertToWorldSpaceAR(block.position);
- const b1WorldPos = new Vec3(
- blockWorldPos.x + b1Node.position.x,
- blockWorldPos.y + b1Node.position.y,
- blockWorldPos.z
- );
-
- // 将B1节点的世界坐标转换为GridContainer的本地坐标
- const gridPos = this.gridContainer.getComponent(UITransform).convertToNodeSpaceAR(b1WorldPos);
-
- // 检查是否在GridContainer范围内
- const gridSize = this.gridContainer.getComponent(UITransform).contentSize;
- const halfWidth = gridSize.width / 2;
- const halfHeight = gridSize.height / 2;
-
- if (gridPos.x < -halfWidth || gridPos.x > halfWidth ||
- gridPos.y < -halfHeight || gridPos.y > halfHeight) {
- console.log('方块不在GridContainer范围内');
- return false;
- }
-
- // 找到最近的网格节点
- const nearestGrid = this.findNearestGridNode(gridPos);
- if (!nearestGrid) {
- console.log('找不到合适的网格节点');
- return false;
- }
-
- // 检查方块的所有部分是否会与已占用的格子重叠
- if (!this.canPlaceBlockAt(block, nearestGrid)) {
- console.log('方块放置位置已被占用或超出边界');
- return false;
- }
-
- // 计算方块应该移动到的位置,使B1对齐到网格
- const targetWorldPos = this.gridContainer.getComponent(UITransform).convertToWorldSpaceAR(nearestGrid.position);
- const targetPos = block.parent.getComponent(UITransform).convertToNodeSpaceAR(targetWorldPos);
-
- // 设置方块位置
- block.position = new Vec3(
- targetPos.x - b1Node.position.x,
- targetPos.y - b1Node.position.y,
- block.position.z
- );
-
- // 标记占用的格子
- this.markOccupiedPositions(block, nearestGrid);
-
- console.log('方块放置成功');
- return true;
- }
-
- // 检查两个方块是否碰撞(使用几何碰撞检测)
- checkBlocksCollision(block1: Node, block2: Node): boolean {
- if (block1 === block2) return false;
-
- const parts1 = this.getBlockParts(block1);
- const parts2 = this.getBlockParts(block2);
-
- // 检查每个部分之间的碰撞
- for (const part1 of parts1) {
- const rect1 = this.getNodeRect(part1);
-
- for (const part2 of parts2) {
- const rect2 = this.getNodeRect(part2);
-
- // 检查矩形是否相交
- if (Intersection2D.rectRect(rect1, rect2)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- // 获取节点的世界矩形
- getNodeRect(node: Node): Rect {
- const uiTransform = node.getComponent(UITransform);
- if (!uiTransform) {
- return new Rect(0, 0, 0, 0);
- }
-
- const size = uiTransform.contentSize;
- const worldPos = node.parent.getComponent(UITransform).convertToWorldSpaceAR(node.position);
-
- return new Rect(
- worldPos.x - size.width * 0.5,
- worldPos.y - size.height * 0.5,
- size.width,
- size.height
- );
- }
-
- // 找到B1节点
- findB1Node(block: Node): Node {
- for (let i = 0; i < block.children.length; i++) {
- const child = block.children[i];
- if (child.name === 'B1') {
- return child;
- }
- }
- return null;
- }
-
- // 找到最近的网格节点
- findNearestGridNode(position: Vec3): Node {
- if (!this.gridContainer) return null;
-
- let nearestNode: Node = null;
- let minDistance = Number.MAX_VALUE;
-
- // 遍历所有网格节点
- for (let i = 0; i < this.gridContainer.children.length; i++) {
- const grid = this.gridContainer.children[i];
- if (grid.name.startsWith('Grid_')) {
- const distance = Vec3.distance(position, grid.position);
- if (distance < minDistance) {
- minDistance = distance;
- nearestNode = grid;
- }
- }
- }
-
- // 如果距离太远(超过网格大小的一半),不吸附
- if (minDistance > 24) { // 假设网格大小为48x48
- return null;
- }
-
- return nearestNode;
- }
-
- // 检查方块是否可以放置在指定位置
- canPlaceBlockAt(block: Node, targetGrid: Node): boolean {
- // 获取方块的所有部分(B1, B2, B3...)
- const blockParts = this.getBlockParts(block);
-
- // 找到B1节点
- const b1Node = this.findB1Node(block);
- if (!b1Node) return false;
-
- // 计算B1与目标网格的偏移
- const offsetX = targetGrid.position.x - b1Node.position.x;
- const offsetY = targetGrid.position.y - b1Node.position.y;
-
- // 检查每个部分是否与已占用的格子重叠
- for (const part of blockParts) {
- // 计算部分在网格中的位置
- const gridX = Math.round(offsetX + part.position.x);
- const gridY = Math.round(offsetY + part.position.y);
- const posKey = `${gridX},${gridY}`;
-
- // 检查该位置是否已被占用
- if (this.occupiedPositions.has(posKey)) {
- return false;
- }
-
- // 检查是否超出GridContainer边界
- const gridNode = this.findGridAtPosition(gridX, gridY);
- if (!gridNode) {
- return false;
- }
- }
-
- return true;
- }
-
- // 根据坐标找到网格节点
- findGridAtPosition(x: number, y: number): Node {
- if (!this.gridContainer) return null;
-
- for (let i = 0; i < this.gridContainer.children.length; i++) {
- const grid = this.gridContainer.children[i];
- if (grid.name.startsWith('Grid_')) {
- if (Math.abs(grid.position.x - x) < 1 && Math.abs(grid.position.y - y) < 1) {
- return grid;
- }
- }
- }
-
- return null;
- }
-
- // 获取方块的所有部分(B1, B2, B3...)
- getBlockParts(block: Node): Node[] {
- const parts: Node[] = [];
-
- for (let i = 0; i < block.children.length; i++) {
- const child = block.children[i];
- if (child.name.startsWith('B') && /^B\d+$/.test(child.name)) {
- parts.push(child);
- }
- }
-
- return parts;
- }
-
- // 标记方块占用的格子
- markOccupiedPositions(block: Node, targetGrid: Node) {
- // 获取方块的所有部分
- const blockParts = this.getBlockParts(block);
-
- // 找到B1节点
- const b1Node = this.findB1Node(block);
- if (!b1Node) return;
-
- // 计算B1与目标网格的偏移
- const offsetX = targetGrid.position.x - b1Node.position.x;
- const offsetY = targetGrid.position.y - b1Node.position.y;
-
- // 为每个部分标记占用位置
- for (const part of blockParts) {
- // 计算部分在网格中的位置
- const gridX = Math.round(offsetX + part.position.x);
- const gridY = Math.round(offsetY + part.position.y);
- const posKey = `${gridX},${gridY}`;
-
- // 记录占用信息
- this.occupiedPositions.add(posKey);
-
- // 存储方块与位置的关联
- block['occupiedPositions'] = block['occupiedPositions'] || [];
- block['occupiedPositions'].push(posKey);
- }
- }
-
- // 移除方块占用的格子
- removeBlockFromGrid(block: Node) {
- // 获取方块占用的位置
- const occupiedPositions = block['occupiedPositions'];
- if (!occupiedPositions) return;
-
- // 移除占用标记
- for (const posKey of occupiedPositions) {
- this.occupiedPositions.delete(posKey);
- }
-
- // 清空方块的占用记录
- block['occupiedPositions'] = [];
- }
-
- clearBlocks() {
- // 移除所有已经生成的块
- for (const block of this.blocks) {
- if (block.isValid) {
- // 移除占用的格子
- this.removeBlockFromGrid(block);
- // 销毁方块
- block.destroy();
- }
- }
- this.blocks = [];
-
- // 清空占用位置
- this.occupiedPositions.clear();
- }
- }
|