BlockManager.ts 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180
  1. import { _decorator, Component, Node, Prefab, instantiate, Vec3, EventTouch, Vec2, UITransform, find, Rect, Label, Color, Size, Sprite, SpriteFrame, resources, Button } from 'cc';
  2. import { ConfigManager, WeaponConfig } from '../Core/ConfigManager';
  3. const { ccclass, property } = _decorator;
  4. @ccclass('BlockManager')
  5. export class BlockManager extends Component {
  6. // 预制体数组,存储5个预制体
  7. @property([Prefab])
  8. public blockPrefabs: Prefab[] = [];
  9. // 网格容器节点
  10. @property({
  11. type: Node,
  12. tooltip: '拖拽GridContainer节点到这里'
  13. })
  14. public gridContainer: Node = null;
  15. // 方块容器节点(kuang)
  16. @property({
  17. type: Node,
  18. tooltip: '拖拽kuang节点到这里'
  19. })
  20. public kuangContainer: Node = null;
  21. // 金币标签节点
  22. @property({
  23. type: Node,
  24. tooltip: '拖拽CoinLabel节点到这里'
  25. })
  26. public coinLabelNode: Node = null;
  27. // 已放置方块容器节点
  28. @property({
  29. type: Node,
  30. tooltip: '拖拽PlacedBlocks节点到这里(Canvas/GameLevelUI/PlacedBlocks)'
  31. })
  32. public placedBlocksContainer: Node = null;
  33. // 游戏是否已开始
  34. public gameStarted: boolean = false;
  35. // 方块移动冷却时间(秒)
  36. @property({
  37. tooltip: '游戏开始后方块移动的冷却时间(秒)'
  38. })
  39. public blockMoveCooldown: number = 1;
  40. // 玩家金币数量
  41. private playerCoins: number = 69999;
  42. // 方块价格标签映射
  43. private blockPriceMap: Map<Node, Node> = new Map();
  44. // 已经生成的块
  45. private blocks: Node[] = [];
  46. // 当前拖拽的块
  47. private currentDragBlock: Node | null = null;
  48. // 拖拽起始位置
  49. private startPos = new Vec2();
  50. // 块的起始位置
  51. private blockStartPos: Vec3 = new Vec3();
  52. // 网格占用情况,用于控制台输出
  53. private gridOccupationMap: number[][] = [];
  54. // 网格行数和列数
  55. private readonly GRID_ROWS = 6;
  56. private readonly GRID_COLS = 11;
  57. // 是否已初始化网格信息
  58. private gridInitialized = false;
  59. // 存储网格节点信息
  60. private gridNodes: Node[][] = [];
  61. // 网格间距
  62. private gridSpacing = 54;
  63. // 不参与占用的节点名称列表
  64. private readonly NON_BLOCK_NODES: string[] = ['Weapon', 'Price'];
  65. // 临时保存方块的原始占用格子
  66. private tempRemovedOccupiedGrids: { block: Node, occupiedGrids: { row: number, col: number }[] }[] = [];
  67. // 方块原始位置(在kuang中的位置)
  68. private originalPositions: Map<Node, Vec3> = new Map();
  69. // 方块当前所在的区域
  70. private blockLocations: Map<Node, string> = new Map();
  71. // 方块移动冷却状态管理
  72. private blockCooldowns: Map<Node, number> = new Map(); // 存储每个方块的冷却结束时间
  73. private globalCooldownEndTime: number = 0; // 全局冷却结束时间
  74. // 配置管理器
  75. private configManager: ConfigManager = null;
  76. // 方块武器配置映射
  77. private blockWeaponConfigs: Map<Node, WeaponConfig> = new Map();
  78. // 刷新方块按钮节点(ann003)
  79. @property({
  80. type: Node,
  81. tooltip: '拖拽BlockSelectionUI/diban/ann003按钮节点到这里'
  82. })
  83. public refreshButton: Node = null;
  84. // 检查方块是否可以移动(冷却检查)
  85. private canMoveBlock(block: Node): boolean {
  86. if (!this.gameStarted) {
  87. // 游戏未开始(备战阶段),可以自由移动
  88. return true;
  89. }
  90. const currentTime = Date.now() / 1000; // 转换为秒
  91. // 检查全局冷却
  92. if (currentTime < this.globalCooldownEndTime) {
  93. const remainingTime = Math.ceil(this.globalCooldownEndTime - currentTime);
  94. return false;
  95. }
  96. return true;
  97. }
  98. // 设置方块移动冷却
  99. private setBlockCooldown(block: Node) {
  100. if (!this.gameStarted) {
  101. // 游戏未开始,不设置冷却
  102. return;
  103. }
  104. const currentTime = Date.now() / 1000; // 转换为秒
  105. const cooldownEndTime = currentTime + this.blockMoveCooldown;
  106. // 设置全局冷却
  107. this.globalCooldownEndTime = cooldownEndTime;
  108. }
  109. // 清除所有冷却(游戏重置时调用)
  110. public clearAllCooldowns() {
  111. this.blockCooldowns.clear();
  112. this.globalCooldownEndTime = 0;
  113. }
  114. start() {
  115. // 获取配置管理器
  116. this.configManager = ConfigManager.getInstance();
  117. if (!this.configManager) {
  118. console.error('无法获取ConfigManager实例');
  119. }
  120. // 如果没有指定GridContainer,尝试找到它
  121. if (!this.gridContainer) {
  122. this.gridContainer = find('Canvas/GameLevelUI/GameArea/GridContainer');
  123. if (!this.gridContainer) {
  124. console.error('找不到GridContainer节点');
  125. return;
  126. }
  127. }
  128. // 如果没有指定kuangContainer,尝试找到它
  129. if (!this.kuangContainer) {
  130. this.kuangContainer = find('Canvas/GameLevelUI/BlockSelectionUI/diban/kuang');
  131. if (!this.kuangContainer) {
  132. console.error('找不到kuang节点');
  133. return;
  134. }
  135. }
  136. // 如果没有指定coinLabelNode,尝试找到它
  137. if (!this.coinLabelNode) {
  138. this.coinLabelNode = find('Canvas/GameLevelUI/CoinNode/CoinLabel');
  139. if (!this.coinLabelNode) {
  140. console.error('找不到CoinLabel节点');
  141. return;
  142. }
  143. }
  144. // 如果没有指定placedBlocksContainer,尝试找到它
  145. if (!this.placedBlocksContainer) {
  146. this.placedBlocksContainer = find('Canvas/GameLevelUI/PlacedBlocks');
  147. if (!this.placedBlocksContainer) {
  148. console.warn('找不到PlacedBlocks节点,将尝试创建');
  149. }
  150. }
  151. // 确保有PlacedBlocks节点用于存放已放置的方块
  152. this.ensurePlacedBlocksNode();
  153. // 初始化玩家金币显示
  154. this.updateCoinDisplay();
  155. // 初始化网格信息
  156. this.initGridInfo();
  157. // 初始化网格占用情况
  158. this.initGridOccupationMap();
  159. if (this.refreshButton) {
  160. const btn = this.refreshButton.getComponent(Button);
  161. if (btn) {
  162. this.refreshButton.on(Button.EventType.CLICK, this.onRefreshButtonClicked, this);
  163. } else {
  164. // 兼容没有 Button 组件的情况
  165. this.refreshButton.on(Node.EventType.TOUCH_END, this.onRefreshButtonClicked, this);
  166. }
  167. }
  168. // 等待配置加载完成后生成方块
  169. this.scheduleOnce(() => {
  170. this.generateRandomBlocksInKuang();
  171. }, 0.5);
  172. }
  173. // 确保有PlacedBlocks节点
  174. ensurePlacedBlocksNode() {
  175. // 如果已经通过拖拽设置了节点,直接使用
  176. if (this.placedBlocksContainer && this.placedBlocksContainer.isValid) {
  177. return;
  178. }
  179. // 尝试查找节点
  180. this.placedBlocksContainer = find('Canvas/GameLevelUI/PlacedBlocks');
  181. if (this.placedBlocksContainer) {
  182. console.log('找到已存在的PlacedBlocks节点');
  183. return;
  184. }
  185. // 如果找不到,创建新节点
  186. const gameLevelUI = find('Canvas/GameLevelUI');
  187. if (!gameLevelUI) {
  188. console.error('找不到GameLevelUI节点,无法创建PlacedBlocks');
  189. return;
  190. }
  191. this.placedBlocksContainer = new Node('PlacedBlocks');
  192. gameLevelUI.addChild(this.placedBlocksContainer);
  193. if (!this.placedBlocksContainer.getComponent(UITransform)) {
  194. this.placedBlocksContainer.addComponent(UITransform);
  195. }
  196. console.log('已在GameLevelUI下创建PlacedBlocks节点');
  197. }
  198. // 初始化网格信息
  199. initGridInfo() {
  200. if (!this.gridContainer || this.gridInitialized) return;
  201. this.gridNodes = [];
  202. for (let row = 0; row < this.GRID_ROWS; row++) {
  203. this.gridNodes[row] = [];
  204. }
  205. for (let i = 0; i < this.gridContainer.children.length; i++) {
  206. const grid = this.gridContainer.children[i];
  207. if (grid.name.startsWith('Grid_')) {
  208. const parts = grid.name.split('_');
  209. if (parts.length === 3) {
  210. const row = parseInt(parts[1]);
  211. const col = parseInt(parts[2]);
  212. if (row >= 0 && row < this.GRID_ROWS && col >= 0 && col < this.GRID_COLS) {
  213. this.gridNodes[row][col] = grid;
  214. }
  215. }
  216. }
  217. }
  218. if (this.GRID_ROWS > 1 && this.GRID_COLS > 0) {
  219. if (this.gridNodes[0][0] && this.gridNodes[1][0]) {
  220. const pos1 = this.gridNodes[0][0].position;
  221. const pos2 = this.gridNodes[1][0].position;
  222. this.gridSpacing = Math.abs(pos2.y - pos1.y);
  223. }
  224. }
  225. this.gridInitialized = true;
  226. }
  227. // 初始化网格占用情况
  228. initGridOccupationMap() {
  229. this.gridOccupationMap = [];
  230. for (let row = 0; row < this.GRID_ROWS; row++) {
  231. const rowArray: number[] = [];
  232. for (let col = 0; col < this.GRID_COLS; col++) {
  233. rowArray.push(0);
  234. }
  235. this.gridOccupationMap.push(rowArray);
  236. }
  237. }
  238. // 在kuang下随机生成三个方块
  239. private generateRandomBlocksInKuang() {
  240. this.clearBlocks();
  241. // 检查配置管理器是否可用
  242. if (!this.configManager || !this.configManager.isConfigLoaded()) {
  243. console.warn('配置管理器未初始化或配置未加载完成,延迟生成方块');
  244. this.scheduleOnce(() => {
  245. this.generateRandomBlocksInKuang();
  246. }, 1.0);
  247. return;
  248. }
  249. if (this.blockPrefabs.length === 0) {
  250. console.error('没有可用的预制体');
  251. return;
  252. }
  253. const kuangNode = this.kuangContainer;
  254. if (!kuangNode) {
  255. console.error('找不到kuang节点');
  256. return;
  257. }
  258. const offsets = [
  259. new Vec3(-200, 0, 0),
  260. new Vec3(0, 0, 0),
  261. new Vec3(200, 0, 0)
  262. ];
  263. const dbNodes = [
  264. kuangNode.getChildByName('db01'),
  265. kuangNode.getChildByName('db02'),
  266. kuangNode.getChildByName('db03')
  267. ];
  268. console.log('开始在kuang容器中生成随机武器方块');
  269. for (let i = 0; i < 3; i++) {
  270. // 获取随机武器配置
  271. const weaponConfig = this.configManager.getRandomWeapon();
  272. if (!weaponConfig) {
  273. console.error(`无法获取第 ${i + 1} 个武器配置`);
  274. continue;
  275. }
  276. // 基于武器配置选择合适的预制体
  277. const prefab = this.selectPrefabForWeapon(weaponConfig);
  278. if (!prefab) {
  279. console.error(`无法为武器 ${weaponConfig.name} 选择合适的预制体`);
  280. continue;
  281. }
  282. const block = instantiate(prefab);
  283. kuangNode.addChild(block);
  284. block.position = offsets[i];
  285. // 设置方块名称
  286. block.name = `WeaponBlock_${weaponConfig.id}`;
  287. // 保存武器配置到方块
  288. this.blockWeaponConfigs.set(block, weaponConfig);
  289. block['weaponConfig'] = weaponConfig;
  290. block['weaponId'] = weaponConfig.id;
  291. this.originalPositions.set(block, offsets[i].clone());
  292. this.blockLocations.set(block, 'kuang');
  293. this.blocks.push(block);
  294. if (dbNodes[i]) {
  295. const priceNode = dbNodes[i].getChildByName('Price');
  296. if (priceNode) {
  297. this.blockPriceMap.set(block, priceNode);
  298. priceNode.active = true;
  299. // 根据武器稀有度设置价格
  300. this.setBlockPriceByRarity(priceNode, weaponConfig.rarity);
  301. }
  302. this.associateDbNodeWithBlock(block, dbNodes[i]);
  303. }
  304. // 设置方块的武器外观
  305. this.setupBlockWeaponVisual(block, weaponConfig);
  306. this.setupDragEvents(block);
  307. console.log(`✅ 生成武器方块 ${i + 1}/3: ${weaponConfig.name} (${weaponConfig.rarity}) 在位置 (${offsets[i].x.toFixed(2)}, ${offsets[i].y.toFixed(2)})`);
  308. }
  309. console.log(`🎉 成功在kuang容器中生成了 ${this.blocks.length} 个武器方块`);
  310. this.updateCoinDisplay();
  311. }
  312. // 将db节点与方块关联
  313. associateDbNodeWithBlock(block: Node, dbNode: Node) {
  314. block['dbNode'] = dbNode;
  315. block.on(Node.EventType.TRANSFORM_CHANGED, () => {
  316. if (dbNode && block.parent) {
  317. const location = this.blockLocations.get(block);
  318. if (location === 'grid') {
  319. dbNode.active = false;
  320. return;
  321. }
  322. dbNode.active = true;
  323. const worldPos = block.parent.getComponent(UITransform).convertToWorldSpaceAR(block.position);
  324. const localPos = dbNode.parent.getComponent(UITransform).convertToNodeSpaceAR(worldPos);
  325. dbNode.position = new Vec3(localPos.x, localPos.y - 80, localPos.z);
  326. }
  327. });
  328. }
  329. // 更新金币显示
  330. updateCoinDisplay() {
  331. if (this.coinLabelNode) {
  332. const label = this.coinLabelNode.getComponent(Label);
  333. if (label) {
  334. label.string = this.playerCoins.toString();
  335. }
  336. }
  337. }
  338. // 获取方块价格
  339. getBlockPrice(block: Node): number {
  340. const priceNode = this.blockPriceMap.get(block);
  341. if (priceNode) {
  342. const label = priceNode.getComponent(Label);
  343. if (label) {
  344. const price = parseInt(label.string);
  345. if (!isNaN(price)) {
  346. return price;
  347. }
  348. }
  349. }
  350. return 50;
  351. }
  352. // 隐藏价格标签
  353. hidePriceLabel(block: Node) {
  354. const priceNode = this.blockPriceMap.get(block);
  355. if (priceNode) {
  356. priceNode.active = false;
  357. }
  358. }
  359. // 显示价格标签
  360. showPriceLabel(block: Node) {
  361. const priceNode = this.blockPriceMap.get(block);
  362. if (priceNode) {
  363. priceNode.active = true;
  364. }
  365. }
  366. // 扣除玩家金币
  367. deductPlayerCoins(amount: number): boolean {
  368. if (this.playerCoins >= amount) {
  369. this.playerCoins -= amount;
  370. this.updateCoinDisplay();
  371. return true;
  372. }
  373. return false;
  374. }
  375. // 归还玩家金币
  376. refundPlayerCoins(amount: number) {
  377. this.playerCoins += amount;
  378. this.updateCoinDisplay();
  379. }
  380. // 设置拖拽事件
  381. setupDragEvents(block: Node) {
  382. block.on(Node.EventType.TOUCH_START, (event: EventTouch) => {
  383. if (this.gameStarted && this.blockLocations.get(block) === 'grid') {
  384. if (!this.canMoveBlock(block)) {
  385. return;
  386. }
  387. }
  388. this.currentDragBlock = block;
  389. this.startPos = event.getUILocation();
  390. this.blockStartPos.set(block.position);
  391. this.currentDragBlock['startLocation'] = this.blockLocations.get(block);
  392. block.setSiblingIndex(block.parent.children.length - 1);
  393. this.tempStoreBlockOccupiedGrids(block);
  394. }, this);
  395. block.on(Node.EventType.TOUCH_MOVE, (event: EventTouch) => {
  396. if (this.gameStarted && this.blockLocations.get(block) === 'grid') {
  397. if (!this.canMoveBlock(block)) {
  398. return;
  399. }
  400. }
  401. if (!this.currentDragBlock) return;
  402. const location = event.getUILocation();
  403. const deltaX = location.x - this.startPos.x;
  404. const deltaY = location.y - this.startPos.y;
  405. this.currentDragBlock.position = new Vec3(
  406. this.blockStartPos.x + deltaX,
  407. this.blockStartPos.y + deltaY,
  408. this.blockStartPos.z
  409. );
  410. }, this);
  411. block.on(Node.EventType.TOUCH_END, (event: EventTouch) => {
  412. if (this.gameStarted && this.blockLocations.get(block) === 'grid') {
  413. if (!this.canMoveBlock(block)) {
  414. return;
  415. }
  416. }
  417. if (this.currentDragBlock) {
  418. this.handleBlockDrop(event);
  419. // 如果成功移动且游戏已开始,设置冷却
  420. if (this.gameStarted && this.blockLocations.get(this.currentDragBlock) === 'grid') {
  421. this.setBlockCooldown(this.currentDragBlock);
  422. }
  423. this.currentDragBlock = null;
  424. }
  425. }, this);
  426. block.on(Node.EventType.TOUCH_CANCEL, () => {
  427. if (this.currentDragBlock) {
  428. this.returnBlockToOriginalPosition();
  429. this.currentDragBlock = null;
  430. }
  431. }, this);
  432. }
  433. // 处理方块放下
  434. handleBlockDrop(event: EventTouch) {
  435. const touchPos = event.getUILocation();
  436. const startLocation = this.currentDragBlock['startLocation'];
  437. if (this.isInKuangArea(touchPos)) {
  438. this.returnBlockToKuang(startLocation);
  439. } else if (this.tryPlaceBlockToGrid(this.currentDragBlock)) {
  440. this.handleSuccessfulPlacement(startLocation);
  441. console.log('成功放置,移动到网格');
  442. } else {
  443. this.returnBlockToOriginalPosition();
  444. }
  445. }
  446. // 返回方块到kuang区域
  447. returnBlockToKuang(startLocation: string) {
  448. const originalPos = this.originalPositions.get(this.currentDragBlock);
  449. if (originalPos) {
  450. const kuangNode = this.kuangContainer;
  451. if (kuangNode && this.currentDragBlock.parent !== kuangNode) {
  452. this.currentDragBlock.removeFromParent();
  453. kuangNode.addChild(this.currentDragBlock);
  454. }
  455. this.currentDragBlock.position = originalPos.clone();
  456. }
  457. this.restoreBlockOccupiedGrids(this.currentDragBlock);
  458. this.blockLocations.set(this.currentDragBlock, 'kuang');
  459. this.showPriceLabel(this.currentDragBlock);
  460. if (startLocation === 'grid') {
  461. const price = this.getBlockPrice(this.currentDragBlock);
  462. this.refundPlayerCoins(price);
  463. this.currentDragBlock['placedBefore'] = false;
  464. }
  465. const dbNode = this.currentDragBlock['dbNode'];
  466. if (dbNode) {
  467. dbNode.active = true;
  468. this.currentDragBlock.emit(Node.EventType.TRANSFORM_CHANGED);
  469. }
  470. }
  471. // 处理成功放置
  472. handleSuccessfulPlacement(startLocation: string) {
  473. const price = this.getBlockPrice(this.currentDragBlock);
  474. if (startLocation === 'grid') {
  475. this.clearTempStoredOccupiedGrids(this.currentDragBlock);
  476. this.blockLocations.set(this.currentDragBlock, 'grid');
  477. this.hidePriceLabel(this.currentDragBlock);
  478. const dbNode = this.currentDragBlock['dbNode'];
  479. if (dbNode) {
  480. dbNode.active = false;
  481. }
  482. // 立即将方块移动到PlacedBlocks节点下,不等游戏开始
  483. this.moveBlockToPlacedBlocks(this.currentDragBlock);
  484. // 如果游戏已开始,添加锁定视觉提示
  485. if (this.gameStarted) {
  486. this.addLockedVisualHint(this.currentDragBlock);
  487. }
  488. } else {
  489. if (this.deductPlayerCoins(price)) {
  490. this.clearTempStoredOccupiedGrids(this.currentDragBlock);
  491. this.blockLocations.set(this.currentDragBlock, 'grid');
  492. this.hidePriceLabel(this.currentDragBlock);
  493. const dbNode = this.currentDragBlock['dbNode'];
  494. if (dbNode) {
  495. dbNode.active = false;
  496. }
  497. this.currentDragBlock['placedBefore'] = true;
  498. // 立即将方块移动到PlacedBlocks节点下,不等游戏开始
  499. this.moveBlockToPlacedBlocks(this.currentDragBlock);
  500. // 如果游戏已开始,添加锁定视觉提示
  501. if (this.gameStarted) {
  502. this.addLockedVisualHint(this.currentDragBlock);
  503. }
  504. } else {
  505. this.returnBlockToOriginalPosition();
  506. }
  507. }
  508. }
  509. // 返回方块到原位置
  510. returnBlockToOriginalPosition() {
  511. const currentLocation = this.blockLocations.get(this.currentDragBlock);
  512. if (currentLocation === 'kuang') {
  513. const originalPos = this.originalPositions.get(this.currentDragBlock);
  514. if (originalPos) {
  515. this.currentDragBlock.position = originalPos.clone();
  516. }
  517. } else {
  518. this.currentDragBlock.position = this.blockStartPos.clone();
  519. }
  520. this.restoreBlockOccupiedGrids(this.currentDragBlock);
  521. this.showPriceLabel(this.currentDragBlock);
  522. const dbNode = this.currentDragBlock['dbNode'];
  523. if (dbNode) {
  524. dbNode.active = true;
  525. this.currentDragBlock.emit(Node.EventType.TRANSFORM_CHANGED);
  526. }
  527. }
  528. // 检查是否在kuang区域内
  529. isInKuangArea(touchPos: Vec2): boolean {
  530. if (!this.kuangContainer) return false;
  531. const kuangTransform = this.kuangContainer.getComponent(UITransform);
  532. if (!kuangTransform) return false;
  533. const kuangBoundingBox = new Rect(
  534. this.kuangContainer.worldPosition.x - kuangTransform.width * kuangTransform.anchorX,
  535. this.kuangContainer.worldPosition.y - kuangTransform.height * kuangTransform.anchorY,
  536. kuangTransform.width,
  537. kuangTransform.height
  538. );
  539. return kuangBoundingBox.contains(new Vec2(touchPos.x, touchPos.y));
  540. }
  541. // 临时保存方块占用的网格
  542. tempStoreBlockOccupiedGrids(block: Node) {
  543. const occupiedGrids = block['occupiedGrids'];
  544. if (!occupiedGrids || occupiedGrids.length === 0) return;
  545. this.tempRemovedOccupiedGrids.push({
  546. block: block,
  547. occupiedGrids: [...occupiedGrids]
  548. });
  549. for (const grid of occupiedGrids) {
  550. if (grid.row >= 0 && grid.row < this.GRID_ROWS &&
  551. grid.col >= 0 && grid.col < this.GRID_COLS) {
  552. this.gridOccupationMap[grid.row][grid.col] = 0;
  553. }
  554. }
  555. block['occupiedGrids'] = [];
  556. }
  557. // 恢复方块原来的占用状态
  558. restoreBlockOccupiedGrids(block: Node) {
  559. const index = this.tempRemovedOccupiedGrids.findIndex(item => item.block === block);
  560. if (index === -1) return;
  561. const savedItem = this.tempRemovedOccupiedGrids[index];
  562. for (const grid of savedItem.occupiedGrids) {
  563. if (grid.row >= 0 && grid.row < this.GRID_ROWS &&
  564. grid.col >= 0 && grid.col < this.GRID_COLS) {
  565. this.gridOccupationMap[grid.row][grid.col] = 1;
  566. }
  567. }
  568. block['occupiedGrids'] = [...savedItem.occupiedGrids];
  569. this.tempRemovedOccupiedGrids.splice(index, 1);
  570. }
  571. // 清除临时保存的占用状态
  572. clearTempStoredOccupiedGrids(block: Node) {
  573. const index = this.tempRemovedOccupiedGrids.findIndex(item => item.block === block);
  574. if (index === -1) return;
  575. this.tempRemovedOccupiedGrids.splice(index, 1);
  576. }
  577. // 尝试将方块放置到网格中
  578. tryPlaceBlockToGrid(block: Node): boolean {
  579. if (!this.gridContainer || !this.gridInitialized) return false;
  580. let b1Node = block;
  581. if (block.name !== 'B1') {
  582. b1Node = block.getChildByName('B1');
  583. if (!b1Node) {
  584. return false;
  585. }
  586. }
  587. const b1WorldPos = b1Node.parent.getComponent(UITransform).convertToWorldSpaceAR(b1Node.position);
  588. const gridPos = this.gridContainer.getComponent(UITransform).convertToNodeSpaceAR(b1WorldPos);
  589. const gridSize = this.gridContainer.getComponent(UITransform).contentSize;
  590. const halfWidth = gridSize.width / 2;
  591. const halfHeight = gridSize.height / 2;
  592. const tolerance = this.gridSpacing * 0.5;
  593. if (gridPos.x < -halfWidth - tolerance || gridPos.x > halfWidth + tolerance ||
  594. gridPos.y < -halfHeight - tolerance || gridPos.y > halfHeight + tolerance) {
  595. return false;
  596. }
  597. const nearestGrid = this.findNearestGridNode(gridPos);
  598. if (!nearestGrid) {
  599. return false;
  600. }
  601. return this.tryPlaceBlockToSpecificGrid(block, nearestGrid);
  602. }
  603. // 找到最近的网格节点
  604. findNearestGridNode(position: Vec3): Node {
  605. if (!this.gridContainer || !this.gridInitialized) return null;
  606. let nearestNode: Node = null;
  607. let minDistance = Number.MAX_VALUE;
  608. for (let row = 0; row < this.GRID_ROWS; row++) {
  609. for (let col = 0; col < this.GRID_COLS; col++) {
  610. const grid = this.gridNodes[row][col];
  611. if (grid) {
  612. const distance = Vec3.distance(position, grid.position);
  613. if (distance < minDistance) {
  614. minDistance = distance;
  615. nearestNode = grid;
  616. }
  617. }
  618. }
  619. }
  620. if (minDistance > this.gridSpacing * 2) {
  621. return null;
  622. }
  623. return nearestNode;
  624. }
  625. // 尝试将方块放置到指定的网格节点
  626. tryPlaceBlockToSpecificGrid(block: Node, targetGrid: Node): boolean {
  627. let b1Node = block;
  628. if (block.name !== 'B1') {
  629. b1Node = block.getChildByName('B1');
  630. if (!b1Node) {
  631. return false;
  632. }
  633. }
  634. if (!this.canPlaceBlockAt(block, targetGrid)) {
  635. return false;
  636. }
  637. const gridCenterWorldPos = this.gridContainer.getComponent(UITransform).convertToWorldSpaceAR(targetGrid.position);
  638. const targetWorldPos = gridCenterWorldPos.clone();
  639. const b1LocalPos = b1Node.position.clone();
  640. let rootTargetWorldPos;
  641. if (b1Node === block) {
  642. rootTargetWorldPos = targetWorldPos.clone();
  643. } else {
  644. rootTargetWorldPos = new Vec3(
  645. targetWorldPos.x - b1LocalPos.x,
  646. targetWorldPos.y - b1LocalPos.y,
  647. targetWorldPos.z
  648. );
  649. }
  650. const rootTargetLocalPos = block.parent.getComponent(UITransform).convertToNodeSpaceAR(rootTargetWorldPos);
  651. block.position = rootTargetLocalPos;
  652. this.markOccupiedPositions(block, targetGrid);
  653. return true;
  654. }
  655. // 检查方块是否可以放置在指定位置
  656. canPlaceBlockAt(block: Node, targetGrid: Node): boolean {
  657. if (!this.gridInitialized) return false;
  658. const targetRowCol = this.getGridRowCol(targetGrid);
  659. if (!targetRowCol) return false;
  660. const parts = this.getBlockParts(block);
  661. for (const part of parts) {
  662. const row = targetRowCol.row - part.y;
  663. const col = targetRowCol.col + part.x;
  664. if (row < 0 || row >= this.GRID_ROWS || col < 0 || col >= this.GRID_COLS) {
  665. return false;
  666. }
  667. if (this.gridOccupationMap[row][col] === 1) {
  668. return false;
  669. }
  670. }
  671. return true;
  672. }
  673. // 获取网格行列索引
  674. getGridRowCol(gridNode: Node): { row: number, col: number } | null {
  675. if (!gridNode || !gridNode.name.startsWith('Grid_')) return null;
  676. const parts = gridNode.name.split('_');
  677. if (parts.length === 3) {
  678. const row = parseInt(parts[1]);
  679. const col = parseInt(parts[2]);
  680. if (row >= 0 && row < this.GRID_ROWS && col >= 0 && col < this.GRID_COLS) {
  681. return { row, col };
  682. }
  683. }
  684. return null;
  685. }
  686. // 获取方块的所有部分节点及其相对坐标
  687. getBlockParts(block: Node): { node: Node, x: number, y: number }[] {
  688. const parts: { node: Node, x: number, y: number }[] = [];
  689. parts.push({ node: block, x: 0, y: 0 });
  690. this.findBlockParts(block, parts, 0, 0);
  691. return parts;
  692. }
  693. // 递归查找方块的所有部分
  694. findBlockParts(node: Node, result: { node: Node, x: number, y: number }[], parentX: number, parentY: number) {
  695. for (let i = 0; i < node.children.length; i++) {
  696. const child = node.children[i];
  697. if (this.NON_BLOCK_NODES.indexOf(child.name) !== -1) {
  698. continue;
  699. }
  700. let x = parentX;
  701. let y = parentY;
  702. const match = child.name.match(/^\((-?\d+),(-?\d+)\)$/);
  703. if (match) {
  704. x = parseInt(match[1]);
  705. y = parseInt(match[2]);
  706. result.push({ node: child, x, y });
  707. } else if (child.name.startsWith('B')) {
  708. const relativeX = Math.round(child.position.x / this.gridSpacing);
  709. const relativeY = -Math.round(child.position.y / this.gridSpacing);
  710. x = parentX + relativeX;
  711. y = parentY + relativeY;
  712. result.push({ node: child, x, y });
  713. }
  714. this.findBlockParts(child, result, x, y);
  715. }
  716. }
  717. // 标记方块占用的格子
  718. markOccupiedPositions(block: Node, targetGrid: Node) {
  719. if (!this.gridInitialized) return;
  720. const targetRowCol = this.getGridRowCol(targetGrid);
  721. if (!targetRowCol) return;
  722. const parts = this.getBlockParts(block);
  723. block['occupiedGrids'] = [];
  724. for (const part of parts) {
  725. const row = targetRowCol.row - part.y;
  726. const col = targetRowCol.col + part.x;
  727. if (row >= 0 && row < this.GRID_ROWS && col >= 0 && col < this.GRID_COLS) {
  728. this.gridOccupationMap[row][col] = 1;
  729. block['occupiedGrids'] = block['occupiedGrids'] || [];
  730. block['occupiedGrids'].push({ row, col });
  731. }
  732. }
  733. }
  734. // 清除方块
  735. clearBlocks() {
  736. const blocksToRemove = [];
  737. for (const block of this.blocks) {
  738. if (block.isValid) {
  739. const location = this.blockLocations.get(block);
  740. if (location === 'kuang') {
  741. blocksToRemove.push(block);
  742. }
  743. }
  744. }
  745. for (const block of blocksToRemove) {
  746. const dbNode = block['dbNode'];
  747. if (dbNode && dbNode.isValid) {
  748. block.off(Node.EventType.TRANSFORM_CHANGED);
  749. const kuangNode = this.kuangContainer;
  750. if (kuangNode) {
  751. const dbName = dbNode.name;
  752. if (!kuangNode.getChildByName(dbName)) {
  753. dbNode.parent = kuangNode;
  754. }
  755. }
  756. }
  757. const index = this.blocks.indexOf(block);
  758. if (index !== -1) {
  759. this.blocks.splice(index, 1);
  760. }
  761. this.originalPositions.delete(block);
  762. this.blockLocations.delete(block);
  763. this.blockPriceMap.delete(block);
  764. // 清理武器配置映射
  765. this.blockWeaponConfigs.delete(block);
  766. block.destroy();
  767. }
  768. }
  769. // 游戏开始时调用
  770. onGameStart() {
  771. this.gameStarted = true;
  772. console.log('游戏已开始,已放置的方块将有移动冷却时间');
  773. for (const block of this.blocks) {
  774. if (block.isValid) {
  775. const location = this.blockLocations.get(block);
  776. if (location === 'grid') {
  777. this.hidePriceLabel(block);
  778. const dbNode = block['dbNode'];
  779. if (dbNode) {
  780. dbNode.active = false;
  781. }
  782. this.moveBlockToPlacedBlocks(block);
  783. this.addLockedVisualHint(block);
  784. }
  785. }
  786. }
  787. }
  788. // 游戏重置时调用
  789. onGameReset() {
  790. this.gameStarted = false;
  791. this.clearAllCooldowns();
  792. console.log('游戏已重置,方块可以自由移动');
  793. }
  794. // 添加视觉提示,表明方块已锁定
  795. addLockedVisualHint(block: Node) {
  796. const children = block.children;
  797. for (let i = 0; i < children.length; i++) {
  798. const child = children[i];
  799. if (this.NON_BLOCK_NODES.indexOf(child.name) !== -1) {
  800. continue;
  801. }
  802. child.setScale(new Vec3(0.95, 0.95, 1));
  803. }
  804. }
  805. // 将方块移动到PlacedBlocks节点下
  806. moveBlockToPlacedBlocks(block: Node) {
  807. if (!this.placedBlocksContainer) {
  808. console.error('PlacedBlocks容器未设置');
  809. return;
  810. }
  811. if (!this.placedBlocksContainer.isValid) {
  812. console.error('PlacedBlocks容器已失效');
  813. return;
  814. }
  815. const worldPosition = new Vec3();
  816. block.getWorldPosition(worldPosition);
  817. // 移除旧的触摸事件监听器
  818. block.off(Node.EventType.TOUCH_START);
  819. block.off(Node.EventType.TOUCH_MOVE);
  820. block.off(Node.EventType.TOUCH_END);
  821. block.off(Node.EventType.TOUCH_CANCEL);
  822. block.removeFromParent();
  823. this.placedBlocksContainer.addChild(block);
  824. block.setWorldPosition(worldPosition);
  825. // 重新设置触摸事件监听器
  826. this.setupDragEvents(block);
  827. console.log(`方块 ${block.name} 已移动到PlacedBlocks容器,并重新设置了触摸事件`);
  828. }
  829. // 根据武器配置选择合适的预制体
  830. private selectPrefabForWeapon(weaponConfig: WeaponConfig): Prefab | null {
  831. if (this.blockPrefabs.length === 0) {
  832. return null;
  833. }
  834. // 根据武器类型或稀有度选择预制体
  835. // 这里可以根据实际需求来选择不同的预制体
  836. // 目前简单地随机选择一个预制体
  837. const randomIndex = Math.floor(Math.random() * this.blockPrefabs.length);
  838. return this.blockPrefabs[randomIndex];
  839. }
  840. // 根据稀有度设置方块价格
  841. private setBlockPriceByRarity(priceNode: Node, rarity: string) {
  842. const label = priceNode.getComponent(Label);
  843. if (!label) {
  844. return;
  845. }
  846. let price: number;
  847. switch (rarity) {
  848. case 'common':
  849. price = 50;
  850. break;
  851. case 'uncommon':
  852. price = 100;
  853. break;
  854. case 'rare':
  855. price = 200;
  856. break;
  857. case 'epic':
  858. price = 350;
  859. break;
  860. case 'legendary':
  861. price = 500;
  862. break;
  863. default:
  864. price = 50;
  865. }
  866. label.string = price.toString();
  867. }
  868. // 设置方块的武器外观
  869. private setupBlockWeaponVisual(block: Node, weaponConfig: WeaponConfig) {
  870. // 设置方块的稀有度颜色
  871. this.setBlockRarityColor(block, weaponConfig.rarity);
  872. // 加载武器图标
  873. this.loadWeaponIcon(block, weaponConfig);
  874. }
  875. // 设置方块稀有度颜色
  876. private setBlockRarityColor(block: Node, rarity: string) {
  877. const sprite = block.getComponent(Sprite);
  878. if (!sprite) {
  879. return;
  880. }
  881. // 根据稀有度设置颜色
  882. let color: Color;
  883. switch (rarity) {
  884. case 'common':
  885. color = new Color(255, 255, 255); // 白色
  886. break;
  887. case 'uncommon':
  888. color = new Color(0, 255, 0); // 绿色
  889. break;
  890. case 'rare':
  891. color = new Color(0, 100, 255); // 蓝色
  892. break;
  893. case 'epic':
  894. color = new Color(160, 32, 240); // 紫色
  895. break;
  896. case 'legendary':
  897. color = new Color(255, 165, 0); // 橙色
  898. break;
  899. default:
  900. color = new Color(255, 255, 255); // 默认白色
  901. }
  902. sprite.color = color;
  903. }
  904. // 加载武器图标
  905. private loadWeaponIcon(block: Node, weaponConfig: WeaponConfig) {
  906. // 根据预制体结构:WeaponBlock -> B1 -> Weapon
  907. const b1Node = block.getChildByName('B1');
  908. if (!b1Node) {
  909. console.warn('找不到B1节点');
  910. return;
  911. }
  912. const weaponNode = b1Node.getChildByName('Weapon');
  913. if (!weaponNode) {
  914. console.warn('找不到Weapon节点');
  915. return;
  916. }
  917. const weaponSprite = weaponNode.getComponent(Sprite);
  918. if (!weaponSprite) {
  919. console.warn('Weapon节点上没有Sprite组件');
  920. return;
  921. }
  922. // 获取武器配置中的图片路径
  923. const spriteConfig = weaponConfig.visualConfig?.weaponSprites;
  924. if (!spriteConfig) {
  925. console.warn(`武器 ${weaponConfig.name} 没有配置图片信息`);
  926. return;
  927. }
  928. // 选择合适的图片路径(这里默认使用1x1)
  929. const spritePath = spriteConfig['1x1'] || spriteConfig['1x2'] || spriteConfig['2x1'] || spriteConfig['2x2'];
  930. if (!spritePath) {
  931. console.warn(`武器 ${weaponConfig.name} 没有可用的图片路径`);
  932. return;
  933. }
  934. // 正确的SpriteFrame子资源路径
  935. const spriteFramePath = `${spritePath}/spriteFrame`;
  936. console.log(`正在加载武器图片: ${spriteFramePath}`);
  937. // 加载SpriteFrame子资源
  938. resources.load(spriteFramePath, SpriteFrame, (err, spriteFrame) => {
  939. if (err) {
  940. console.warn(`加载武器图片失败: ${spriteFramePath}`, err);
  941. return;
  942. }
  943. if (weaponSprite && spriteFrame) {
  944. weaponSprite.spriteFrame = spriteFrame;
  945. }
  946. });
  947. }
  948. // 根据方块获取武器配置
  949. public getBlockWeaponConfig(block: Node): WeaponConfig | null {
  950. return this.blockWeaponConfigs.get(block) || block['weaponConfig'] || null;
  951. }
  952. // 获取方块的武器ID
  953. public getBlockWeaponId(block: Node): string | null {
  954. const weaponConfig = this.getBlockWeaponConfig(block);
  955. return weaponConfig ? weaponConfig.id : null;
  956. }
  957. // 刷新方块 - 重新生成三个新的武器方块
  958. public refreshBlocks() {
  959. console.log('刷新方块,生成新的武器方块');
  960. this.generateRandomBlocksInKuang();
  961. }
  962. // 刷新按钮点击回调
  963. private onRefreshButtonClicked() {
  964. this.refreshBlocks();
  965. }
  966. }