BlockManager.ts 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702
  1. import { _decorator, Component, Node, Prefab, instantiate, Vec3, EventTouch, Vec2, UITransform, find } from 'cc';
  2. const { ccclass, property } = _decorator;
  3. @ccclass('BlockManager')
  4. export class BlockManager extends Component {
  5. // 预制体数组,存储5个预制体
  6. @property([Prefab])
  7. public blockPrefabs: Prefab[] = [];
  8. // 网格容器节点
  9. @property({
  10. type: Node,
  11. tooltip: '拖拽GridContainer节点到这里'
  12. })
  13. public gridContainer: Node = null;
  14. // 已经生成的块
  15. private blocks: Node[] = [];
  16. // 当前拖拽的块
  17. private currentDragBlock: Node | null = null;
  18. // 拖拽起始位置
  19. private startPos = new Vec2();
  20. // 块的起始位置
  21. private blockStartPos: Vec3 = new Vec3();
  22. // 网格占用情况,用于控制台输出
  23. private gridOccupationMap: number[][] = [];
  24. // 网格行数和列数
  25. private readonly GRID_ROWS = 6;
  26. private readonly GRID_COLS = 11;
  27. // 是否已初始化网格信息
  28. private gridInitialized = false;
  29. // 存储网格节点信息
  30. private gridNodes: Node[][] = [];
  31. // 网格间距
  32. private gridSpacing = 54;
  33. // 不参与占用的节点名称列表
  34. private readonly NON_BLOCK_NODES: string[] = ['Weapon'];
  35. // 调试模式
  36. private readonly DEBUG_MODE = true;
  37. // 网格偏移修正
  38. private readonly GRID_OFFSET_X = 0;
  39. private readonly GRID_OFFSET_Y = 0;
  40. // 添加一个新属性来保存方块的原始占用格子
  41. private tempRemovedOccupiedGrids: { block: Node, occupiedGrids: { row: number, col: number }[] }[] = [];
  42. start() {
  43. // 如果没有指定GridContainer,尝试找到它
  44. if (!this.gridContainer) {
  45. this.gridContainer = find('Canvas/GameLevelUI/GameArea/GridContainer');
  46. if (!this.gridContainer) {
  47. console.error('找不到GridContainer节点');
  48. return;
  49. }
  50. }
  51. // 初始化网格信息
  52. this.initGridInfo();
  53. // 初始化网格占用情况
  54. this.initGridOccupationMap();
  55. // 生成随机的三个块
  56. this.generateRandomBlocks();
  57. }
  58. // 调试日志
  59. logDebug(message: string) {
  60. if (this.DEBUG_MODE) {
  61. console.log(message);
  62. }
  63. }
  64. // 初始化网格信息
  65. initGridInfo() {
  66. if (!this.gridContainer || this.gridInitialized) return;
  67. this.logDebug('初始化网格信息...');
  68. // 创建二维数组存储网格节点
  69. this.gridNodes = [];
  70. for (let row = 0; row < this.GRID_ROWS; row++) {
  71. this.gridNodes[row] = [];
  72. }
  73. // 遍历所有网格节点,将它们放入二维数组
  74. for (let i = 0; i < this.gridContainer.children.length; i++) {
  75. const grid = this.gridContainer.children[i];
  76. if (grid.name.startsWith('Grid_')) {
  77. // 从节点名称解析行列信息,例如Grid_2_3表示第2行第3列
  78. const parts = grid.name.split('_');
  79. if (parts.length === 3) {
  80. const row = parseInt(parts[1]);
  81. const col = parseInt(parts[2]);
  82. if (row >= 0 && row < this.GRID_ROWS && col >= 0 && col < this.GRID_COLS) {
  83. this.gridNodes[row][col] = grid;
  84. // 输出网格节点的世界坐标和锚点信息
  85. const gridWorldPos = this.gridContainer.getComponent(UITransform).convertToWorldSpaceAR(grid.position);
  86. const gridTransform = grid.getComponent(UITransform);
  87. this.logDebug(`网格节点 ${grid.name} 世界坐标: (${gridWorldPos.x}, ${gridWorldPos.y}), 锚点: (${gridTransform.anchorX}, ${gridTransform.anchorY}), 大小: ${gridTransform.width}x${gridTransform.height}`);
  88. }
  89. }
  90. }
  91. }
  92. // 计算网格间距
  93. if (this.GRID_ROWS > 1 && this.GRID_COLS > 0) {
  94. if (this.gridNodes[0][0] && this.gridNodes[1][0]) {
  95. const pos1 = this.gridNodes[0][0].position;
  96. const pos2 = this.gridNodes[1][0].position;
  97. this.gridSpacing = Math.abs(pos2.y - pos1.y);
  98. this.logDebug(`计算得到网格间距: ${this.gridSpacing}`);
  99. }
  100. }
  101. this.gridInitialized = true;
  102. this.logDebug('网格信息初始化完成');
  103. // 输出GridContainer信息
  104. const containerTransform = this.gridContainer.getComponent(UITransform);
  105. console.log(`GridContainer信息 - 大小: ${containerTransform.width}x${containerTransform.height}, 锚点: (${containerTransform.anchorX}, ${containerTransform.anchorY})`);
  106. }
  107. // 初始化网格占用情况
  108. initGridOccupationMap() {
  109. this.gridOccupationMap = [];
  110. for (let row = 0; row < this.GRID_ROWS; row++) {
  111. const rowArray: number[] = [];
  112. for (let col = 0; col < this.GRID_COLS; col++) {
  113. rowArray.push(0);
  114. }
  115. this.gridOccupationMap.push(rowArray);
  116. }
  117. }
  118. // 打印网格占用情况
  119. printGridOccupation() {
  120. console.log('网格占用情况 (1=占用, 0=空闲):');
  121. // 打印列索引
  122. let colHeader = ' ';
  123. for (let col = 0; col < this.GRID_COLS; col++) {
  124. colHeader += ` ${col} `;
  125. }
  126. console.log(colHeader);
  127. // 打印分隔线
  128. console.log(' ' + '-'.repeat(this.GRID_COLS * 3));
  129. // 打印每一行
  130. for (let row = 0; row < this.GRID_ROWS; row++) {
  131. let rowStr = ` ${row} |`;
  132. for (let col = 0; col < this.GRID_COLS; col++) {
  133. rowStr += ` ${this.gridOccupationMap[row][col]} `;
  134. }
  135. console.log(rowStr);
  136. }
  137. }
  138. generateRandomBlocks() {
  139. // 清除现有的块
  140. this.clearBlocks();
  141. // 检查是否有预制体可用
  142. if (this.blockPrefabs.length === 0) {
  143. console.error('没有可用的预制体');
  144. return;
  145. }
  146. // 位置偏移量
  147. const offsets = [
  148. new Vec3(-200, 0, 0),
  149. new Vec3(0, 0, 0),
  150. new Vec3(200, 0, 0)
  151. ];
  152. // 生成三个随机块
  153. for (let i = 0; i < 3; i++) {
  154. // 随机选择一个预制体
  155. const randomIndex = Math.floor(Math.random() * this.blockPrefabs.length);
  156. const prefab = this.blockPrefabs[randomIndex];
  157. // 实例化预制体
  158. const block = instantiate(prefab);
  159. this.node.addChild(block);
  160. // 设置位置
  161. block.position = offsets[i];
  162. // 添加到块数组
  163. this.blocks.push(block);
  164. // 添加触摸事件
  165. this.setupDragEvents(block);
  166. // 输出方块B1节点(根节点)的世界坐标和锚点信息
  167. const blockWorldPos = block.parent.getComponent(UITransform).convertToWorldSpaceAR(block.position);
  168. const blockTransform = block.getComponent(UITransform);
  169. console.log(`方块 ${block.name} B1节点(根节点)世界坐标: (${blockWorldPos.x}, ${blockWorldPos.y}), 锚点: (${blockTransform.anchorX}, ${blockTransform.anchorY}), 大小: ${blockTransform.width}x${blockTransform.height}`);
  170. }
  171. }
  172. setupDragEvents(block: Node) {
  173. // 添加触摸开始事件
  174. block.on(Node.EventType.TOUCH_START, (event: EventTouch) => {
  175. // 记录当前拖拽的块
  176. this.currentDragBlock = block;
  177. // 记录触摸起始位置
  178. this.startPos = event.getUILocation();
  179. // 记录块的起始位置
  180. this.blockStartPos.set(block.position);
  181. // 将块置于顶层
  182. block.setSiblingIndex(this.node.children.length - 1);
  183. // 临时保存方块占用的网格,而不是直接移除
  184. this.tempStoreBlockOccupiedGrids(block);
  185. // 更新并打印网格占用情况
  186. this.printGridOccupation();
  187. // 输出方块B1节点(根节点)的世界坐标
  188. const b1Node = block.name === 'B1' ? block : block.getChildByName('B1');
  189. if (b1Node) {
  190. const blockWorldPos = b1Node.parent.getComponent(UITransform).convertToWorldSpaceAR(b1Node.position);
  191. console.log(`拖拽开始 - 方块 ${block.name} B1节点世界坐标: (${blockWorldPos.x}, ${blockWorldPos.y})`);
  192. }
  193. }, this);
  194. // 添加触摸移动事件
  195. block.on(Node.EventType.TOUCH_MOVE, (event: EventTouch) => {
  196. if (!this.currentDragBlock) return;
  197. // 计算触摸点位移
  198. const location = event.getUILocation();
  199. const deltaX = location.x - this.startPos.x;
  200. const deltaY = location.y - this.startPos.y;
  201. // 更新块的位置
  202. this.currentDragBlock.position = new Vec3(
  203. this.blockStartPos.x + deltaX,
  204. this.blockStartPos.y + deltaY,
  205. this.blockStartPos.z
  206. );
  207. }, this);
  208. // 添加触摸结束事件
  209. block.on(Node.EventType.TOUCH_END, (event: EventTouch) => {
  210. if (this.currentDragBlock) {
  211. // 输出拖拽结束时方块B1节点的世界坐标
  212. const b1Node = this.currentDragBlock.name === 'B1' ? this.currentDragBlock : this.currentDragBlock.getChildByName('B1');
  213. if (b1Node) {
  214. const blockWorldPos = b1Node.parent.getComponent(UITransform).convertToWorldSpaceAR(b1Node.position);
  215. console.log(`拖拽结束 - 方块 ${this.currentDragBlock.name} B1节点世界坐标: (${blockWorldPos.x}, ${blockWorldPos.y})`);
  216. }
  217. // 尝试将方块放置到网格中
  218. if (!this.tryPlaceBlockToGrid(this.currentDragBlock)) {
  219. // 放置失败,返回原位
  220. this.currentDragBlock.position = this.blockStartPos.clone();
  221. console.log('方块放置失败,返回原位');
  222. // 恢复方块原来的占用状态
  223. this.restoreBlockOccupiedGrids(this.currentDragBlock);
  224. } else {
  225. // 放置成功,清除临时保存的占用状态
  226. this.clearTempStoredOccupiedGrids(this.currentDragBlock);
  227. // 更新并打印网格占用情况
  228. this.printGridOccupation();
  229. }
  230. this.currentDragBlock = null;
  231. }
  232. }, this);
  233. // 添加触摸取消事件
  234. block.on(Node.EventType.TOUCH_CANCEL, () => {
  235. if (this.currentDragBlock) {
  236. // 触摸取消,返回原位
  237. this.currentDragBlock.position = this.blockStartPos.clone();
  238. // 恢复方块原来的占用状态
  239. this.restoreBlockOccupiedGrids(this.currentDragBlock);
  240. this.currentDragBlock = null;
  241. }
  242. }, this);
  243. }
  244. // 获取网格行列索引
  245. getGridRowCol(gridNode: Node): { row: number, col: number } | null {
  246. if (!gridNode || !gridNode.name.startsWith('Grid_')) return null;
  247. const parts = gridNode.name.split('_');
  248. if (parts.length === 3) {
  249. const row = parseInt(parts[1]);
  250. const col = parseInt(parts[2]);
  251. if (row >= 0 && row < this.GRID_ROWS && col >= 0 && col < this.GRID_COLS) {
  252. return { row, col };
  253. }
  254. }
  255. return null;
  256. }
  257. // 找到最近的网格节点
  258. findNearestGridNode(position: Vec3): Node {
  259. if (!this.gridContainer || !this.gridInitialized) return null;
  260. let nearestNode: Node = null;
  261. let minDistance = Number.MAX_VALUE;
  262. // 遍历所有网格节点
  263. for (let row = 0; row < this.GRID_ROWS; row++) {
  264. for (let col = 0; col < this.GRID_COLS; col++) {
  265. const grid = this.gridNodes[row][col];
  266. if (grid) {
  267. const distance = Vec3.distance(position, grid.position);
  268. if (distance < minDistance) {
  269. minDistance = distance;
  270. nearestNode = grid;
  271. }
  272. }
  273. }
  274. }
  275. // 增加容错性,放宽距离限制
  276. // 如果距离太远,但我们仍然有一个最近的节点,就使用它
  277. // 只有在极端情况下才返回null
  278. if (minDistance > this.gridSpacing * 2) {
  279. console.log(`警告: 距离最近的网格节点 ${nearestNode ? nearestNode.name : '无'} 距离较远: ${minDistance}`);
  280. // 如果距离超过网格间距的4倍,可能是完全偏离了网格区域
  281. if (minDistance > this.gridSpacing * 4) {
  282. return null;
  283. }
  284. }
  285. return nearestNode;
  286. }
  287. // 获取方块的所有部分节点及其相对坐标
  288. getBlockParts(block: Node): { node: Node, x: number, y: number }[] {
  289. const parts: { node: Node, x: number, y: number }[] = [];
  290. // 添加B1节点本身(根节点)
  291. parts.push({ node: block, x: 0, y: 0 });
  292. // 递归查找所有子节点
  293. this.findBlockParts(block, parts, 0, 0);
  294. return parts;
  295. }
  296. // 递归查找方块的所有部分
  297. findBlockParts(node: Node, result: { node: Node, x: number, y: number }[], parentX: number, parentY: number) {
  298. // 遍历所有子节点
  299. for (let i = 0; i < node.children.length; i++) {
  300. const child = node.children[i];
  301. // 跳过不参与占用的节点
  302. if (this.NON_BLOCK_NODES.indexOf(child.name) !== -1) {
  303. continue;
  304. }
  305. let x = parentX;
  306. let y = parentY;
  307. // 尝试从节点名称中解析坐标
  308. const match = child.name.match(/^\((-?\d+),(-?\d+)\)$/);
  309. if (match) {
  310. // 如果节点名称是坐标格式,直接使用
  311. x = parseInt(match[1]);
  312. y = parseInt(match[2]);
  313. result.push({ node: child, x, y });
  314. this.logDebug(`找到坐标节点: ${child.name}, x=${x}, y=${y}`);
  315. } else if (child.name.startsWith('B')) {
  316. // 如果是B开头的节点,使用其相对位置
  317. // 计算相对于父节点的位置,并转换为网格单位
  318. const relativeX = Math.round(child.position.x / this.gridSpacing);
  319. const relativeY = -Math.round(child.position.y / this.gridSpacing); // Y轴向下为正
  320. x = parentX + relativeX;
  321. y = parentY + relativeY;
  322. result.push({ node: child, x, y });
  323. this.logDebug(`找到B节点: ${child.name}, 相对位置: (${relativeX}, ${relativeY}), 累计位置: (${x}, ${y})`);
  324. }
  325. // 递归处理子节点
  326. this.findBlockParts(child, result, x, y);
  327. }
  328. }
  329. // 检查方块是否可以放置在指定位置
  330. canPlaceBlockAt(block: Node, targetGrid: Node): boolean {
  331. if (!this.gridInitialized) return false;
  332. // 获取目标网格的行列索引
  333. const targetRowCol = this.getGridRowCol(targetGrid);
  334. if (!targetRowCol) return false;
  335. // 获取方块的所有部分
  336. const parts = this.getBlockParts(block);
  337. console.log(`方块有 ${parts.length} 个部分`);
  338. // 检查每个部分是否与已占用的格子重叠
  339. for (const part of parts) {
  340. // 计算在网格中的行列位置
  341. const row = targetRowCol.row - part.y; // Y轴向下为正,所以是减法
  342. const col = targetRowCol.col + part.x; // X轴向右为正
  343. this.logDebug(`检查部分 ${part.node.name} 在位置 (${part.x}, ${part.y}) 对应网格位置: 行=${row}, 列=${col}`);
  344. // 检查是否超出网格范围
  345. if (row < 0 || row >= this.GRID_ROWS || col < 0 || col >= this.GRID_COLS) {
  346. console.log(`部分 ${part.node.name} 超出网格范围`);
  347. return false;
  348. }
  349. // 检查该位置是否已被占用
  350. if (this.gridOccupationMap[row][col] === 1) {
  351. console.log(`部分 ${part.node.name} 对应的网格位置已被占用: 行=${row}, 列=${col}`);
  352. return false;
  353. }
  354. }
  355. return true;
  356. }
  357. // 标记方块占用的格子
  358. markOccupiedPositions(block: Node, targetGrid: Node) {
  359. if (!this.gridInitialized) return;
  360. // 获取目标网格的行列索引
  361. const targetRowCol = this.getGridRowCol(targetGrid);
  362. if (!targetRowCol) return;
  363. // 获取方块的所有部分
  364. const parts = this.getBlockParts(block);
  365. // 清除之前的占用记录
  366. block['occupiedGrids'] = [];
  367. // 为每个部分标记占用位置
  368. for (const part of parts) {
  369. // 计算在网格中的行列位置
  370. const row = targetRowCol.row - part.y; // Y轴向下为正,所以是减法
  371. const col = targetRowCol.col + part.x; // X轴向右为正
  372. // 检查是否在有效范围内
  373. if (row >= 0 && row < this.GRID_ROWS && col >= 0 && col < this.GRID_COLS) {
  374. // 更新网格占用情况
  375. this.gridOccupationMap[row][col] = 1;
  376. // 存储方块与网格的关联
  377. block['occupiedGrids'] = block['occupiedGrids'] || [];
  378. block['occupiedGrids'].push({ row, col });
  379. this.logDebug(`标记占用: 部分=${part.node.name}, 位置=(${part.x}, ${part.y}), 行=${row}, 列=${col}`);
  380. }
  381. }
  382. }
  383. // 临时保存方块占用的网格
  384. tempStoreBlockOccupiedGrids(block: Node) {
  385. // 获取方块占用的网格
  386. const occupiedGrids = block['occupiedGrids'];
  387. if (!occupiedGrids || occupiedGrids.length === 0) return;
  388. console.log(`临时保存占用: 方块=${block.name}, 占用网格数=${occupiedGrids.length}`);
  389. // 保存到临时数组
  390. this.tempRemovedOccupiedGrids.push({
  391. block: block,
  392. occupiedGrids: [...occupiedGrids] // 复制一份,避免引用问题
  393. });
  394. // 移除占用标记
  395. for (const grid of occupiedGrids) {
  396. if (grid.row >= 0 && grid.row < this.GRID_ROWS &&
  397. grid.col >= 0 && grid.col < this.GRID_COLS) {
  398. this.gridOccupationMap[grid.row][grid.col] = 0;
  399. }
  400. }
  401. // 清空方块的占用记录
  402. block['occupiedGrids'] = [];
  403. }
  404. // 恢复方块原来的占用状态
  405. restoreBlockOccupiedGrids(block: Node) {
  406. // 查找临时保存的占用状态
  407. const index = this.tempRemovedOccupiedGrids.findIndex(item => item.block === block);
  408. if (index === -1) return;
  409. const savedItem = this.tempRemovedOccupiedGrids[index];
  410. console.log(`恢复占用: 方块=${block.name}, 占用网格数=${savedItem.occupiedGrids.length}`);
  411. // 恢复占用标记
  412. for (const grid of savedItem.occupiedGrids) {
  413. if (grid.row >= 0 && grid.row < this.GRID_ROWS &&
  414. grid.col >= 0 && grid.col < this.GRID_COLS) {
  415. this.gridOccupationMap[grid.row][grid.col] = 1;
  416. }
  417. }
  418. // 恢复方块的占用记录
  419. block['occupiedGrids'] = [...savedItem.occupiedGrids];
  420. // 从临时数组中移除
  421. this.tempRemovedOccupiedGrids.splice(index, 1);
  422. // 更新并打印网格占用情况
  423. this.printGridOccupation();
  424. }
  425. // 清除临时保存的占用状态
  426. clearTempStoredOccupiedGrids(block: Node) {
  427. // 查找临时保存的占用状态
  428. const index = this.tempRemovedOccupiedGrids.findIndex(item => item.block === block);
  429. if (index === -1) return;
  430. // 从临时数组中移除
  431. this.tempRemovedOccupiedGrids.splice(index, 1);
  432. }
  433. // 移除方块占用的格子
  434. removeBlockFromGrid(block: Node) {
  435. // 获取方块占用的网格
  436. const occupiedGrids = block['occupiedGrids'];
  437. if (!occupiedGrids) return;
  438. console.log(`移除占用: 方块=${block.name}, 占用网格数=${occupiedGrids.length}`);
  439. // 移除占用标记
  440. for (const grid of occupiedGrids) {
  441. if (grid.row >= 0 && grid.row < this.GRID_ROWS &&
  442. grid.col >= 0 && grid.col < this.GRID_COLS) {
  443. this.gridOccupationMap[grid.row][grid.col] = 0;
  444. }
  445. }
  446. // 清空方块的占用记录
  447. block['occupiedGrids'] = [];
  448. }
  449. clearBlocks() {
  450. // 移除所有已经生成的块
  451. for (const block of this.blocks) {
  452. if (block.isValid) {
  453. // 移除占用的格子
  454. this.removeBlockFromGrid(block);
  455. // 销毁方块
  456. block.destroy();
  457. }
  458. }
  459. this.blocks = [];
  460. // 重置网格占用情况
  461. this.initGridOccupationMap();
  462. // 打印网格占用情况
  463. this.printGridOccupation();
  464. }
  465. // 尝试将方块放置到网格中
  466. tryPlaceBlockToGrid(block: Node): boolean {
  467. if (!this.gridContainer || !this.gridInitialized) return false;
  468. // 获取方块信息
  469. const blockTransform = block.getComponent(UITransform);
  470. console.log(`方块 ${block.name} 信息 - 大小: ${blockTransform.width}x${blockTransform.height}, 锚点: (${blockTransform.anchorX}, ${blockTransform.anchorY})`);
  471. // 获取B1节点(在这个情况下,B1就是block自身,因为预制体的根节点就是B1)
  472. // 如果B1不是根节点,而是子节点,则需要查找B1节点
  473. let b1Node = block;
  474. if (block.name !== 'B1') {
  475. // 查找B1节点
  476. b1Node = block.getChildByName('B1');
  477. if (!b1Node) {
  478. console.error(`找不到B1节点,无法放置方块`);
  479. return false;
  480. }
  481. }
  482. console.log(`B1节点名称: ${b1Node.name}`);
  483. // 获取方块B1节点在世界坐标中的位置
  484. const b1WorldPos = b1Node.parent.getComponent(UITransform).convertToWorldSpaceAR(b1Node.position);
  485. console.log(`放置前 - B1节点世界坐标: (${b1WorldPos.x}, ${b1WorldPos.y})`);
  486. // 将B1节点的世界坐标转换为GridContainer的本地坐标
  487. const gridPos = this.gridContainer.getComponent(UITransform).convertToNodeSpaceAR(b1WorldPos);
  488. // 检查是否在GridContainer范围内
  489. const gridSize = this.gridContainer.getComponent(UITransform).contentSize;
  490. const halfWidth = gridSize.width / 2;
  491. const halfHeight = gridSize.height / 2;
  492. // 增加容错性,放宽边界检查
  493. const tolerance = this.gridSpacing * 0.5;
  494. if (gridPos.x < -halfWidth - tolerance || gridPos.x > halfWidth + tolerance ||
  495. gridPos.y < -halfHeight - tolerance || gridPos.y > halfHeight + tolerance) {
  496. console.log('方块不在GridContainer范围内');
  497. return false;
  498. }
  499. // 找到最近的网格节点
  500. const nearestGrid = this.findNearestGridNode(gridPos);
  501. if (!nearestGrid) {
  502. console.log('找不到合适的网格节点');
  503. // 尝试使用网格行列直接定位
  504. const row = Math.floor((gridPos.y + halfHeight) / this.gridSpacing);
  505. const col = Math.floor((gridPos.x + halfWidth) / this.gridSpacing);
  506. // 检查计算出的行列是否在有效范围内
  507. if (row >= 0 && row < this.GRID_ROWS && col >= 0 && col < this.GRID_COLS) {
  508. const grid = this.gridNodes[row][col];
  509. if (grid) {
  510. console.log(`尝试使用计算的行列 (${row}, ${col}) 找到网格节点 ${grid.name}`);
  511. return this.tryPlaceBlockToSpecificGrid(block, grid);
  512. }
  513. }
  514. return false;
  515. }
  516. return this.tryPlaceBlockToSpecificGrid(block, nearestGrid);
  517. }
  518. // 尝试将方块放置到指定的网格节点
  519. tryPlaceBlockToSpecificGrid(block: Node, targetGrid: Node): boolean {
  520. // 获取B1节点
  521. let b1Node = block;
  522. if (block.name !== 'B1') {
  523. b1Node = block.getChildByName('B1');
  524. if (!b1Node) {
  525. console.error(`找不到B1节点,无法放置方块`);
  526. return false;
  527. }
  528. }
  529. // 获取网格节点信息
  530. const gridTransform = targetGrid.getComponent(UITransform);
  531. console.log(`网格节点 ${targetGrid.name} 信息 - 大小: ${gridTransform.width}x${gridTransform.height}, 锚点: (${gridTransform.anchorX}, ${gridTransform.anchorY})`);
  532. // 获取网格节点的中心点世界坐标
  533. const gridCenterWorldPos = this.gridContainer.getComponent(UITransform).convertToWorldSpaceAR(targetGrid.position);
  534. console.log(`最近网格节点 ${targetGrid.name} 中心点世界坐标: (${gridCenterWorldPos.x}, ${gridCenterWorldPos.y})`);
  535. // 检查方块的所有部分是否会与已占用的格子重叠
  536. if (!this.canPlaceBlockAt(block, targetGrid)) {
  537. console.log('方块放置位置已被占用或超出边界');
  538. return false;
  539. }
  540. // 计算B1节点应该移动到的位置
  541. const targetWorldPos = gridCenterWorldPos.clone();
  542. // 计算根节点需要移动的位置
  543. // 1. 先计算B1节点相对于根节点的偏移
  544. const b1LocalPos = b1Node.position.clone();
  545. console.log(`B1节点相对于根节点的偏移: (${b1LocalPos.x}, ${b1LocalPos.y}, ${b1LocalPos.z})`);
  546. // 2. 计算根节点的目标世界坐标
  547. // 如果B1是根节点,则直接使用网格中心点
  548. let rootTargetWorldPos;
  549. if (b1Node === block) {
  550. rootTargetWorldPos = targetWorldPos.clone();
  551. } else {
  552. // 如果B1是子节点,需要计算根节点应该在的位置,使B1对准网格中心
  553. // 根节点位置 = 网格中心位置 - B1相对于根节点的偏移
  554. rootTargetWorldPos = new Vec3(
  555. targetWorldPos.x - b1LocalPos.x,
  556. targetWorldPos.y - b1LocalPos.y,
  557. targetWorldPos.z
  558. );
  559. }
  560. // 3. 将世界坐标转换为根节点父节点的本地坐标
  561. const rootTargetLocalPos = block.parent.getComponent(UITransform).convertToNodeSpaceAR(rootTargetWorldPos);
  562. // 设置方块位置,确保B1节点的中心与网格节点的中心对齐
  563. block.position = rootTargetLocalPos;
  564. // 输出放置后B1节点的世界坐标
  565. const placedB1WorldPos = b1Node.parent.getComponent(UITransform).convertToWorldSpaceAR(b1Node.position);
  566. console.log(`放置后 - B1节点世界坐标: (${placedB1WorldPos.x}, ${placedB1WorldPos.y})`);
  567. // 输出对比信息,查看是否完全对齐
  568. console.log(`对齐检查 - 网格中心点: (${gridCenterWorldPos.x}, ${gridCenterWorldPos.y}), B1节点中心点: (${placedB1WorldPos.x}, ${placedB1WorldPos.y})`);
  569. console.log(`对齐差值 - X: ${placedB1WorldPos.x - gridCenterWorldPos.x}, Y: ${placedB1WorldPos.y - gridCenterWorldPos.y}`);
  570. // 标记占用的格子
  571. this.markOccupiedPositions(block, targetGrid);
  572. console.log('方块放置成功');
  573. return true;
  574. }
  575. }