test_gameend_node_debug.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. /**
  2. * GameEnd节点实际状态调试脚本
  3. * 模拟实际游戏环境,检查GameEnd节点的各种状态
  4. */
  5. // 模拟事件系统
  6. class MockEventBus {
  7. constructor() {
  8. this.listeners = new Map();
  9. this.eventLog = [];
  10. }
  11. on(event, callback, context) {
  12. if (!this.listeners.has(event)) {
  13. this.listeners.set(event, []);
  14. }
  15. this.listeners.get(event).push({ callback, context });
  16. console.log(`[EventBus] 注册事件监听器: ${event}`);
  17. }
  18. emit(event, ...args) {
  19. console.log(`[EventBus] 触发事件: ${event}`);
  20. this.eventLog.push({ event, timestamp: Date.now(), args });
  21. if (this.listeners.has(event)) {
  22. const listeners = this.listeners.get(event);
  23. listeners.forEach(({ callback, context }) => {
  24. try {
  25. callback.call(context, ...args);
  26. } catch (error) {
  27. console.error(`[EventBus] 事件处理错误 ${event}:`, error);
  28. }
  29. });
  30. }
  31. }
  32. off(event, callback, context) {
  33. if (this.listeners.has(event)) {
  34. const listeners = this.listeners.get(event);
  35. const index = listeners.findIndex(l => l.callback === callback && l.context === context);
  36. if (index !== -1) {
  37. listeners.splice(index, 1);
  38. console.log(`[EventBus] 移除事件监听器: ${event}`);
  39. }
  40. }
  41. }
  42. getEventLog() {
  43. return this.eventLog;
  44. }
  45. }
  46. // 模拟GameEvents
  47. const GameEvents = {
  48. GAME_DEFEAT: 'GAME_DEFEAT',
  49. GAME_RESUME: 'GAME_RESUME',
  50. GAME_START: 'GAME_START',
  51. RESET_UI_STATES: 'RESET_UI_STATES'
  52. };
  53. // 模拟UIOpacity组件
  54. class MockUIOpacity {
  55. constructor() {
  56. this.opacity = 255;
  57. }
  58. }
  59. // 模拟Node
  60. class MockNode {
  61. constructor(name, parent = null) {
  62. this.name = name;
  63. this.active = true;
  64. this.scale = { x: 1, y: 1, z: 1 };
  65. this.position = { x: 0, y: 0, z: 0 };
  66. this.parent = parent;
  67. this.children = [];
  68. this.components = new Map();
  69. this.siblingIndex = 0;
  70. if (parent) {
  71. parent.children.push(this);
  72. this.siblingIndex = parent.children.length - 1;
  73. }
  74. }
  75. setScale(x, y, z) {
  76. this.scale = { x, y, z };
  77. }
  78. setPosition(x, y, z) {
  79. this.position = { x, y, z };
  80. }
  81. getComponent(componentType) {
  82. return this.components.get(componentType) || null;
  83. }
  84. addComponent(componentType) {
  85. const component = new componentType();
  86. this.components.set(componentType, component);
  87. return component;
  88. }
  89. setSiblingIndex(index) {
  90. if (!this.parent) return;
  91. // 从当前位置移除
  92. const currentIndex = this.parent.children.indexOf(this);
  93. if (currentIndex !== -1) {
  94. this.parent.children.splice(currentIndex, 1);
  95. }
  96. // 插入到新位置
  97. this.parent.children.splice(index, 0, this);
  98. this.siblingIndex = index;
  99. // 更新其他子节点的siblingIndex
  100. this.parent.children.forEach((child, i) => {
  101. child.siblingIndex = i;
  102. });
  103. }
  104. getNodeStatus() {
  105. return {
  106. name: this.name,
  107. active: this.active,
  108. scale: { ...this.scale },
  109. position: { ...this.position },
  110. siblingIndex: this.siblingIndex,
  111. opacity: this.getComponent(MockUIOpacity)?.opacity || 'N/A',
  112. childrenCount: this.children.length
  113. };
  114. }
  115. }
  116. // 模拟Canvas层级结构
  117. function createMockCanvasStructure() {
  118. const canvas = new MockNode('Canvas');
  119. // 创建主要UI面板
  120. const gameLevelUI = new MockNode('GameLevelUI', canvas);
  121. const topArea = new MockNode('TopArea', canvas);
  122. const gameEnd = new MockNode('GameEnd', canvas);
  123. const pauseMenu = new MockNode('PauseMenu', canvas);
  124. const settingsPanel = new MockNode('SettingsPanel', canvas);
  125. // 设置初始层级顺序(siblingIndex越大越在上层)
  126. gameLevelUI.setSiblingIndex(0); // 最底层
  127. topArea.setSiblingIndex(1);
  128. gameEnd.setSiblingIndex(2); // GameEnd在中间层
  129. pauseMenu.setSiblingIndex(3);
  130. settingsPanel.setSiblingIndex(4); // 最顶层
  131. // 为GameEnd添加UIOpacity组件
  132. gameEnd.addComponent(MockUIOpacity);
  133. console.log('[Canvas] 模拟Canvas层级结构创建完成');
  134. console.log('[Canvas] 节点层级顺序(从底到顶):');
  135. canvas.children.forEach((child, index) => {
  136. console.log(` ${index}: ${child.name} (siblingIndex: ${child.siblingIndex})`);
  137. });
  138. return { canvas, gameEnd, pauseMenu, settingsPanel };
  139. }
  140. // 模拟GameEnd组件(简化版)
  141. class MockGameEnd {
  142. constructor(node, eventBus) {
  143. this.node = node;
  144. this.eventBus = eventBus;
  145. this.animationDuration = 0.3;
  146. this.isVisible = false;
  147. this.animationCount = 0;
  148. this.hasProcessedGameEnd = false;
  149. this.setupEventListeners();
  150. this.initializeHiddenStateKeepActive();
  151. }
  152. setupEventListeners() {
  153. this.eventBus.on(GameEvents.GAME_DEFEAT, this.onGameDefeat, this);
  154. this.eventBus.on(GameEvents.RESET_UI_STATES, this.onResetUI, this);
  155. }
  156. onGameDefeat() {
  157. console.log('[GameEnd] 接收到GAME_DEFEAT事件');
  158. console.log('[GameEnd] 游戏失败事件处理,开始统一处理流程');
  159. if (this.hasProcessedGameEnd) {
  160. console.log('[GameEnd] 游戏结束已处理过,跳过重复计算');
  161. return;
  162. }
  163. this.hasProcessedGameEnd = true;
  164. this.showEndPanelWithAnimation();
  165. }
  166. showEndPanelWithAnimation() {
  167. console.log('[GameEnd] 开始显示结算面板动画');
  168. // 检查节点状态
  169. console.log('[GameEnd] 动画前节点状态:', this.node.getNodeStatus());
  170. // 确保节点处于激活状态
  171. if (!this.node.active) {
  172. this.node.active = true;
  173. console.log('[GameEnd] 激活GameEnd节点');
  174. }
  175. // 检查是否被其他面板遮挡
  176. this.checkUILayering();
  177. // 确保有UIOpacity组件
  178. let uiOpacity = this.node.getComponent(MockUIOpacity);
  179. if (!uiOpacity) {
  180. uiOpacity = this.node.addComponent(MockUIOpacity);
  181. console.log('[GameEnd] 添加UIOpacity组件');
  182. }
  183. // 播放动画
  184. if (this.animationDuration > 0) {
  185. console.log(`[GameEnd] 动画持续时间: ${this.animationDuration}秒,开始播放GameEnd面板弹出动画`);
  186. this.playShowAnimation();
  187. } else {
  188. this.node.setScale(1, 1, 1);
  189. uiOpacity.opacity = 255;
  190. console.log('[GameEnd] 无动画配置,直接显示面板');
  191. }
  192. this.animationCount++;
  193. this.isVisible = true;
  194. console.log('[GameEnd] 动画后节点状态:', this.node.getNodeStatus());
  195. console.log('[GameEnd] GameEnd面板显示流程完成');
  196. }
  197. playShowAnimation() {
  198. console.log('[GameEnd] 开始播放GameEnd面板弹出动画');
  199. // 设置节点位置到屏幕中心
  200. this.node.setPosition(0, 0, 0);
  201. console.log('[GameEnd] 已将面板居中到屏幕中央');
  202. // 设置初始状态
  203. this.node.setScale(0.3, 0.3, 1);
  204. const uiOpacity = this.node.getComponent(MockUIOpacity);
  205. if (uiOpacity) {
  206. uiOpacity.opacity = 0;
  207. }
  208. console.log('[GameEnd] 设置动画初始状态 - 缩放: 0.3, 透明度: 0');
  209. // 模拟动画完成
  210. setTimeout(() => {
  211. this.node.setScale(1, 1, 1);
  212. if (uiOpacity) {
  213. uiOpacity.opacity = 255;
  214. }
  215. console.log('[GameEnd] GameEnd面板缩放动画完成');
  216. console.log('[GameEnd] GameEnd面板淡入动画完成');
  217. }, this.animationDuration * 1000);
  218. console.log('[GameEnd] GameEnd面板弹出动画开始执行');
  219. }
  220. initializeHiddenStateKeepActive() {
  221. const uiOpacity = this.node.getComponent(MockUIOpacity) || this.node.addComponent(MockUIOpacity);
  222. uiOpacity.opacity = 0;
  223. this.node.setScale(0.3, 0.3, 1);
  224. console.log('[GameEnd] 初始化为隐藏状态(保持激活) - 缩放: 0.3, 透明度: 0');
  225. }
  226. onResetUI() {
  227. console.log('[GameEnd] 重置UI状态');
  228. this.hasProcessedGameEnd = false;
  229. this.isVisible = false;
  230. this.node.active = false;
  231. console.log('[GameEnd] UI状态重置完成');
  232. }
  233. checkUILayering() {
  234. if (!this.node.parent) return;
  235. console.log('[GameEnd] 检查UI层级:');
  236. console.log(`[GameEnd] 当前GameEnd的siblingIndex: ${this.node.siblingIndex}`);
  237. // 检查是否有其他激活的UI面板在更高层级
  238. const higherLevelPanels = this.node.parent.children.filter(child =>
  239. child.siblingIndex > this.node.siblingIndex && child.active && child !== this.node
  240. );
  241. if (higherLevelPanels.length > 0) {
  242. console.log('[GameEnd] ⚠️ 发现更高层级的激活面板:');
  243. higherLevelPanels.forEach(panel => {
  244. console.log(`[GameEnd] - ${panel.name} (siblingIndex: ${panel.siblingIndex})`);
  245. });
  246. console.log('[GameEnd] 这些面板可能遮挡GameEnd显示');
  247. } else {
  248. console.log('[GameEnd] ✅ 没有更高层级的面板遮挡');
  249. }
  250. }
  251. getStatus() {
  252. return {
  253. animationCount: this.animationCount,
  254. isVisible: this.isVisible,
  255. hasProcessedGameEnd: this.hasProcessedGameEnd,
  256. nodeStatus: this.node.getNodeStatus()
  257. };
  258. }
  259. }
  260. // 模拟Wall组件
  261. class MockWall {
  262. constructor(eventBus) {
  263. this.eventBus = eventBus;
  264. this.currentHealth = 100;
  265. this.maxHealth = 100;
  266. }
  267. takeDamage(damage) {
  268. console.log(`[Wall] 受到伤害: ${damage}, 当前血量: ${this.currentHealth}`);
  269. this.currentHealth -= damage;
  270. if (this.currentHealth <= 0) {
  271. console.log('[Wall] 墙体血量为0,调用onWallDestroyed');
  272. this.onWallDestroyed();
  273. }
  274. }
  275. onWallDestroyed() {
  276. console.log('[Wall] 墙体被摧毁,触发GAME_DEFEAT事件');
  277. this.eventBus.emit(GameEvents.GAME_DEFEAT);
  278. }
  279. }
  280. // 测试函数1:正常情况下的墙体失败
  281. function testNormalWallDefeat() {
  282. console.log('\n=== 测试1:正常情况下的墙体失败 ===');
  283. const eventBus = new MockEventBus();
  284. const { canvas, gameEnd: gameEndNode } = createMockCanvasStructure();
  285. const gameEnd = new MockGameEnd(gameEndNode, eventBus);
  286. const wall = new MockWall(eventBus);
  287. console.log('\n--- 初始状态 ---');
  288. console.log('GameEnd状态:', gameEnd.getStatus());
  289. console.log('\n--- 模拟墙体血量为0 ---');
  290. wall.takeDamage(100);
  291. console.log('\n--- 检查结果 ---');
  292. const status = gameEnd.getStatus();
  293. console.log('GameEnd状态:', status);
  294. return {
  295. scenario: 'normal_wall_defeat',
  296. success: status.animationCount === 1 && status.isVisible,
  297. status: status
  298. };
  299. }
  300. // 测试函数2:有其他UI面板干扰的情况
  301. function testUIInterferenceWallDefeat() {
  302. console.log('\n=== 测试2:UI面板干扰情况下的墙体失败 ===');
  303. const eventBus = new MockEventBus();
  304. const { canvas, gameEnd: gameEndNode, pauseMenu, settingsPanel } = createMockCanvasStructure();
  305. const gameEnd = new MockGameEnd(gameEndNode, eventBus);
  306. const wall = new MockWall(eventBus);
  307. console.log('\n--- 模拟其他UI面板激活 ---');
  308. pauseMenu.active = true;
  309. settingsPanel.active = true;
  310. console.log('PauseMenu激活状态:', pauseMenu.active, 'siblingIndex:', pauseMenu.siblingIndex);
  311. console.log('SettingsPanel激活状态:', settingsPanel.active, 'siblingIndex:', settingsPanel.siblingIndex);
  312. console.log('\n--- 初始状态 ---');
  313. console.log('GameEnd状态:', gameEnd.getStatus());
  314. console.log('\n--- 模拟墙体血量为0 ---');
  315. wall.takeDamage(100);
  316. console.log('\n--- 检查结果 ---');
  317. const status = gameEnd.getStatus();
  318. console.log('GameEnd状态:', status);
  319. return {
  320. scenario: 'ui_interference_wall_defeat',
  321. success: status.animationCount === 1 && status.isVisible,
  322. status: status,
  323. interference: {
  324. pauseMenuActive: pauseMenu.active,
  325. settingsPanelActive: settingsPanel.active
  326. }
  327. };
  328. }
  329. // 测试函数3:GameEnd层级被调整的情况
  330. function testLayerOrderWallDefeat() {
  331. console.log('\n=== 测试3:GameEnd层级被调整情况下的墙体失败 ===');
  332. const eventBus = new MockEventBus();
  333. const { canvas, gameEnd: gameEndNode, pauseMenu } = createMockCanvasStructure();
  334. const gameEnd = new MockGameEnd(gameEndNode, eventBus);
  335. const wall = new MockWall(eventBus);
  336. console.log('\n--- 模拟GameEnd层级被意外调整到最底层 ---');
  337. gameEndNode.setSiblingIndex(0); // 调整到最底层
  338. pauseMenu.active = true;
  339. console.log('调整后的层级顺序:');
  340. canvas.children.forEach((child, index) => {
  341. console.log(` ${index}: ${child.name} (siblingIndex: ${child.siblingIndex}, active: ${child.active})`);
  342. });
  343. console.log('\n--- 初始状态 ---');
  344. console.log('GameEnd状态:', gameEnd.getStatus());
  345. console.log('\n--- 模拟墙体血量为0 ---');
  346. wall.takeDamage(100);
  347. console.log('\n--- 检查结果 ---');
  348. const status = gameEnd.getStatus();
  349. console.log('GameEnd状态:', status);
  350. return {
  351. scenario: 'layer_order_wall_defeat',
  352. success: status.animationCount === 1 && status.isVisible,
  353. status: status,
  354. layerInfo: {
  355. gameEndSiblingIndex: gameEndNode.siblingIndex,
  356. pauseMenuSiblingIndex: pauseMenu.siblingIndex,
  357. pauseMenuActive: pauseMenu.active
  358. }
  359. };
  360. }
  361. // 测试函数4:UI重置事件干扰
  362. function testUIResetInterference() {
  363. console.log('\n=== 测试4:UI重置事件干扰情况 ===');
  364. const eventBus = new MockEventBus();
  365. const { canvas, gameEnd: gameEndNode } = createMockCanvasStructure();
  366. const gameEnd = new MockGameEnd(gameEndNode, eventBus);
  367. const wall = new MockWall(eventBus);
  368. console.log('\n--- 初始状态 ---');
  369. console.log('GameEnd状态:', gameEnd.getStatus());
  370. console.log('\n--- 模拟墙体血量为0 ---');
  371. wall.takeDamage(100);
  372. console.log('\n--- 模拟UI重置事件干扰 ---');
  373. setTimeout(() => {
  374. eventBus.emit(GameEvents.RESET_UI_STATES);
  375. console.log('\n--- 检查UI重置后的结果 ---');
  376. const statusAfterReset = gameEnd.getStatus();
  377. console.log('UI重置后GameEnd状态:', statusAfterReset);
  378. }, 100);
  379. console.log('\n--- 检查初始结果 ---');
  380. const status = gameEnd.getStatus();
  381. console.log('GameEnd状态:', status);
  382. return {
  383. scenario: 'ui_reset_interference',
  384. success: status.animationCount === 1 && status.isVisible,
  385. status: status
  386. };
  387. }
  388. // 运行所有测试
  389. function runAllTests() {
  390. console.log('\n🔍 开始GameEnd节点状态调试测试');
  391. const results = [];
  392. // 测试1:正常情况
  393. results.push(testNormalWallDefeat());
  394. // 测试2:UI干扰
  395. results.push(testUIInterferenceWallDefeat());
  396. // 测试3:层级问题
  397. results.push(testLayerOrderWallDefeat());
  398. // 测试4:UI重置干扰
  399. results.push(testUIResetInterference());
  400. console.log('\n=== 调试测试结果分析 ===');
  401. results.forEach((result, index) => {
  402. console.log(`\n测试${index + 1} (${result.scenario}):`);
  403. console.log(' 测试通过:', result.success ? '✅' : '❌');
  404. console.log(' 动画执行次数:', result.status.animationCount);
  405. console.log(' 面板可见性:', result.status.isVisible);
  406. console.log(' 节点激活状态:', result.status.nodeStatus.active);
  407. console.log(' 节点透明度:', result.status.nodeStatus.opacity);
  408. console.log(' 节点缩放:', result.status.nodeStatus.scale);
  409. console.log(' 节点层级:', result.status.nodeStatus.siblingIndex);
  410. if (result.interference) {
  411. console.log(' UI干扰情况:', result.interference);
  412. }
  413. if (result.layerInfo) {
  414. console.log(' 层级信息:', result.layerInfo);
  415. }
  416. });
  417. const allPassed = results.every(r => r.success);
  418. console.log('\n=== 调试结论 ===');
  419. if (allPassed) {
  420. console.log('🎉 所有测试通过!GameEnd节点状态正常。');
  421. console.log('💡 如果实际游戏中仍有问题,可能是以下原因:');
  422. console.log(' 1. 实际场景中的UI层级配置不同');
  423. console.log(' 2. 其他组件在运行时修改了GameEnd节点状态');
  424. console.log(' 3. 动画系统或渲染系统的问题');
  425. console.log(' 4. GameEnd组件的某些属性在编辑器中配置不正确');
  426. } else {
  427. console.log('⚠️ 发现潜在问题:');
  428. const failedTests = results.filter(r => !r.success);
  429. failedTests.forEach(test => {
  430. console.log(` - ${test.scenario}: 可能的问题原因`);
  431. if (test.scenario.includes('interference')) {
  432. console.log(' → UI面板层级冲突,GameEnd被其他面板遮挡');
  433. }
  434. if (test.scenario.includes('layer_order')) {
  435. console.log(' → GameEnd的siblingIndex被意外调整到较低层级');
  436. }
  437. if (test.scenario.includes('reset')) {
  438. console.log(' → UI重置事件在不当时机触发,影响GameEnd显示');
  439. }
  440. });
  441. }
  442. return {
  443. allPassed,
  444. results,
  445. summary: {
  446. total: results.length,
  447. passed: results.filter(r => r.success).length,
  448. failed: results.filter(r => !r.success).length
  449. }
  450. };
  451. }
  452. // 运行测试
  453. const testResults = runAllTests();
  454. console.log('\n📊 调试测试统计:');
  455. console.log('总测试数:', testResults.summary.total);
  456. console.log('通过数:', testResults.summary.passed);
  457. console.log('失败数:', testResults.summary.failed);
  458. console.log('通过率:', Math.round(testResults.summary.passed / testResults.summary.total * 100) + '%');
  459. if (testResults.allPassed) {
  460. console.log('\n✨ 调试结论:GameEnd节点逻辑正常,问题可能在实际游戏环境的配置或其他组件干扰!');
  461. console.log('\n🔧 建议检查项目:');
  462. console.log(' 1. 在Cocos Creator编辑器中检查GameEnd节点的层级位置');
  463. console.log(' 2. 确认GameEnd组件的animationDuration属性设置正确(>0)');
  464. console.log(' 3. 检查是否有其他脚本在运行时修改GameEnd节点状态');
  465. console.log(' 4. 确认GameEnd节点在场景中的初始active状态');
  466. console.log(' 5. 检查Canvas的渲染模式和UI摄像机设置');
  467. } else {
  468. console.log('\n🔍 需要进一步调试的问题:');
  469. testResults.results.forEach(result => {
  470. if (!result.success) {
  471. console.log(` - ${result.scenario}: 动画${result.status.animationCount}次,可见${result.status.isVisible}`);
  472. }
  473. });
  474. }