BlockManager_old.ts 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  1. import { _decorator, Component, Node, Prefab, instantiate, Vec3, EventTouch, Vec2, UITransform, find, Rect, Label, Color, Size, Contact2DType, Collider2D, IPhysics2DContact } 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. // 方块容器节点(kuang)
  15. @property({
  16. type: Node,
  17. tooltip: '拖拽kuang节点到这里'
  18. })
  19. public kuangContainer: Node = null;
  20. // 金币标签节点
  21. @property({
  22. type: Node,
  23. tooltip: '拖拽CoinLabel节点到这里'
  24. })
  25. public coinLabelNode: Node = null;
  26. // 游戏是否已开始
  27. public gameStarted: boolean = false;
  28. // 方块移动冷却时间(秒)
  29. @property({
  30. tooltip: '游戏开始后方块移动的冷却时间(秒)'
  31. })
  32. public blockMoveCooldown: number = 10;
  33. // 玩家金币数量
  34. private playerCoins: number = 699;
  35. // 方块价格标签映射
  36. private blockPriceMap: Map<Node, Node> = new Map();
  37. // 已经生成的块
  38. private blocks: Node[] = [];
  39. // 当前拖拽的块
  40. private currentDragBlock: Node | null = null;
  41. // 拖拽起始位置
  42. private startPos = new Vec2();
  43. // 块的起始位置
  44. private blockStartPos: Vec3 = new Vec3();
  45. // 网格占用情况,用于控制台输出
  46. private gridOccupationMap: number[][] = [];
  47. // 网格行数和列数
  48. private readonly GRID_ROWS = 6;
  49. private readonly GRID_COLS = 11;
  50. // 是否已初始化网格信息
  51. private gridInitialized = false;
  52. // 存储网格节点信息
  53. private gridNodes: Node[][] = [];
  54. // 网格间距
  55. private gridSpacing = 54;
  56. // 不参与占用的节点名称列表
  57. private readonly NON_BLOCK_NODES: string[] = ['Weapon', 'Price'];
  58. // 临时保存方块的原始占用格子
  59. private tempRemovedOccupiedGrids: { block: Node, occupiedGrids: { row: number, col: number }[] }[] = [];
  60. // 方块原始位置(在kuang中的位置)
  61. private originalPositions: Map<Node, Vec3> = new Map();
  62. // 方块当前所在的区域
  63. private blockLocations: Map<Node, string> = new Map();
  64. // 方块移动冷却状态管理
  65. private blockCooldowns: Map<Node, number> = new Map(); // 存储每个方块的冷却结束时间
  66. private globalCooldownEndTime: number = 0; // 全局冷却结束时间
  67. // 检查方块是否可以移动(冷却检查)
  68. private canMoveBlock(block: Node): boolean {
  69. if (!this.gameStarted) {
  70. // 游戏未开始(备战阶段),可以自由移动
  71. return true;
  72. }
  73. const currentTime = Date.now() / 1000; // 转换为秒
  74. // 检查全局冷却
  75. if (currentTime < this.globalCooldownEndTime) {
  76. const remainingTime = Math.ceil(this.globalCooldownEndTime - currentTime);
  77. console.log(`方块移动冷却中,剩余时间: ${remainingTime}秒`);
  78. return false;
  79. }
  80. return true;
  81. }
  82. // 设置方块移动冷却
  83. private setBlockCooldown(block: Node) {
  84. if (!this.gameStarted) {
  85. // 游戏未开始,不设置冷却
  86. return;
  87. }
  88. const currentTime = Date.now() / 1000; // 转换为秒
  89. const cooldownEndTime = currentTime + this.blockMoveCooldown;
  90. // 设置全局冷却
  91. this.globalCooldownEndTime = cooldownEndTime;
  92. console.log(`方块移动冷却已设置,持续时间: ${this.blockMoveCooldown}秒`);
  93. }
  94. // 清除所有冷却(游戏重置时调用)
  95. public clearAllCooldowns() {
  96. this.blockCooldowns.clear();
  97. this.globalCooldownEndTime = 0;
  98. console.log('所有方块移动冷却已清除');
  99. }
  100. start() {
  101. // 如果没有指定GridContainer,尝试找到它
  102. if (!this.gridContainer) {
  103. this.gridContainer = find('Canvas/GameLevelUI/GameArea/GridContainer');
  104. if (!this.gridContainer) {
  105. console.error('找不到GridContainer节点');
  106. return;
  107. }
  108. }
  109. // 如果没有指定kuangContainer,尝试找到它
  110. if (!this.kuangContainer) {
  111. this.kuangContainer = find('Canvas/GameLevelUI/BlockSelectionUI/diban/kuang');
  112. if (!this.kuangContainer) {
  113. console.error('找不到kuang节点');
  114. return;
  115. }
  116. }
  117. // 如果没有指定coinLabelNode,尝试找到它
  118. if (!this.coinLabelNode) {
  119. this.coinLabelNode = find('Canvas/GameLevelUI/CoinNode/CoinLabel');
  120. if (!this.coinLabelNode) {
  121. console.error('找不到CoinLabel节点');
  122. return;
  123. }
  124. }
  125. // 确保有PlacedBlocks节点用于存放已放置的方块
  126. this.ensurePlacedBlocksNode();
  127. // 初始化玩家金币显示
  128. this.updateCoinDisplay();
  129. // 初始化网格信息
  130. this.initGridInfo();
  131. // 初始化网格占用情况
  132. this.initGridOccupationMap();
  133. // 在kuang下随机生成三个方块
  134. this.generateRandomBlocksInKuang();
  135. // 注册碰撞回调
  136. const collider = this.getComponent(Collider2D);
  137. if (collider) {
  138. collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
  139. console.log('方块碰撞监听器已注册');
  140. }
  141. }
  142. // 确保有PlacedBlocks节点
  143. ensurePlacedBlocksNode() {
  144. const canvas = find('Canvas');
  145. if (!canvas) {
  146. console.error('找不到Canvas节点');
  147. return;
  148. }
  149. let placedBlocksNode = find('Canvas/PlacedBlocks');
  150. if (!placedBlocksNode) {
  151. placedBlocksNode = new Node('PlacedBlocks');
  152. canvas.addChild(placedBlocksNode);
  153. if (!placedBlocksNode.getComponent(UITransform)) {
  154. placedBlocksNode.addComponent(UITransform);
  155. }
  156. console.log('已创建PlacedBlocks节点');
  157. }
  158. }
  159. // 初始化网格信息
  160. initGridInfo() {
  161. if (!this.gridContainer || this.gridInitialized) return;
  162. this.gridNodes = [];
  163. for (let row = 0; row < this.GRID_ROWS; row++) {
  164. this.gridNodes[row] = [];
  165. }
  166. for (let i = 0; i < this.gridContainer.children.length; i++) {
  167. const grid = this.gridContainer.children[i];
  168. if (grid.name.startsWith('Grid_')) {
  169. const parts = grid.name.split('_');
  170. if (parts.length === 3) {
  171. const row = parseInt(parts[1]);
  172. const col = parseInt(parts[2]);
  173. if (row >= 0 && row < this.GRID_ROWS && col >= 0 && col < this.GRID_COLS) {
  174. this.gridNodes[row][col] = grid;
  175. }
  176. }
  177. }
  178. }
  179. if (this.GRID_ROWS > 1 && this.GRID_COLS > 0) {
  180. if (this.gridNodes[0][0] && this.gridNodes[1][0]) {
  181. const pos1 = this.gridNodes[0][0].position;
  182. const pos2 = this.gridNodes[1][0].position;
  183. this.gridSpacing = Math.abs(pos2.y - pos1.y);
  184. }
  185. }
  186. this.gridInitialized = true;
  187. }
  188. // 初始化网格占用情况
  189. initGridOccupationMap() {
  190. this.gridOccupationMap = [];
  191. for (let row = 0; row < this.GRID_ROWS; row++) {
  192. const rowArray: number[] = [];
  193. for (let col = 0; col < this.GRID_COLS; col++) {
  194. rowArray.push(0);
  195. }
  196. this.gridOccupationMap.push(rowArray);
  197. }
  198. }
  199. // 在kuang下随机生成三个方块
  200. private generateRandomBlocksInKuang() {
  201. this.clearBlocks();
  202. if (this.blockPrefabs.length === 0) {
  203. console.error('没有可用的预制体');
  204. return;
  205. }
  206. const kuangNode = this.kuangContainer;
  207. if (!kuangNode) {
  208. console.error('找不到kuang节点');
  209. return;
  210. }
  211. const offsets = [
  212. new Vec3(-200, 0, 0),
  213. new Vec3(0, 0, 0),
  214. new Vec3(200, 0, 0)
  215. ];
  216. const dbNodes = [
  217. kuangNode.getChildByName('db01'),
  218. kuangNode.getChildByName('db02'),
  219. kuangNode.getChildByName('db03')
  220. ];
  221. console.log('开始在kuang容器中生成随机方块');
  222. for (let i = 0; i < 3; i++) {
  223. const randomIndex = Math.floor(Math.random() * this.blockPrefabs.length);
  224. const prefab = this.blockPrefabs[randomIndex];
  225. if (!prefab) {
  226. console.error(`方块预制体索引 ${randomIndex} 无效`);
  227. continue;
  228. }
  229. const block = instantiate(prefab);
  230. kuangNode.addChild(block);
  231. block.position = offsets[i];
  232. this.originalPositions.set(block, offsets[i].clone());
  233. this.blockLocations.set(block, 'kuang');
  234. this.blocks.push(block);
  235. if (dbNodes[i]) {
  236. const priceNode = dbNodes[i].getChildByName('Price');
  237. if (priceNode) {
  238. this.blockPriceMap.set(block, priceNode);
  239. priceNode.active = true;
  240. }
  241. this.associateDbNodeWithBlock(block, dbNodes[i]);
  242. }
  243. this.setupDragEvents(block);
  244. console.log(`生成方块 ${i + 1}/3: ${prefab.name} 在位置 (${offsets[i].x.toFixed(2)}, ${offsets[i].y.toFixed(2)})`);
  245. }
  246. console.log(`成功在kuang容器中生成了 ${this.blocks.length} 个方块`);
  247. this.updateCoinDisplay();
  248. }
  249. // 将db节点与方块关联
  250. associateDbNodeWithBlock(block: Node, dbNode: Node) {
  251. block['dbNode'] = dbNode;
  252. block.on(Node.EventType.TRANSFORM_CHANGED, () => {
  253. if (dbNode && block.parent) {
  254. const location = this.blockLocations.get(block);
  255. if (location === 'grid') {
  256. dbNode.active = false;
  257. return;
  258. }
  259. dbNode.active = true;
  260. const worldPos = block.parent.getComponent(UITransform).convertToWorldSpaceAR(block.position);
  261. const localPos = dbNode.parent.getComponent(UITransform).convertToNodeSpaceAR(worldPos);
  262. dbNode.position = new Vec3(localPos.x, localPos.y - 80, localPos.z);
  263. }
  264. });
  265. }
  266. // 更新金币显示
  267. updateCoinDisplay() {
  268. if (this.coinLabelNode) {
  269. const label = this.coinLabelNode.getComponent(Label);
  270. if (label) {
  271. label.string = this.playerCoins.toString();
  272. }
  273. }
  274. }
  275. // 获取方块价格
  276. getBlockPrice(block: Node): number {
  277. const priceNode = this.blockPriceMap.get(block);
  278. if (priceNode) {
  279. const label = priceNode.getComponent(Label);
  280. if (label) {
  281. const price = parseInt(label.string);
  282. if (!isNaN(price)) {
  283. return price;
  284. }
  285. }
  286. }
  287. return 50;
  288. }
  289. // 隐藏价格标签
  290. hidePriceLabel(block: Node) {
  291. const priceNode = this.blockPriceMap.get(block);
  292. if (priceNode) {
  293. priceNode.active = false;
  294. }
  295. }
  296. // 显示价格标签
  297. showPriceLabel(block: Node) {
  298. const priceNode = this.blockPriceMap.get(block);
  299. if (priceNode) {
  300. priceNode.active = true;
  301. }
  302. }
  303. // 扣除玩家金币
  304. deductPlayerCoins(amount: number): boolean {
  305. if (this.playerCoins >= amount) {
  306. this.playerCoins -= amount;
  307. this.updateCoinDisplay();
  308. return true;
  309. }
  310. return false;
  311. }
  312. // 归还玩家金币
  313. refundPlayerCoins(amount: number) {
  314. this.playerCoins += amount;
  315. this.updateCoinDisplay();
  316. }
  317. // 设置拖拽事件
  318. setupDragEvents(block: Node) {
  319. block.on(Node.EventType.TOUCH_START, (event: EventTouch) => {
  320. if (this.gameStarted && this.blockLocations.get(block) === 'grid') {
  321. if (!this.canMoveBlock(block)) {
  322. return;
  323. }
  324. }
  325. this.currentDragBlock = block;
  326. this.startPos = event.getUILocation();
  327. this.blockStartPos.set(block.position);
  328. this.currentDragBlock['startLocation'] = this.blockLocations.get(block);
  329. block.setSiblingIndex(block.parent.children.length - 1);
  330. this.tempStoreBlockOccupiedGrids(block);
  331. }, this);
  332. block.on(Node.EventType.TOUCH_MOVE, (event: EventTouch) => {
  333. if (this.gameStarted && this.blockLocations.get(block) === 'grid') {
  334. if (!this.canMoveBlock(block)) {
  335. return;
  336. }
  337. }
  338. if (!this.currentDragBlock) return;
  339. const location = event.getUILocation();
  340. const deltaX = location.x - this.startPos.x;
  341. const deltaY = location.y - this.startPos.y;
  342. this.currentDragBlock.position = new Vec3(
  343. this.blockStartPos.x + deltaX,
  344. this.blockStartPos.y + deltaY,
  345. this.blockStartPos.z
  346. );
  347. }, this);
  348. block.on(Node.EventType.TOUCH_END, (event: EventTouch) => {
  349. if (this.gameStarted && this.blockLocations.get(block) === 'grid') {
  350. if (!this.canMoveBlock(block)) {
  351. return;
  352. }
  353. }
  354. if (this.currentDragBlock) {
  355. this.handleBlockDrop(event);
  356. // 如果成功移动且游戏已开始,设置冷却
  357. if (this.gameStarted && this.blockLocations.get(this.currentDragBlock) === 'grid') {
  358. this.setBlockCooldown(this.currentDragBlock);
  359. }
  360. this.currentDragBlock = null;
  361. }
  362. }, this);
  363. block.on(Node.EventType.TOUCH_CANCEL, () => {
  364. if (this.currentDragBlock) {
  365. this.returnBlockToOriginalPosition();
  366. this.currentDragBlock = null;
  367. }
  368. }, this);
  369. }
  370. // 处理方块放下
  371. handleBlockDrop(event: EventTouch) {
  372. const touchPos = event.getUILocation();
  373. const startLocation = this.currentDragBlock['startLocation'];
  374. if (this.isInKuangArea(touchPos)) {
  375. this.returnBlockToKuang(startLocation);
  376. } else if (this.tryPlaceBlockToGrid(this.currentDragBlock)) {
  377. this.handleSuccessfulPlacement(startLocation);
  378. } else {
  379. this.returnBlockToOriginalPosition();
  380. }
  381. }
  382. // 返回方块到kuang区域
  383. returnBlockToKuang(startLocation: string) {
  384. const originalPos = this.originalPositions.get(this.currentDragBlock);
  385. if (originalPos) {
  386. const kuangNode = this.kuangContainer;
  387. if (kuangNode && this.currentDragBlock.parent !== kuangNode) {
  388. this.currentDragBlock.removeFromParent();
  389. kuangNode.addChild(this.currentDragBlock);
  390. }
  391. this.currentDragBlock.position = originalPos.clone();
  392. }
  393. this.restoreBlockOccupiedGrids(this.currentDragBlock);
  394. this.blockLocations.set(this.currentDragBlock, 'kuang');
  395. this.showPriceLabel(this.currentDragBlock);
  396. if (startLocation === 'grid') {
  397. const price = this.getBlockPrice(this.currentDragBlock);
  398. this.refundPlayerCoins(price);
  399. this.currentDragBlock['placedBefore'] = false;
  400. }
  401. const dbNode = this.currentDragBlock['dbNode'];
  402. if (dbNode) {
  403. dbNode.active = true;
  404. this.currentDragBlock.emit(Node.EventType.TRANSFORM_CHANGED);
  405. }
  406. }
  407. // 处理成功放置
  408. handleSuccessfulPlacement(startLocation: string) {
  409. const price = this.getBlockPrice(this.currentDragBlock);
  410. if (startLocation === 'grid') {
  411. this.clearTempStoredOccupiedGrids(this.currentDragBlock);
  412. this.blockLocations.set(this.currentDragBlock, 'grid');
  413. this.hidePriceLabel(this.currentDragBlock);
  414. const dbNode = this.currentDragBlock['dbNode'];
  415. if (dbNode) {
  416. dbNode.active = false;
  417. }
  418. if (this.gameStarted) {
  419. this.moveBlockToPlacedBlocks(this.currentDragBlock);
  420. }
  421. } else {
  422. if (this.deductPlayerCoins(price)) {
  423. this.clearTempStoredOccupiedGrids(this.currentDragBlock);
  424. this.blockLocations.set(this.currentDragBlock, 'grid');
  425. this.hidePriceLabel(this.currentDragBlock);
  426. const dbNode = this.currentDragBlock['dbNode'];
  427. if (dbNode) {
  428. dbNode.active = false;
  429. }
  430. this.currentDragBlock['placedBefore'] = true;
  431. if (this.gameStarted) {
  432. this.moveBlockToPlacedBlocks(this.currentDragBlock);
  433. }
  434. } else {
  435. this.returnBlockToOriginalPosition();
  436. }
  437. }
  438. }
  439. // 返回方块到原位置
  440. returnBlockToOriginalPosition() {
  441. const currentLocation = this.blockLocations.get(this.currentDragBlock);
  442. if (currentLocation === 'kuang') {
  443. const originalPos = this.originalPositions.get(this.currentDragBlock);
  444. if (originalPos) {
  445. this.currentDragBlock.position = originalPos.clone();
  446. }
  447. } else {
  448. this.currentDragBlock.position = this.blockStartPos.clone();
  449. }
  450. this.restoreBlockOccupiedGrids(this.currentDragBlock);
  451. this.showPriceLabel(this.currentDragBlock);
  452. const dbNode = this.currentDragBlock['dbNode'];
  453. if (dbNode) {
  454. dbNode.active = true;
  455. this.currentDragBlock.emit(Node.EventType.TRANSFORM_CHANGED);
  456. }
  457. }
  458. // 检查是否在kuang区域内
  459. isInKuangArea(touchPos: Vec2): boolean {
  460. if (!this.kuangContainer) return false;
  461. const kuangTransform = this.kuangContainer.getComponent(UITransform);
  462. if (!kuangTransform) return false;
  463. const kuangBoundingBox = new Rect(
  464. this.kuangContainer.worldPosition.x - kuangTransform.width * kuangTransform.anchorX,
  465. this.kuangContainer.worldPosition.y - kuangTransform.height * kuangTransform.anchorY,
  466. kuangTransform.width,
  467. kuangTransform.height
  468. );
  469. return kuangBoundingBox.contains(new Vec2(touchPos.x, touchPos.y));
  470. }
  471. // 临时保存方块占用的网格
  472. tempStoreBlockOccupiedGrids(block: Node) {
  473. const occupiedGrids = block['occupiedGrids'];
  474. if (!occupiedGrids || occupiedGrids.length === 0) return;
  475. this.tempRemovedOccupiedGrids.push({
  476. block: block,
  477. occupiedGrids: [...occupiedGrids]
  478. });
  479. for (const grid of occupiedGrids) {
  480. if (grid.row >= 0 && grid.row < this.GRID_ROWS &&
  481. grid.col >= 0 && grid.col < this.GRID_COLS) {
  482. this.gridOccupationMap[grid.row][grid.col] = 0;
  483. }
  484. }
  485. block['occupiedGrids'] = [];
  486. }
  487. // 恢复方块原来的占用状态
  488. restoreBlockOccupiedGrids(block: Node) {
  489. const index = this.tempRemovedOccupiedGrids.findIndex(item => item.block === block);
  490. if (index === -1) return;
  491. const savedItem = this.tempRemovedOccupiedGrids[index];
  492. for (const grid of savedItem.occupiedGrids) {
  493. if (grid.row >= 0 && grid.row < this.GRID_ROWS &&
  494. grid.col >= 0 && grid.col < this.GRID_COLS) {
  495. this.gridOccupationMap[grid.row][grid.col] = 1;
  496. }
  497. }
  498. block['occupiedGrids'] = [...savedItem.occupiedGrids];
  499. this.tempRemovedOccupiedGrids.splice(index, 1);
  500. }
  501. // 清除临时保存的占用状态
  502. clearTempStoredOccupiedGrids(block: Node) {
  503. const index = this.tempRemovedOccupiedGrids.findIndex(item => item.block === block);
  504. if (index === -1) return;
  505. this.tempRemovedOccupiedGrids.splice(index, 1);
  506. }
  507. // 尝试将方块放置到网格中
  508. tryPlaceBlockToGrid(block: Node): boolean {
  509. if (!this.gridContainer || !this.gridInitialized) return false;
  510. let b1Node = block;
  511. if (block.name !== 'B1') {
  512. b1Node = block.getChildByName('B1');
  513. if (!b1Node) {
  514. return false;
  515. }
  516. }
  517. const b1WorldPos = b1Node.parent.getComponent(UITransform).convertToWorldSpaceAR(b1Node.position);
  518. const gridPos = this.gridContainer.getComponent(UITransform).convertToNodeSpaceAR(b1WorldPos);
  519. const gridSize = this.gridContainer.getComponent(UITransform).contentSize;
  520. const halfWidth = gridSize.width / 2;
  521. const halfHeight = gridSize.height / 2;
  522. const tolerance = this.gridSpacing * 0.5;
  523. if (gridPos.x < -halfWidth - tolerance || gridPos.x > halfWidth + tolerance ||
  524. gridPos.y < -halfHeight - tolerance || gridPos.y > halfHeight + tolerance) {
  525. return false;
  526. }
  527. const nearestGrid = this.findNearestGridNode(gridPos);
  528. if (!nearestGrid) {
  529. return false;
  530. }
  531. return this.tryPlaceBlockToSpecificGrid(block, nearestGrid);
  532. }
  533. // 找到最近的网格节点
  534. findNearestGridNode(position: Vec3): Node {
  535. if (!this.gridContainer || !this.gridInitialized) return null;
  536. let nearestNode: Node = null;
  537. let minDistance = Number.MAX_VALUE;
  538. for (let row = 0; row < this.GRID_ROWS; row++) {
  539. for (let col = 0; col < this.GRID_COLS; col++) {
  540. const grid = this.gridNodes[row][col];
  541. if (grid) {
  542. const distance = Vec3.distance(position, grid.position);
  543. if (distance < minDistance) {
  544. minDistance = distance;
  545. nearestNode = grid;
  546. }
  547. }
  548. }
  549. }
  550. if (minDistance > this.gridSpacing * 2) {
  551. return null;
  552. }
  553. return nearestNode;
  554. }
  555. // 尝试将方块放置到指定的网格节点
  556. tryPlaceBlockToSpecificGrid(block: Node, targetGrid: Node): boolean {
  557. let b1Node = block;
  558. if (block.name !== 'B1') {
  559. b1Node = block.getChildByName('B1');
  560. if (!b1Node) {
  561. return false;
  562. }
  563. }
  564. if (!this.canPlaceBlockAt(block, targetGrid)) {
  565. return false;
  566. }
  567. const gridCenterWorldPos = this.gridContainer.getComponent(UITransform).convertToWorldSpaceAR(targetGrid.position);
  568. const targetWorldPos = gridCenterWorldPos.clone();
  569. const b1LocalPos = b1Node.position.clone();
  570. let rootTargetWorldPos;
  571. if (b1Node === block) {
  572. rootTargetWorldPos = targetWorldPos.clone();
  573. } else {
  574. rootTargetWorldPos = new Vec3(
  575. targetWorldPos.x - b1LocalPos.x,
  576. targetWorldPos.y - b1LocalPos.y,
  577. targetWorldPos.z
  578. );
  579. }
  580. const rootTargetLocalPos = block.parent.getComponent(UITransform).convertToNodeSpaceAR(rootTargetWorldPos);
  581. block.position = rootTargetLocalPos;
  582. this.markOccupiedPositions(block, targetGrid);
  583. return true;
  584. }
  585. // 检查方块是否可以放置在指定位置
  586. canPlaceBlockAt(block: Node, targetGrid: Node): boolean {
  587. if (!this.gridInitialized) return false;
  588. const targetRowCol = this.getGridRowCol(targetGrid);
  589. if (!targetRowCol) return false;
  590. const parts = this.getBlockParts(block);
  591. for (const part of parts) {
  592. const row = targetRowCol.row - part.y;
  593. const col = targetRowCol.col + part.x;
  594. if (row < 0 || row >= this.GRID_ROWS || col < 0 || col >= this.GRID_COLS) {
  595. return false;
  596. }
  597. if (this.gridOccupationMap[row][col] === 1) {
  598. return false;
  599. }
  600. }
  601. return true;
  602. }
  603. // 获取网格行列索引
  604. getGridRowCol(gridNode: Node): { row: number, col: number } | null {
  605. if (!gridNode || !gridNode.name.startsWith('Grid_')) return null;
  606. const parts = gridNode.name.split('_');
  607. if (parts.length === 3) {
  608. const row = parseInt(parts[1]);
  609. const col = parseInt(parts[2]);
  610. if (row >= 0 && row < this.GRID_ROWS && col >= 0 && col < this.GRID_COLS) {
  611. return { row, col };
  612. }
  613. }
  614. return null;
  615. }
  616. // 获取方块的所有部分节点及其相对坐标
  617. getBlockParts(block: Node): { node: Node, x: number, y: number }[] {
  618. const parts: { node: Node, x: number, y: number }[] = [];
  619. parts.push({ node: block, x: 0, y: 0 });
  620. this.findBlockParts(block, parts, 0, 0);
  621. return parts;
  622. }
  623. // 递归查找方块的所有部分
  624. findBlockParts(node: Node, result: { node: Node, x: number, y: number }[], parentX: number, parentY: number) {
  625. for (let i = 0; i < node.children.length; i++) {
  626. const child = node.children[i];
  627. if (this.NON_BLOCK_NODES.indexOf(child.name) !== -1) {
  628. continue;
  629. }
  630. let x = parentX;
  631. let y = parentY;
  632. const match = child.name.match(/^\((-?\d+),(-?\d+)\)$/);
  633. if (match) {
  634. x = parseInt(match[1]);
  635. y = parseInt(match[2]);
  636. result.push({ node: child, x, y });
  637. } else if (child.name.startsWith('B')) {
  638. const relativeX = Math.round(child.position.x / this.gridSpacing);
  639. const relativeY = -Math.round(child.position.y / this.gridSpacing);
  640. x = parentX + relativeX;
  641. y = parentY + relativeY;
  642. result.push({ node: child, x, y });
  643. }
  644. this.findBlockParts(child, result, x, y);
  645. }
  646. }
  647. // 标记方块占用的格子
  648. markOccupiedPositions(block: Node, targetGrid: Node) {
  649. if (!this.gridInitialized) return;
  650. const targetRowCol = this.getGridRowCol(targetGrid);
  651. if (!targetRowCol) return;
  652. const parts = this.getBlockParts(block);
  653. block['occupiedGrids'] = [];
  654. for (const part of parts) {
  655. const row = targetRowCol.row - part.y;
  656. const col = targetRowCol.col + part.x;
  657. if (row >= 0 && row < this.GRID_ROWS && col >= 0 && col < this.GRID_COLS) {
  658. this.gridOccupationMap[row][col] = 1;
  659. block['occupiedGrids'] = block['occupiedGrids'] || [];
  660. block['occupiedGrids'].push({ row, col });
  661. }
  662. }
  663. }
  664. // 清除方块
  665. clearBlocks() {
  666. const blocksToRemove = [];
  667. for (const block of this.blocks) {
  668. if (block.isValid) {
  669. const location = this.blockLocations.get(block);
  670. if (location === 'kuang') {
  671. blocksToRemove.push(block);
  672. }
  673. }
  674. }
  675. for (const block of blocksToRemove) {
  676. const dbNode = block['dbNode'];
  677. if (dbNode && dbNode.isValid) {
  678. block.off(Node.EventType.TRANSFORM_CHANGED);
  679. const kuangNode = this.kuangContainer;
  680. if (kuangNode) {
  681. const dbName = dbNode.name;
  682. if (!kuangNode.getChildByName(dbName)) {
  683. dbNode.parent = kuangNode;
  684. }
  685. }
  686. }
  687. const index = this.blocks.indexOf(block);
  688. if (index !== -1) {
  689. this.blocks.splice(index, 1);
  690. }
  691. this.originalPositions.delete(block);
  692. this.blockLocations.delete(block);
  693. this.blockPriceMap.delete(block);
  694. block.destroy();
  695. }
  696. }
  697. // 游戏开始时调用
  698. onGameStart() {
  699. this.gameStarted = true;
  700. console.log('游戏已开始,已放置的方块将有移动冷却时间');
  701. for (const block of this.blocks) {
  702. if (block.isValid) {
  703. const location = this.blockLocations.get(block);
  704. if (location === 'grid') {
  705. this.hidePriceLabel(block);
  706. const dbNode = block['dbNode'];
  707. if (dbNode) {
  708. dbNode.active = false;
  709. }
  710. this.moveBlockToPlacedBlocks(block);
  711. this.addLockedVisualHint(block);
  712. }
  713. }
  714. }
  715. }
  716. // 游戏重置时调用
  717. onGameReset() {
  718. this.gameStarted = false;
  719. this.clearAllCooldowns();
  720. console.log('游戏已重置,方块可以自由移动');
  721. }
  722. // 添加视觉提示,表明方块已锁定
  723. addLockedVisualHint(block: Node) {
  724. const children = block.children;
  725. for (let i = 0; i < children.length; i++) {
  726. const child = children[i];
  727. if (this.NON_BLOCK_NODES.indexOf(child.name) !== -1) {
  728. continue;
  729. }
  730. child.setScale(new Vec3(0.95, 0.95, 1));
  731. }
  732. console.log(`已为方块 ${block.name} 添加锁定视觉提示`);
  733. }
  734. // 将方块移动到PlacedBlocks节点下
  735. moveBlockToPlacedBlocks(block: Node) {
  736. const placedBlocksNode = find('Canvas/PlacedBlocks');
  737. if (!placedBlocksNode) {
  738. console.error('找不到PlacedBlocks节点');
  739. return;
  740. }
  741. const worldPosition = new Vec3();
  742. block.getWorldPosition(worldPosition);
  743. block.removeFromParent();
  744. placedBlocksNode.addChild(block);
  745. block.setWorldPosition(worldPosition);
  746. console.log(`已将方块 ${block.name} 移动到PlacedBlocks节点下`);
  747. }
  748. // 碰撞回调
  749. onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
  750. console.log('方块碰到了:', otherCollider.node.name);
  751. if (otherCollider.node.name === 'Ball') {
  752. console.log('方块被球击中!');
  753. }
  754. }
  755. }