181404010226 před 1 měsícem
rodič
revize
af70ee394e
32 změnil soubory, kde provedl 3058 přidání a 440 odebrání
  1. 235 136
      assets/Scenes/GameLevel.scene
  2. 17 156
      assets/assets/Prefabs/PelletContainer.prefab
  3. binární
      assets/data/excel/弹弹守卫战_事件数据表.xlsx
  4. 12 0
      assets/data/excel/弹弹守卫战_事件数据表.xlsx.meta
  5. 1 1
      assets/data/weapons.json
  6. 10 1
      assets/scripts/Ads/AdManager.ts
  7. 32 6
      assets/scripts/CombatSystem/BlockManager.ts
  8. 119 52
      assets/scripts/CombatSystem/BlockSelection/GameBlockSelection.ts
  9. 225 70
      assets/scripts/CombatSystem/BulletEffects/BulletTrajectory.ts
  10. 110 1
      assets/scripts/CombatSystem/BulletTrailController.ts
  11. 39 0
      assets/scripts/CombatSystem/Wall.ts
  12. 22 5
      assets/scripts/CombatSystem/WeaponBullet.ts
  13. 45 0
      assets/scripts/FourUI/ShopSystem/ShopController.ts
  14. 99 0
      assets/scripts/FourUI/UpgradeSystem/UpgradeController.ts
  15. 183 0
      assets/scripts/LaunchScreen.ts
  16. 0 12
      assets/scripts/LevelSystem/GameManager.ts
  17. 28 0
      assets/scripts/LevelSystem/IN_game.ts
  18. 17 0
      assets/scripts/LevelSystem/SaveDataManager.ts
  19. 680 0
      assets/scripts/Utils/AnalyticsManager.ts
  20. 9 0
      assets/scripts/Utils/AnalyticsManager.ts.meta
  21. 270 0
      assets/scripts/Utils/AnalyticsTest.ts
  22. 9 0
      assets/scripts/Utils/AnalyticsTest.ts.meta
  23. 243 0
      assets/scripts/Utils/AnalyticsTestSimple.ts
  24. 9 0
      assets/scripts/Utils/AnalyticsTestSimple.ts.meta
  25. 92 0
      assets/scripts/Utils/BlockMergeTrackingGuide.md
  26. 11 0
      assets/scripts/Utils/BlockMergeTrackingGuide.md.meta
  27. 143 0
      assets/scripts/Utils/BlockMergeTrackingTest.ts
  28. 9 0
      assets/scripts/Utils/BlockMergeTrackingTest.ts.meta
  29. 212 0
      assets/scripts/Utils/MPLifecycleManager.ts
  30. 9 0
      assets/scripts/Utils/MPLifecycleManager.ts.meta
  31. 157 0
      assets/scripts/Utils/埋点测试说明.md
  32. 11 0
      assets/scripts/Utils/埋点测试说明.md.meta

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 235 - 136
assets/Scenes/GameLevel.scene


+ 17 - 156
assets/assets/Prefabs/PelletContainer.prefab

@@ -23,31 +23,28 @@
       },
       {
         "__id__": 8
-      },
-      {
-        "__id__": 14
       }
     ],
     "_active": true,
     "_components": [
       {
-        "__id__": 20
+        "__id__": 14
       },
       {
-        "__id__": 22
+        "__id__": 16
       },
       {
-        "__id__": 24
+        "__id__": 18
       },
       {
-        "__id__": 26
+        "__id__": 20
       },
       {
-        "__id__": 28
+        "__id__": 22
       }
     ],
     "_prefab": {
-      "__id__": 30
+      "__id__": 24
     },
     "_lpos": {
       "__type__": "cc.Vec3",
@@ -87,7 +84,7 @@
       "__id__": 1
     },
     "_children": [],
-    "_active": false,
+    "_active": true,
     "_components": [
       {
         "__id__": 3
@@ -208,7 +205,7 @@
   },
   {
     "__type__": "cc.Node",
-    "_name": "Spine",
+    "_name": "Pellet",
     "_objFlags": 0,
     "__editorExtras__": {},
     "_parent": {
@@ -227,142 +224,6 @@
     "_prefab": {
       "__id__": 13
     },
-    "_lpos": {
-      "__type__": "cc.Vec3",
-      "x": -92.176,
-      "y": -5.938,
-      "z": 0
-    },
-    "_lrot": {
-      "__type__": "cc.Quat",
-      "x": 0,
-      "y": 0,
-      "z": 0.7071067811865476,
-      "w": -0.7071067811865475
-    },
-    "_lscale": {
-      "__type__": "cc.Vec3",
-      "x": 1,
-      "y": 1,
-      "z": 1
-    },
-    "_mobility": 0,
-    "_layer": 1073741824,
-    "_euler": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": 0,
-      "z": 270
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.UITransform",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 8
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 10
-    },
-    "_contentSize": {
-      "__type__": "cc.Size",
-      "width": 72.06999969482422,
-      "height": 175.0399932861328
-    },
-    "_anchorPoint": {
-      "__type__": "cc.Vec2",
-      "x": 0.630914408397987,
-      "y": 0.5104547640610523
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "56KO0APYtKRrRSDUqPjZT4"
-  },
-  {
-    "__type__": "sp.Skeleton",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 8
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 12
-    },
-    "_customMaterial": null,
-    "_srcBlendFactor": 2,
-    "_dstBlendFactor": 4,
-    "_color": {
-      "__type__": "cc.Color",
-      "r": 255,
-      "g": 255,
-      "b": 255,
-      "a": 255
-    },
-    "_skeletonData": {
-      "__uuid__": "15209fa2-9c09-477e-a266-1bd7596cc915",
-      "__expectedType__": "sp.SkeletonData"
-    },
-    "defaultSkin": "default",
-    "defaultAnimation": "",
-    "_premultipliedAlpha": true,
-    "_timeScale": 1,
-    "_preCacheMode": 0,
-    "_cacheMode": 0,
-    "_sockets": [],
-    "_useTint": false,
-    "_debugMesh": false,
-    "_debugBones": false,
-    "_debugSlots": false,
-    "_enableBatch": false,
-    "loop": true,
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "2buPpbG2ZO06BJlQOG5/cL"
-  },
-  {
-    "__type__": "cc.PrefabInfo",
-    "root": {
-      "__id__": 1
-    },
-    "asset": {
-      "__id__": 0
-    },
-    "fileId": "e4cE95pldB5qMiM5U9zRCY",
-    "instance": null,
-    "targetOverrides": null,
-    "nestedPrefabInstanceRoots": null
-  },
-  {
-    "__type__": "cc.Node",
-    "_name": "Pellet",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "_parent": {
-      "__id__": 1
-    },
-    "_children": [],
-    "_active": true,
-    "_components": [
-      {
-        "__id__": 15
-      },
-      {
-        "__id__": 17
-      }
-    ],
-    "_prefab": {
-      "__id__": 19
-    },
     "_lpos": {
       "__type__": "cc.Vec3",
       "x": 0,
@@ -398,11 +259,11 @@
     "_objFlags": 0,
     "__editorExtras__": {},
     "node": {
-      "__id__": 14
+      "__id__": 8
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 16
+      "__id__": 10
     },
     "_contentSize": {
       "__type__": "cc.Size",
@@ -426,11 +287,11 @@
     "_objFlags": 0,
     "__editorExtras__": {},
     "node": {
-      "__id__": 14
+      "__id__": 8
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 18
+      "__id__": 12
     },
     "_customMaterial": null,
     "_srcBlendFactor": 2,
@@ -485,7 +346,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 21
+      "__id__": 15
     },
     "_contentSize": {
       "__type__": "cc.Size",
@@ -513,7 +374,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 23
+      "__id__": 17
     },
     "enabledContactListener": true,
     "bullet": true,
@@ -547,7 +408,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 25
+      "__id__": 19
     },
     "tag": 3,
     "_group": 8,
@@ -581,7 +442,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 27
+      "__id__": 21
     },
     "motionStreak": {
       "__id__": 5
@@ -602,7 +463,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 29
+      "__id__": 23
     },
     "tag": 6,
     "_group": 64,

binární
assets/data/excel/弹弹守卫战_事件数据表.xlsx


+ 12 - 0
assets/data/excel/弹弹守卫战_事件数据表.xlsx.meta

@@ -0,0 +1,12 @@
+{
+  "ver": "1.0.0",
+  "importer": "*",
+  "imported": true,
+  "uuid": "c762b6c8-afa8-4c03-ac25-b958b28b4e01",
+  "files": [
+    ".json",
+    ".xlsx"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

+ 1 - 1
assets/data/weapons.json

@@ -866,7 +866,7 @@
           "burstDelay": 2
         },
         "trajectory": {
-          "type": "arc",
+          "type": "guided",
           "speed": 200,
           "gravity": 0,
           "arcHeight": 0,

+ 10 - 1
assets/scripts/Ads/AdManager.ts

@@ -1,4 +1,5 @@
 import { _decorator, Component } from 'cc';
+import { AnalyticsManager } from '../Utils/AnalyticsManager';
 const { ccclass } = _decorator;
 
 interface RewardedVideoAd {
@@ -268,7 +269,15 @@ export class AdManager {
         this.videoAd.onClose(onCloseHandler);
         
         // 显示广告
-        this.videoAd.show().catch((showError) => {
+        this.videoAd.show().then(() => {
+            console.log('[AdManager] 广告显示成功');
+            // 追踪广告展示事件
+            AnalyticsManager.getInstance().trackAdShow({
+                ad_type: 'rewarded_video',
+                ad_unit_id: this.AD_UNIT_ID,
+                show_time: new Date().toISOString()
+            });
+        }).catch((showError) => {
             console.log('[AdManager] 广告显示失败,尝试重新加载');
             // 失败重试,增加延迟确保广告完全加载
             this.videoAd.load()

+ 32 - 6
assets/scripts/CombatSystem/BlockManager.ts

@@ -1,4 +1,4 @@
-import { _decorator, Component, Node, Prefab, instantiate, Vec3, EventTouch, Color, Vec2, UITransform, find, Rect, Label, Size, Sprite, SpriteFrame, resources, Button, Collider2D, Material, tween, JsonAsset } from 'cc';
+import { _decorator, Component, Node, Prefab, instantiate, Vec3, EventTouch, Color, Vec2, UITransform, find, Rect, Label, Size, Sprite, SpriteFrame, resources, Button, Collider2D, Material, tween, JsonAsset, Tween } from 'cc';
 import * as cc from 'cc';
 import { ConfigManager, WeaponConfig } from '../Core/ConfigManager';
 import { SaveDataManager } from '../LevelSystem/SaveDataManager';
@@ -12,6 +12,7 @@ import { sp } from 'cc';
 import { BundleLoader } from '../Core/BundleLoader';
 import { Audio } from '../AudioManager/AudioManager';
 import { GuideUIController } from '../../NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideUIController';
+import { Analytics } from '../Utils/AnalyticsManager';
 const { ccclass, property } = _decorator;
 
 @ccclass('BlockManager')
@@ -121,6 +122,8 @@ export class BlockManager extends Component {
     
     // 方块价格标签映射
     private blockPriceMap: Map<Node, Node> = new Map();
+    // 价格标签初始颜色缓存,防止多次闪烁后无法恢复
+    private priceLabelOriginalColors: Map<Node, Color> = new Map();
     
     // 已经生成的块
     private blocks: Node[] = [];
@@ -1193,14 +1196,23 @@ export class BlockManager extends Component {
             console.warn('[BlockManager] 价格节点缺少Label组件');
             return;
         }
-        
-        // 保存原始颜色
-        const originalColor = label.color.clone();
+
+        // 首次缓存原始颜色,后续使用缓存避免在红色中被覆盖
+        if (!this.priceLabelOriginalColors.has(priceNode)) {
+            this.priceLabelOriginalColors.set(priceNode, label.color.clone());
+        }
+        const cachedOriginal = this.priceLabelOriginalColors.get(priceNode)!;
+        const originalColor = cachedOriginal.clone();
         
         // 设置红色
         const redColor = new Color(255, 0, 0, 255);
-        
-        // 创建闪烁动画:变红 -> 恢复原色,重复3次,总时长2秒
+
+        // 停止已有动画,避免堆叠导致颜色停留在红色
+        Tween.stopAllByTarget(label);
+        // 统一从原始颜色开始,避免中途状态影响效果
+        label.color = originalColor.clone();
+
+        // 创建闪烁动画:变红 -> 恢复原色,重复3次,最后确保恢复原色
         tween(label)
             .to(0.2, { color: redColor })
             .to(0.2, { color: originalColor })
@@ -2958,6 +2970,20 @@ export class BlockManager extends Component {
                 // 播放合成成功音效
                 Audio.playSFX('data/弹球音效/level up 2');
                 
+                // 追踪合成等级达成事件(仅追踪2-4级)
+                const rarityMap = { 'uncommon': 2, 'rare': 3, 'epic': 4 };
+                const mergeLevel = rarityMap[nextRarity];
+                if (mergeLevel) {
+                    // 获取武器和形状信息用于埋点
+                    const weaponConfig = this.blockWeaponConfigs.get(target);
+                    const weaponType = weaponConfig ? weaponConfig.name : 'unknown';
+                    const shapeType = this.getBlockShape(target) || 'unknown';
+                    
+                    // 调用埋点方法
+                    Analytics.trackBlockMergeLevel(mergeLevel, weaponType, shapeType);
+                    console.log(`[BlockManager] 埋点追踪: 合成等级${mergeLevel}, 武器=${weaponType}, 形状=${shapeType}`);
+                }
+                
                 // 获取当前武器配置
                 const currentConfig = this.blockWeaponConfigs.get(target);
                 if (currentConfig) {

+ 119 - 52
assets/scripts/CombatSystem/BlockSelection/GameBlockSelection.ts

@@ -9,6 +9,7 @@ import { SkillManager } from '../SkillSelection/SkillManager';
 import { Audio } from '../../AudioManager/AudioManager';
 import { AdManager } from '../../Ads/AdManager';
 import { JsonConfigLoader } from '../../Core/JsonConfigLoader';
+import { AnalyticsManager, BlockSelectionClickProperties } from '../../Utils/AnalyticsManager';
 
 import EventBus, { GameEvents } from '../../Core/EventBus';
 const { ccclass, property } = _decorator;
@@ -460,30 +461,50 @@ export class GameBlockSelection extends Component {
     private onAddBallClicked() {
         // 播放UI点击音效
         Audio.playUISound('data/弹球音效/ui play');
+        
         // 从UI节点获取价格并应用便宜技能效果计算实际费用
         const baseCost = this.getAddBallCost();
         const actualCost = this.getActualCost(baseCost);
         
-        if (!this.canSpendCoins(actualCost)) {
+        // 检查是否有足够金币
+        const canAfford = this.canSpendCoins(actualCost);
+        let success = false;
+        let failureReason: string | undefined;
+        
+        if (!canAfford) {
+            failureReason = 'insufficient_coins';
             this.showInsufficientCoinsUI();
-            return;
-        }
-
-        // 扣除金币
-        if (this.session.spendCoins(actualCost)) {
-            // 增加使用次数
-            this.session.incrementAddBallUsageCount();
-            
-            this.updateCoinDisplay();
-            
-            // 更新价格显示
-            this.updatePriceDisplay();
-            
-            // 通过事件系统创建新的小球
-            const eventBus = EventBus.getInstance();
-            eventBus.emit(GameEvents.BALL_CREATE_ADDITIONAL);
-            // 新增小球成功
+        } else {
+            // 扣除金币
+            if (this.session.spendCoins(actualCost)) {
+                success = true;
+                // 增加使用次数
+                this.session.incrementAddBallUsageCount();
+                
+                this.updateCoinDisplay();
+                
+                // 更新价格显示
+                this.updatePriceDisplay();
+                
+                // 通过事件系统创建新的小球
+                const eventBus = EventBus.getInstance();
+                eventBus.emit(GameEvents.BALL_CREATE_ADDITIONAL);
+                // 新增小球成功
+            } else {
+                failureReason = 'spend_coins_failed';
+            }
         }
+        
+        // 数据分析追踪
+        const properties: BlockSelectionClickProperties = {
+            button_type: 'add_ball',
+            button_cost: actualCost,
+            user_money: this.session.getCoins(),
+            usage_count: this.session.getAddBallUsageCount(),
+            success: success,
+            failure_reason: failureReason
+        };
+        AnalyticsManager.getInstance().trackBlockSelectionClick(properties);
     }
 
     // 增加金币按钮点击
@@ -491,6 +512,9 @@ export class GameBlockSelection extends Component {
         // 播放UI点击音效
         Audio.playUISound('data/弹球音效/ui play');
         
+        let success = false;
+        let failureReason: string | undefined;
+        
         // 显示激励视频广告
         AdManager.getInstance().showRewardedVideoAd(
             () => {
@@ -504,10 +528,32 @@ export class GameBlockSelection extends Component {
                 // 更新显示
                 this.updateCoinDisplay();
                 this.updateRewardDisplay();
+                
+                success = true;
+                
+                // 数据分析追踪 - 成功情况
+                const properties: BlockSelectionClickProperties = {
+                    button_type: 'add_coin',
+                    user_money: this.session.getCoins(),
+                    usage_count: this.session.getAddCoinUsageCount(),
+                    success: success
+                };
+                AnalyticsManager.getInstance().trackBlockSelectionClick(properties);
             },
             (error) => {
                 console.error('[GameBlockSelection] 广告显示失败:', error);
                 // 广告失败时不给予奖励
+                failureReason = 'ad_failed';
+                
+                // 数据分析追踪 - 失败情况
+                const properties: BlockSelectionClickProperties = {
+                    button_type: 'add_coin',
+                    user_money: this.session.getCoins(),
+                    usage_count: this.session.getAddCoinUsageCount(),
+                    success: false,
+                    failure_reason: failureReason
+                };
+                AnalyticsManager.getInstance().trackBlockSelectionClick(properties);
             }
         );
     }
@@ -516,51 +562,72 @@ export class GameBlockSelection extends Component {
     private onRefreshClicked() {
         // 播放UI点击音效
         Audio.playUISound('data/弹球音效/ui play');
+        
         // 从UI节点获取价格并应用便宜技能效果计算实际费用
         const baseCost = this.getRefreshCost();
         const actualCost = this.getActualCost(baseCost);
         
-        if (!this.canSpendCoins(actualCost)) {
+        // 检查是否有足够金币
+        const canAfford = this.canSpendCoins(actualCost);
+        let success = false;
+        let failureReason: string | undefined;
+        
+        if (!canAfford) {
+            failureReason = 'insufficient_coins';
             this.showInsufficientCoinsUI();
-            return;
-        }
-
-        // 扣除金币
-        if (this.session.spendCoins(actualCost)) {
-            // 增加使用次数
-            this.session.incrementRefreshUsageCount();
-            
-            // 成功扣除金币
-            this.updateCoinDisplay();
-            
-            // 更新价格显示
-            this.updatePriceDisplay();
-            
-            // 刷新方块
-            if (this.blockManager) {
-                console.log('[GameBlockSelection] 开始刷新方块流程');
+        } else {
+            // 扣除金币
+            if (this.session.spendCoins(actualCost)) {
+                success = true;
+                // 增加使用次数
+                this.session.incrementRefreshUsageCount();
                 
-                // 找到PlacedBlocks容器
-                const placedBlocksContainer = find('Canvas/GameLevelUI/GameArea/PlacedBlocks');
-                if (placedBlocksContainer) {
-                    // 移除已放置方块的标签
-                    // 移除已放置方块的标签
-                    BlockTag.removeTagsInContainer(placedBlocksContainer);
-                }
+                // 成功扣除金币
+                this.updateCoinDisplay();
                 
-                // 刷新方块
-                // 调用 blockManager.refreshBlocks()
-                this.blockManager.refreshBlocks();
+                // 更新价格显示
+                this.updatePriceDisplay();
                 
-                // 等待一帧确保方块生成完成
-                this.scheduleOnce(() => {
-                }, 0.1);
+                // 刷新方块
+                if (this.blockManager) {
+                    console.log('[GameBlockSelection] 开始刷新方块流程');
+                    
+                    // 找到PlacedBlocks容器
+                    const placedBlocksContainer = find('Canvas/GameLevelUI/GameArea/PlacedBlocks');
+                    if (placedBlocksContainer) {
+                        // 移除已放置方块的标签
+                        // 移除已放置方块的标签
+                        BlockTag.removeTagsInContainer(placedBlocksContainer);
+                    }
+                    
+                    // 刷新方块
+                    // 调用 blockManager.refreshBlocks()
+                    this.blockManager.refreshBlocks();
+                    
+                    // 等待一帧确保方块生成完成
+                    this.scheduleOnce(() => {
+                    }, 0.1);
+                } else {
+                    console.error('[GameBlockSelection] 找不到BlockManager,无法刷新方块');
+                    success = false;
+                    failureReason = 'block_manager_not_found';
+                }
             } else {
-                console.error('[GameBlockSelection] 找不到BlockManager,无法刷新方块');
+                console.error('[GameBlockSelection] 扣除金币失败');
+                failureReason = 'spend_coins_failed';
             }
-        } else {
-            console.error('[GameBlockSelection] 扣除金币失败');
         }
+        
+        // 数据分析追踪
+        const properties: BlockSelectionClickProperties = {
+            button_type: 'refresh_blocks',
+            button_cost: actualCost,
+            user_money: this.session.getCoins(),
+            usage_count: this.session.getRefreshUsageCount(),
+            success: success,
+            failure_reason: failureReason
+        };
+        AnalyticsManager.getInstance().trackBlockSelectionClick(properties);
     }
 
     // 确认按钮点击

+ 225 - 70
assets/scripts/CombatSystem/BulletEffects/BulletTrajectory.ts

@@ -1,4 +1,5 @@
 import { _decorator, Component, Node, Vec2, Vec3, RigidBody2D, find } from 'cc';
+import { EnemyAttackStateManager } from '../EnemyAttackStateManager';
 import { BulletTrajectoryConfig } from '../../Core/ConfigManager';
 const { ccclass, property } = _decorator;
 
@@ -34,6 +35,10 @@ export class BulletTrajectory extends Component {
     // === Arc Trajectory ===
     private arcDir: Vec3 = null;          // 当前方向
     private arcTargetDir: Vec3 = null;    // 目标方向(最终方向)
+
+    // === Guided Trajectory (更强自动导航,可拐弯并切换目标) ===
+    private guidedDir: Vec3 = null;          // 当前引导方向
+    private guidedTargetDir: Vec3 = null;    // 目标方向(平滑后的)
     
     /**
      * 初始化弹道
@@ -71,6 +76,7 @@ export class BulletTrajectory extends Component {
         if (config.type === 'homing') {
             this.findTarget();
         }
+
     }
     
     /**
@@ -85,13 +91,6 @@ export class BulletTrajectory extends Component {
                 );
                 break;
                 
-            case 'parabolic':
-                // 计算抛物线初始速度
-                const velocity = this.calculateParabolicVelocity();
-                this.rigidBody.linearVelocity = velocity;
-                this.state.currentVelocity.set(velocity.x, velocity.y, 0);
-                break;
-                
             case 'homing':
                 this.rigidBody.linearVelocity = new Vec2(
                     this.state.initialVelocity.x,
@@ -99,16 +98,40 @@ export class BulletTrajectory extends Component {
                 );
                 break;
                 
-            case 'arc':
-                // 计算带 45° 随机偏移的初始方向
+            case 'guided': {
+                // 参考 arc:初始方向加入随机偏移,但拐弯能力更强
                 const baseDir = this.state.initialVelocity.clone().normalize();
                 const sign = Math.random() < 0.5 ? 1 : -1;
-                const rad = 45 * Math.PI / 180 * sign;
-                const cos = Math.cos(rad);
-                const sin = Math.sin(rad);
+                const rad = 35 * Math.PI / 180 * sign; // 比 arc 略小的初始偏移,便于后续强导航
+                const c = Math.cos(rad);
+                const s = Math.sin(rad);
                 const offsetDir = new Vec3(
-                    baseDir.x * cos - baseDir.y * sin,
-                    baseDir.x * sin + baseDir.y * cos,
+                    baseDir.x * c - baseDir.y * s,
+                    baseDir.x * s + baseDir.y * c,
+                    0
+                ).normalize();
+
+                this.guidedDir = offsetDir.clone();
+                this.guidedTargetDir = baseDir.clone();
+
+                // 设置初速度
+                if (this.rigidBody) {
+                    this.rigidBody.linearVelocity = new Vec2(offsetDir.x * this.config.speed, offsetDir.y * this.config.speed);
+                }
+                this.state.currentVelocity.set(offsetDir.x * this.config.speed, offsetDir.y * this.config.speed, 0);
+                break;
+            }
+
+            case 'arc': {
+                // 计算带 45° 随机偏移的初始方向
+                const baseDir = this.state.initialVelocity.clone().normalize();
+                const signArc = Math.random() < 0.5 ? 1 : -1;
+                const radArc = 45 * Math.PI / 180 * signArc;
+                const cosArc = Math.cos(radArc);
+                const sinArc = Math.sin(radArc);
+                const offsetDir = new Vec3(
+                    baseDir.x * cosArc - baseDir.y * sinArc,
+                    baseDir.x * sinArc + baseDir.y * cosArc,
                     0
                 ).normalize();
 
@@ -122,47 +145,10 @@ export class BulletTrajectory extends Component {
                 // 保存当前速度
                 this.state.currentVelocity.set(offsetDir.x * this.config.speed, offsetDir.y * this.config.speed, 0);
                 break;
+            }
         }
     }
     
-    /**
-     * 计算抛物线初始速度
-     */
-    private calculateParabolicVelocity(): Vec2 {
-        // NOTE:
-        // 1. 当目标位于子弹正上/下方时, 原 direction 的 x 分量可能非常小甚至为 0, 导致 vx≈0, 子弹会几乎垂直运动, 看起来像"钢球"直落。
-        // 2. 对于抛物线弹道, 我们总是希望子弹具备一个最小的水平速度, 并且保证初速度向上( vy>0 ),
-        //    这样才能形成明显的抛物线效果并最终击中目标。
-
-        const rawDir = this.state.initialVelocity.clone().normalize();
-
-        // 单独提取水平方向, 忽略原始 y 分量, 以保证抛物线始终向前飞行
-        const horizontalDir = new Vec3(rawDir.x, 0, 0);
-
-        // 若水平方向过小, 取发射者面朝方向 (rawDir.x 的符号) 作为水平单位向量
-        if (Math.abs(horizontalDir.x) < 0.01) {
-            horizontalDir.x = rawDir.x >= 0 ? 1 : -1;
-        }
-        horizontalDir.normalize();
-
-        const speed = this.config.speed;
-
-        // 基础水平速度 (保持恒定, 不受重力影响)
-        const vx = horizontalDir.x * speed;
-
-        // 计算纵向初速度, 使得理论最高点接近 arcHeight
-        // vy = sqrt(2 * g * h)
-        const g = Math.max(0.1, Math.abs(this.config.gravity * 9.8)); // 避免 g=0 导致除0或 vy=0
-        const desiredHeight = Math.max(1, 20); // 使用固定高度20作为默认抛物线高度
-        let vy = Math.sqrt(2 * g * desiredHeight);
-
-        // 如果原始方向有明显的向上分量, 为了兼容旧配置, 适当叠加
-        if (rawDir.y > 0.1) {
-            vy += rawDir.y * speed * 0.5; // 50% 叠加, 防止过高
-        }
-
-        return new Vec2(vx, vy);
-    }
     
     /**
      * 寻找追踪目标
@@ -211,6 +197,23 @@ export class BulletTrajectory extends Component {
                name.includes('敌人') ||
                node.getComponent('EnemyInstance') !== null;
     }
+
+    /**
+     * 检查场上是否仍有“真实存在”的敌人
+     * 不受隐身影响:隐身敌人仍算存在;只有全部死亡/销毁才返回false
+     */
+    private hasAliveEnemies(): boolean {
+        // 优先使用攻击状态管理器的敌人总数(包含隐身敌人)
+        const mgr = EnemyAttackStateManager.getInstance();
+        if (mgr && typeof mgr.getEnemyCount === 'function') {
+            if (mgr.getEnemyCount() > 0) return true;
+        }
+        // 回退:扫描场景节点
+        const enemyContainer = find('Canvas/GameLevelUI/enemyContainer');
+        if (!enemyContainer) return false;
+        const enemies = enemyContainer.children.filter(child => child.isValid && child.active && this.isEnemyNode(child));
+        return enemies.length > 0;
+    }
     
     update(dt: number) {
         if (!this.config || !this.state) return;
@@ -224,12 +227,12 @@ export class BulletTrajectory extends Component {
             case 'straight':
                 this.updateStraightTrajectory(dt);
                 break;
-            case 'parabolic':
-                this.updateParabolicTrajectory(dt);
-                break;
             case 'homing':
                 this.updateHomingTrajectory(dt);
                 break;
+            case 'guided':
+                this.updateGuidedTrajectory(dt);
+                break;
             case 'arc':
                 this.updateArcTrajectory(dt);
                 break;
@@ -252,21 +255,6 @@ export class BulletTrajectory extends Component {
         }
     }
     
-    /**
-     * 更新抛物线弹道
-     */
-    private updateParabolicTrajectory(dt: number) {
-        // 应用重力影响
-        const currentVel = this.rigidBody.linearVelocity;
-        const gravityForce = this.config.gravity * 9.8 * dt;
-        
-        this.rigidBody.linearVelocity = new Vec2(
-            currentVel.x,
-            currentVel.y - gravityForce
-        );
-        
-        this.state.currentVelocity.set(currentVel.x, currentVel.y - gravityForce, 0);
-    }
     
     /**
      * 更新追踪弹道
@@ -327,6 +315,20 @@ export class BulletTrajectory extends Component {
         // 若当前没有有效目标,则尝试重新寻找
         if (!this.targetNode || !this.targetNode.isValid) {
             this.findTarget();
+            // 若仍无目标且场上没有任何存活敌人,则立刻结束生命周期
+            if ((!this.targetNode || !this.targetNode.isValid) && !this.hasAliveEnemies()) {
+                const lifecycle = this.getComponent('BulletLifecycle') as any;
+                if (this.rigidBody) {
+                    this.rigidBody.linearVelocity = new Vec2(0, 0);
+                }
+                if (lifecycle && typeof lifecycle.forceDestroy === 'function') {
+                    lifecycle.forceDestroy();
+                } else {
+                    // 兜底:销毁节点
+                    if (this.node && this.node.isValid) this.node.destroy();
+                }
+                return;
+            }
         }
 
         // 检查是否到达目标位置(爆炸武器和回旋镖都适用,但处理方式不同)
@@ -478,6 +480,159 @@ export class BulletTrajectory extends Component {
         // 更新状态中的当前速度
         this.state.currentVelocity.set(this.arcDir.x * this.config.speed, this.arcDir.y * this.config.speed, 0);
     }
+
+    /**
+     * 更新 guided 轨迹:强导航、顺滑拐弯与目标切换
+     */
+    private updateGuidedTrajectory(dt: number) {
+        if (!this.guidedDir || !this.guidedTargetDir) return;
+
+        // 动态追踪与目标切换:优先当前锁定;失效或消失则寻找最近目标
+        if (!this.targetNode || !this.targetNode.isValid) {
+            this.findTarget();
+            // 若仍无目标且场上没有任何存活敌人,则立刻结束生命周期
+            if ((!this.targetNode || !this.targetNode.isValid) && !this.hasAliveEnemies()) {
+                const lifecycle = this.getComponent('BulletLifecycle') as any;
+                if (this.rigidBody) {
+                    this.rigidBody.linearVelocity = new Vec2(0, 0);
+                }
+                if (lifecycle && typeof lifecycle.forceDestroy === 'function') {
+                    lifecycle.forceDestroy();
+                } else {
+                    if (this.node && this.node.isValid) this.node.destroy();
+                }
+                return;
+            }
+        }
+
+        const currentPos = this.node.worldPosition;
+        let targetPos: Vec3 | null = null;
+        if (this.targetNode && this.targetNode.isValid) {
+            targetPos = this.targetNode.worldPosition;
+        } else if (this.state.targetPosition) {
+            targetPos = this.state.targetPosition;
+        }
+        // 若仍无目标,保持原方向匀速飞行
+        if (!targetPos) {
+            if (this.rigidBody) {
+                this.rigidBody.linearVelocity = new Vec2(this.guidedDir.x * this.config.speed, this.guidedDir.y * this.config.speed);
+            }
+            return;
+        }
+
+        // 期望方向(纯追踪)
+        const dirToTarget = targetPos.clone().subtract(currentPos).normalize();
+        const dist = Vec3.distance(currentPos, targetPos);
+
+        // 动态平滑:远距离更平滑,近距离响应更快
+        const t = this.state.elapsedTime;
+        let smooth: number;
+        if (dist > 350) {
+            smooth = t < 0.6 ? 0.15 : 0.18; // 前期更柔和
+        } else if (dist > 180) {
+            smooth = 0.24; // 中段略增强响应
+        } else if (dist > 90) {
+            smooth = 0.36; // 接近时明显加快
+        } else {
+            smooth = 0.52; // 临近目标快速贴合
+        }
+        const blended = new Vec3(
+            this.guidedTargetDir.x + (dirToTarget.x - this.guidedTargetDir.x) * smooth,
+            this.guidedTargetDir.y + (dirToTarget.y - this.guidedTargetDir.y) * smooth,
+            0
+        ).normalize();
+        this.guidedTargetDir.set(blended);
+
+        // 根据角误差与距离动态提升转向速率(构造“导弹投掷”前平滑、末段急拐”手感)
+        const dot = Math.max(-1, Math.min(1, this.guidedDir.x * this.guidedTargetDir.x + this.guidedDir.y * this.guidedTargetDir.y));
+        const angleErr = Math.acos(dot); // [0, π]
+
+        const baseRotate = (this.config.rotateSpeed ?? 0.8) * dt;
+        let turnScale: number;
+        if (dist > 350) {
+            turnScale = 0.35; // 远处很平滑
+        } else if (dist > 180) {
+            turnScale = 0.6;  // 中段加速但仍平滑
+        } else if (dist > 90) {
+            turnScale = 0.9;  // 接近目标时显著加快
+        } else {
+            turnScale = 1.5;  // 末段急拐,形成导弹俯冲感觉
+        }
+        const angleScale = 0.8 + (angleErr / Math.PI) * 1.4;
+        // 末段大角误差额外加成,增强“急转”质感
+        const lateTurnBonus = (dist < 70 && angleErr > (50 * Math.PI / 180)) ? 1.6 : 1.0;
+        let rotateFactor = baseRotate * turnScale * angleScale * lateTurnBonus;
+
+        // 限制范围,确保数值稳定
+        rotateFactor = Math.min(0.95, Math.max(0.015, rotateFactor));
+
+        // 使用带符号角度的旋转以避免过度插值导致的打圈
+        // 计算带符号角误差 [-π, π]
+        const dotClamped = Math.max(-1, Math.min(1, this.guidedDir.x * this.guidedTargetDir.x + this.guidedDir.y * this.guidedTargetDir.y));
+        const crossZ = this.guidedDir.x * this.guidedTargetDir.y - this.guidedDir.y * this.guidedTargetDir.x;
+        const signedAngle = Math.atan2(crossZ, dotClamped);
+
+        // 以 rotateFactor 作为“最大步进”,限制每帧转向幅度,避免来回过冲产生自转
+        const maxTurn = Math.min(0.95, Math.max(0.01, rotateFactor)) * Math.PI; // 将插值因子映射到弧度步进
+        const clampedTurn = Math.max(-maxTurn, Math.min(maxTurn, signedAngle));
+
+        // 近距稳定锁定:很近且角误差很小,直接对准避免绕圈
+        if (dist < 50 && Math.abs(signedAngle) < (12 * Math.PI / 180)) {
+            this.guidedDir.set(this.guidedTargetDir);
+        } else {
+            const c = Math.cos(clampedTurn);
+            const s = Math.sin(clampedTurn);
+            const rotated = new Vec3(
+                this.guidedDir.x * c - this.guidedDir.y * s,
+                this.guidedDir.x * s + this.guidedDir.y * c,
+                0
+            ).normalize();
+            this.guidedDir.set(rotated);
+        }
+
+        // 更新速度
+        if (this.rigidBody) {
+            this.rigidBody.linearVelocity = new Vec2(this.guidedDir.x * this.config.speed, this.guidedDir.y * this.config.speed);
+        } else {
+            const displacement = this.guidedDir.clone().multiplyScalar(this.config.speed * dt);
+            this.node.worldPosition = this.node.worldPosition.add(displacement);
+        }
+        this.state.currentVelocity.set(this.guidedDir.x * this.config.speed, this.guidedDir.y * this.config.speed, 0);
+
+        // 近距命中(对齐爆炸型的处理)
+        const lifecycle = this.getComponent('BulletLifecycle') as any;
+        const hitEffect = this.getComponent('BulletHitEffect') as any;
+        const isExplosion = hitEffect && typeof (hitEffect as any).hasExplosionEffect === 'function'
+            ? (hitEffect as any).hasExplosionEffect()
+            : false;
+
+        if (this.targetNode && this.targetNode.isValid) {
+            const hitRadius = 45;
+            const d = Vec3.distance(currentPos, this.targetNode.worldPosition);
+            if (d <= hitRadius) {
+                if (this.rigidBody) this.rigidBody.linearVelocity = new Vec2(0, 0);
+                if (isExplosion) {
+                    if (hitEffect && typeof hitEffect.processHit === 'function') {
+                        hitEffect.processHit(this.node, currentPos);
+                    }
+                    if (lifecycle && typeof lifecycle.onHit === 'function') {
+                        lifecycle.onHit(this.node);
+                    }
+                } else {
+                    if (hitEffect && typeof hitEffect.processHit === 'function') {
+                        hitEffect.processHit(this.targetNode, currentPos);
+                    }
+                    if (lifecycle && typeof lifecycle.onHit === 'function') {
+                        lifecycle.onHit(this.targetNode);
+                    }
+                }
+                return;
+            }
+        } else {
+            // 目标消失:立刻尝试找新目标并继续引导
+            this.findTarget();
+        }
+    }
     
     /**
      * 获取当前速度

+ 110 - 1
assets/scripts/CombatSystem/BulletTrailController.ts

@@ -13,6 +13,7 @@ export class BulletTrailController extends Component {
         tooltip: '拖尾组件引用'
     })
     public motionStreak: MotionStreak | null = null;
+    private secondaryStreak: MotionStreak | null = null;
     
     // 拖尾配置
     private trailConfig = {
@@ -27,6 +28,7 @@ export class BulletTrailController extends Component {
         default: new Color(255, 255, 0, 255),    // 黄色
         white: new Color(255, 255, 255, 255),    // 白色
         fire: new Color(255, 100, 0, 255),       // 橙红色
+        gold: new Color(255, 200, 80, 255),      // 金黄(更接近外黄效果)
         ice: new Color(0, 200, 255, 255),        // 冰蓝色
         poison: new Color(100, 255, 0, 255),     // 毒绿色
         electric: new Color(200, 0, 255, 255)    // 紫色
@@ -65,7 +67,7 @@ export class BulletTrailController extends Component {
         this.motionStreak.minSeg = this.trailConfig.minSeg;
         this.motionStreak.stroke = this.trailConfig.stroke;
         this.motionStreak.fastMode = this.trailConfig.fastMode;
-        this.motionStreak.color = this.trailColors.white;
+        // 保持当前颜色,不在通用配置中强制重置为白色,避免覆盖武器专属颜色
     }
     
     /**
@@ -112,6 +114,16 @@ export class BulletTrailController extends Component {
         this.trailConfig.fadeTime = fadeTime;
         this.motionStreak.fadeTime = fadeTime;
     }
+
+    /**
+     * 设置拖尾最小分段(越小越平滑,减少三角形感)
+     * @param minSeg 最小分段
+     */
+    public setTrailMinSeg(minSeg: number) {
+        if (!this.motionStreak) return;
+        this.trailConfig.minSeg = minSeg;
+        this.motionStreak.minSeg = minSeg;
+    }
     
     /**
      * 设置拖尾纹理
@@ -121,6 +133,42 @@ export class BulletTrailController extends Component {
         if (!this.motionStreak) return;
         
         this.motionStreak.texture = texture;
+        if (this.secondaryStreak) {
+            this.secondaryStreak.texture = texture;
+        }
+    }
+
+    /**
+     * 设置快速模式(拖尾响应更灵敏)
+     */
+    public setFastMode(enabled: boolean) {
+        if (!this.motionStreak) return;
+        this.trailConfig.fastMode = enabled;
+        this.motionStreak.fastMode = enabled;
+    }
+
+    /**
+     * 预设:火焰喷射拖尾(用于秋葵导弹)
+     */
+    public applyPresetRocket() {
+        if (!this.motionStreak) return;
+        // 颜色:火焰橙红
+        this.setTrailColor('fire');
+        // 更粗的拖尾,形成喷焰柱效果
+        this.setTrailWidth(36);
+        // 更短的消失时间,拖尾更紧凑
+        this.setTrailFadeTime(0.14);
+        // 更小的分段让曲线更贴合,避免顶部太宽的三角形感
+        this.setTrailMinSeg(0.05);
+        // 启用快速模式,响应更快
+        this.setFastMode(true);
+
+        // 在下一帧再次强制颜色,防止其他初始化流程覆盖颜色(例如材质或默认值)
+        this.scheduleOnce(() => {
+            if (this.motionStreak) {
+                this.motionStreak.color = this.trailColors.fire;
+            }
+        }, 0);
     }
     
     /**
@@ -148,10 +196,71 @@ export class BulletTrailController extends Component {
         if (this.motionStreak) {
             this.motionStreak.reset();
         }
+        if (this.secondaryStreak) {
+            this.secondaryStreak.reset();
+        }
     }
 
     onDestroy() {
         // 清理资源
         this.motionStreak = null;
+        this.secondaryStreak = null;
+    }
+
+    /** 创建或获取次级拖尾(用于双层效果) */
+    private getOrCreateSecondaryStreak(): MotionStreak | null {
+        if (!this.motionStreak) return null;
+        if (this.secondaryStreak && this.secondaryStreak.isValid) return this.secondaryStreak;
+        try {
+            const hostNode = this.motionStreak.node;
+            // 为次级拖尾创建一个子节点,避免与主拖尾同节点可能产生的覆盖/冲突
+            const child = new Node('TrailEffectSecondary');
+            hostNode.addChild(child);
+            this.secondaryStreak = child.addComponent(MotionStreak);
+            // 复制基础纹理与参数
+            this.secondaryStreak.texture = this.motionStreak.texture;
+            this.secondaryStreak.minSeg = this.trailConfig.minSeg;
+            this.secondaryStreak.fadeTime = this.trailConfig.fadeTime;
+            this.secondaryStreak.stroke = this.trailConfig.stroke;
+            this.secondaryStreak.fastMode = this.trailConfig.fastMode;
+            return this.secondaryStreak;
+        } catch (e) {
+            console.warn('[BulletTrailController] 创建次级拖尾失败:', e);
+            return null;
+        }
+    }
+
+    /**
+     * 预设:双层喷焰(边缘橙红,中心白色)
+     */
+    public applyPresetRocketDualLayer() {
+        if (!this.motionStreak) return;
+        // 主拖尾:中心白色,稍窄
+        this.setTrailWidth(24);
+        this.setTrailFadeTime(0.5);
+        this.setTrailMinSeg(1);
+        this.setFastMode(false);
+        this.setTrailColor('white');
+
+        // 次级拖尾:边缘橙红,更宽
+        const secondary = this.getOrCreateSecondaryStreak();
+        if (secondary) {
+            secondary.stroke = 30;
+            secondary.fadeTime = 0.5;
+            secondary.minSeg = 1;
+            secondary.fastMode = false;
+            // 使用金黄色增强外层可见性
+            secondary.color = this.trailColors.gold;
+        }
+
+        // 保护色:下一帧再次写入
+        this.scheduleOnce(() => {
+            if (this.motionStreak && this.motionStreak.isValid) {
+                this.motionStreak.color = this.trailColors.white;
+            }
+            if (this.secondaryStreak && this.secondaryStreak.isValid) {
+                this.secondaryStreak.color = this.trailColors.gold;
+            }
+        }, 0);
     }
 }

+ 39 - 0
assets/scripts/CombatSystem/Wall.ts

@@ -2,6 +2,7 @@ import { _decorator, Component, Node, Label, find, JsonAsset, Collider2D, RigidB
 import { SaveDataManager } from '../LevelSystem/SaveDataManager';
 import EventBus, { GameEvents } from '../Core/EventBus';
 import { JsonConfigLoader } from '../Core/JsonConfigLoader';
+import { AnalyticsManager } from '../Utils/AnalyticsManager';
 
 import { SkillManager } from './SkillSelection/SkillManager';
 const { ccclass, property } = _decorator;
@@ -219,6 +220,9 @@ export class Wall extends Component {
     private onWallDestroyed() {
         console.log('[Wall] 墙体被摧毁,触发游戏失败');
         
+        // 埋点:上报 $GameFailure 事件
+        this.trackGameFailureEvent();
+        
         // 通过事件系统触发墙体被摧毁事件(保留用于其他监听器)
         const eventBus = EventBus.getInstance();
         eventBus.emit(GameEvents.WALL_DESTROYED, {
@@ -510,6 +514,41 @@ export class Wall extends Component {
         }
     }
     
+    /**
+     * 上报游戏失败埋点事件
+     */
+    private trackGameFailureEvent() {
+        try {
+            // 获取游戏时长(从 IN_game 管理器获取)
+            const inGameNode = find('IN_game');
+            let gameDuration = 0;
+            if (inGameNode) {
+                const inGameManager = inGameNode.getComponent('InGameManager');
+                if (inGameManager && typeof inGameManager['getGameDuration'] === 'function') {
+                    gameDuration = Math.floor(inGameManager['getGameDuration']() / 1000); // 转换为秒
+                }
+            }
+            
+            // 获取当前关卡信息
+            const currentLevel = this.saveDataManager?.getCurrentLevel() || 1;
+            
+            // 构建埋点数据
+            const failureData = {
+                $event_duration: gameDuration,
+                $section_id: currentLevel,
+                $section_name: `Level ${currentLevel}`,
+                $section_type: "主线关卡" // 目前项目中所有关卡都是主线关卡
+            };
+            
+            // 上报埋点
+            AnalyticsManager.getInstance().trackGameFailure(failureData);
+            
+            console.log('[Wall] $GameFailure 埋点已上报:', failureData);
+        } catch (error) {
+            console.error('[Wall] 上报 $GameFailure 埋点时发生错误:', error);
+        }
+    }
+    
     onDestroy() {
         // 清理事件监听
         const eventBus = EventBus.getInstance();

+ 22 - 5
assets/scripts/CombatSystem/WeaponBullet.ts

@@ -737,15 +737,32 @@ export class WeaponBullet extends Component {
             spineNode.active = false;
         }
         
-        // 查找TrailEffect节点 - 所有武器都使用拖尾效果
+        // 查找TrailEffect节点 - 武器都使用拖尾效果
         const trailEffectNode = this.node.getChildByName('TrailEffect');
         if (trailEffectNode) {
             trailEffectNode.active = true;
-            
-            // 为拖尾效果设置白色颜色
-            const trailController = trailEffectNode.getComponent(BulletTrailController);
+
+            // BulletTrailController 挂在容器节点上(PelletContainer),不是 TrailEffect 子节点
+            const trailController = this.getComponent(BulletTrailController)
+                || this.node.getComponentInChildren(BulletTrailController)
+                || trailEffectNode.getComponent(BulletTrailController);
+
             if (trailController) {
-                trailController.setTrailColor('white');
+                const id = this.weaponConfig?.id;
+                if (id === 'okra_missile') {
+                    // 秋葵导弹专属拖尾:双层喷焰(边缘橙红,中心白色)
+                    if ((trailController as any).applyPresetRocketDualLayer) {
+                        (trailController as any).applyPresetRocketDualLayer();
+                    } else {
+                        // 回退到单层火焰色
+                        trailController.applyPresetRocket();
+                    }
+                } else {
+                    // 默认:白色拖尾
+                    trailController.setTrailColor('white');
+                }
+            } else {
+                console.warn('[WeaponBullet] 未找到 BulletTrailController,拖尾颜色无法按武器设置');
             }
         }
     }

+ 45 - 0
assets/scripts/FourUI/ShopSystem/ShopController.ts

@@ -5,6 +5,7 @@ import { TopBarController } from '../TopBarController';
 import { MoneyAni } from '../../Animations/MoneyAni';
 import { AdManager } from '../../Ads/AdManager';
 import { JsonConfigLoader } from '../../Core/JsonConfigLoader';
+import { AnalyticsManager, OpenShopProperties, ViewMallContentProperties } from '../../Utils/AnalyticsManager';
 
 const { ccclass, property } = _decorator;
 
@@ -98,6 +99,12 @@ export class ShopController extends Component {
         await this.loadShopConfig();
         this.setupEventListeners();
         this.updateUI();
+        
+        // 追踪商店打开事件
+        this.trackOpenShopEvent();
+        
+        // 追踪查看商城内容事件
+        this.trackViewMallContentEvent();
     }
     
     start() {
@@ -624,4 +631,42 @@ export class ShopController extends Component {
             today: this.getCurrentDateString()
         };
     }
+
+    /**
+     * 追踪商店打开事件
+     */
+    private trackOpenShopEvent(): void {
+        try {
+            const properties: OpenShopProperties = {
+                shop_type: 'daily_reward', // 商店类型:每日奖励商店
+                entry_point: 'main_ui', // 入口点:主界面
+                user_level: this.saveDataManager?.getCurrentLevel() || 1,
+                user_money: this.saveDataManager?.getMoney() || 0,
+                user_diamonds: this.saveDataManager?.getDiamonds() || 0
+            };
+
+            AnalyticsManager.getInstance().trackOpenShop(properties);
+            console.log('[ShopController] $OpenShop 事件已上报:', properties);
+        } catch (error) {
+            console.error('[ShopController] 追踪商店打开事件时出错:', error);
+        }
+    }
+
+    private trackViewMallContentEvent(): void {
+        try {
+            const properties: ViewMallContentProperties = {
+                content_type: 'daily_rewards', // 内容类型:每日奖励
+                content_id: 'shop_daily_rewards', // 内容ID:商店每日奖励
+                view_time: Date.now(), // 查看时间戳
+                user_level: this.saveDataManager?.getCurrentLevel() || 1,
+                user_money: this.saveDataManager?.getMoney() || 0,
+                user_diamonds: this.saveDataManager?.getDiamonds() || 0
+            };
+
+            AnalyticsManager.getInstance().trackViewMallContent(properties);
+            console.log('[ShopController] $ViewMallContent 事件已上报:', properties);
+        } catch (error) {
+            console.error('[ShopController] 追踪查看商城内容事件时出错:', error);
+        }
+    }
 }

+ 99 - 0
assets/scripts/FourUI/UpgradeSystem/UpgradeController.ts

@@ -5,6 +5,7 @@ import { SaveDataManager, WeaponData } from '../../LevelSystem/SaveDataManager';
 import EventBus, { GameEvents } from '../../Core/EventBus';
 import { UpgradeAni } from './UpgradeAni';
 import { PopUPAni } from '../../Animations/PopUPAni';
+import { AnalyticsManager, MPAddFavoritesProperties } from '../../Utils/AnalyticsManager';
 
 const { ccclass, property } = _decorator;
 
@@ -512,6 +513,25 @@ export class UpgradeController extends Component {
                 }
             }
         }
+
+        // 设置收藏按钮 - 查找Favorite按钮节点
+        const favoriteButton = weaponNode.getChildByName('Favorite')?.getComponent(Button);
+        if (favoriteButton) {
+            // 清除之前的事件监听
+            favoriteButton.node.off(Button.EventType.CLICK);
+            
+            // 检查当前武器是否为收藏武器
+            const currentFavorite = this.saveDataManager.getFavoriteWeapon();
+            const isFavorite = currentFavorite === weaponConfig.id;
+            
+            // 设置收藏按钮状态
+            this.updateFavoriteButtonState(favoriteButton.node, isFavorite);
+            
+            // 添加收藏事件
+            favoriteButton.node.on(Button.EventType.CLICK, () => {
+                this.onFavoriteWeapon(weaponConfig.id);
+            }, this);
+        }
         
         // Unlock.prefab已经有正常的视觉效果,确保节点激活
         weaponNode.active = true;
@@ -1231,6 +1251,85 @@ export class UpgradeController extends Component {
         }
     }
     
+    /**
+     * 处理武器收藏事件
+     */
+    private onFavoriteWeapon(weaponId: string): void {
+        const currentFavorite = this.saveDataManager.getFavoriteWeapon();
+        const isFavorite = currentFavorite === weaponId;
+        
+        if (isFavorite) {
+            // 取消收藏
+            this.saveDataManager.setFavoriteWeapon('');
+            console.log(`[UpgradeController] 取消收藏武器: ${weaponId}`);
+        } else {
+            // 设置为收藏
+            this.saveDataManager.setFavoriteWeapon(weaponId);
+            console.log(`[UpgradeController] 收藏武器: ${weaponId}`);
+            
+            // 触发$MPAddFavorites事件
+            this.trackMPAddFavorites(weaponId);
+        }
+        
+        // 刷新所有武器节点的收藏状态
+        this.refreshFavoriteStates();
+    }
+    
+    /**
+     * 更新收藏按钮状态
+     */
+    private updateFavoriteButtonState(buttonNode: Node, isFavorite: boolean): void {
+        const buttonLabel = buttonNode.getChildByName('Label')?.getComponent(Label);
+        if (buttonLabel) {
+            buttonLabel.string = isFavorite ? '★' : '☆';
+        }
+        
+        // 可以添加更多视觉效果,比如颜色变化
+        const buttonSprite = buttonNode.getComponent(Sprite);
+        if (buttonSprite) {
+            buttonSprite.color = isFavorite ? new Color(255, 215, 0) : new Color(255, 255, 255); // 金色 vs 白色
+        }
+    }
+    
+    /**
+     * 刷新所有武器节点的收藏状态
+     */
+    private refreshFavoriteStates(): void {
+        const currentFavorite = this.saveDataManager.getFavoriteWeapon();
+        
+        this.weaponNodes.forEach(weaponNode => {
+            const favoriteButton = weaponNode.getChildByName('Favorite')?.getComponent(Button);
+            if (favoriteButton) {
+                // 从节点名称或数据中获取武器ID
+                const weaponId = weaponNode.name; // 假设节点名称就是武器ID
+                const isFavorite = currentFavorite === weaponId;
+                this.updateFavoriteButtonState(favoriteButton.node, isFavorite);
+            }
+        });
+    }
+    
+    /**
+     * 跟踪$MPAddFavorites事件
+     */
+    private trackMPAddFavorites(weaponId: string): void {
+        const weaponConfig = this.weaponsConfig.weapons.find(config => config.id === weaponId);
+        const weaponData = this.saveDataManager.getWeapon(weaponId);
+        
+        const properties: MPAddFavoritesProperties = {
+            favorite_type: 'weapon',
+            favorite_id: weaponId,
+            favorite_name: weaponConfig?.name || weaponId,
+            favorite_time: Date.now(),
+            user_level: this.saveDataManager.getCurrentLevel(),
+            user_money: this.saveDataManager.getMoney(),
+            user_diamonds: this.saveDataManager.getDiamonds(),
+            weapon_level: weaponData?.level || 0,
+            weapon_rarity: weaponConfig?.rarity || 'common'
+        };
+        
+        AnalyticsManager.getInstance().trackMPAddFavorites(properties);
+    }
+    
     /**
      * 组件销毁时清理事件监听
      */

+ 183 - 0
assets/scripts/LaunchScreen.ts

@@ -1,4 +1,7 @@
 import { _decorator, Component, Node, director, assetManager, ProgressBar, Label, Sprite, SpriteFrame, resources } from 'cc';
+import { Analytics } from './Utils/AnalyticsManager';
+import { MPLifecycle } from './Utils/MPLifecycleManager';
+import './Utils/BlockMergeTrackingTest';
 const { ccclass, property } = _decorator;
 
 // 微信小游戏API类型声明
@@ -10,6 +13,9 @@ declare global {
                 success?: () => void;
                 fail?: (err: any) => void;
             }) => any;
+            getLaunchOptionsSync?: () => any;
+            onShow?: (callback: (options: any) => void) => void;
+            onHide?: (callback: () => void) => void;
         };
     }
     const wx: {
@@ -18,6 +24,9 @@ declare global {
             success?: () => void;
             fail?: (err: any) => void;
         }) => any;
+        getLaunchOptionsSync?: () => any;
+        onShow?: (callback: (options: any) => void) => void;
+        onHide?: (callback: () => void) => void;
     } | undefined;
 }
 
@@ -37,10 +46,24 @@ export class LaunchScreen extends Component {
 
     private totalAssets = 0;
     private loadedAssets = 0;
+    private sceneLoadStartTime = 0;
+    private launchOptions: any = {};
 
     start() {
         console.log('[LaunchScreen] 启动页开始初始化');
         
+        // 初始化埋点管理器
+        Analytics.init();
+        
+        // 初始化小程序生命周期管理器
+        MPLifecycle.init();
+        
+        // 注册与登录埋点
+        this.setupAuthTracking();
+
+        // 追踪小程序启动事件
+        this.trackMPLaunch();
+        
         // 设置启动页背景
         this.setupBackground();
         
@@ -87,13 +110,28 @@ export class LaunchScreen extends Component {
         return new Promise<void>((resolve, reject) => {
             console.log('[LaunchScreen] 开始预加载GameLevel场景');
             
+            // 追踪场景加载开始事件
+            this.sceneLoadStartTime = Date.now();
+            Analytics.trackSceneLoadStart('GameLevel');
+            
             // 直接预加载场景
             director.preloadScene('GameLevel', (finished: number, total: number) => {
                 const progress = finished / total;
                 this.updateProgress(progress * 100, `正在加载游戏场景... ${Math.floor(progress * 100)}%`);
             }, (err) => {
+                const loadTime = Date.now() - this.sceneLoadStartTime;
+                
                 if (err) {
                     console.error('[LaunchScreen] GameLevel场景预加载失败:', err);
+                    
+                    // 追踪场景加载失败事件
+                    Analytics.trackSceneLoaded({
+                        scene_name: 'GameLevel',
+                        load_time: loadTime,
+                        success: false,
+                        error_msg: err.toString()
+                    });
+                    
                     // 预加载失败时,仍然尝试直接跳转(可能场景存在但预加载失败)
                     console.log('[LaunchScreen] 预加载失败,但仍将尝试直接加载场景');
                     this.updateProgress(100, '准备进入游戏...');
@@ -102,6 +140,14 @@ export class LaunchScreen extends Component {
                 }
                 
                 console.log('[LaunchScreen] GameLevel场景预加载完成');
+                
+                // 追踪场景加载成功事件
+                Analytics.trackSceneLoaded({
+                    scene_name: 'GameLevel',
+                    load_time: loadTime,
+                    success: true
+                });
+                
                 this.updateProgress(100, '加载完成!');
                 resolve();
             });
@@ -149,4 +195,141 @@ export class LaunchScreen extends Component {
             });
         }, 1.0);
     }
+
+    // ==================== 埋点相关方法 ====================
+
+    /**
+     * 追踪小程序启动事件
+     */
+    private trackMPLaunch(): void {
+        // 使用已获取的启动参数
+        const opts = this.launchOptions || {};
+
+        Analytics.trackMPLaunch({
+            scene: opts.scene || 0,
+            query: JSON.stringify(opts.query || {}),
+            shareTicket: opts.shareTicket || '',
+            referrerInfo: opts.referrerInfo || {}
+        });
+
+        // 检测是否通过分享启动,如果是则追踪分享事件
+        if (opts.shareTicket) {
+            this.trackMPShareFromLaunch(opts);
+        }
+    }
+
+    /**
+     * 追踪通过分享启动的事件
+     */
+    private trackMPShareFromLaunch(launchOptions: any): void {
+        try {
+            Analytics.trackMPShare({
+                share_type: 'launch_from_share', // 分享类型:从分享启动
+                share_target: 'unknown', // 分享目标:未知(启动时无法确定)
+                share_content: launchOptions.shareTicket || '', // 分享内容:shareTicket
+                share_time: Date.now(), // 分享时间戳
+                scene: launchOptions.scene || 0, // 启动场景
+                query: JSON.stringify(launchOptions.query || {}) // 启动参数
+            });
+            console.log('[LaunchScreen] $MPShare 事件已上报(从分享启动):', launchOptions);
+        } catch (error) {
+            console.error('[LaunchScreen] 追踪分享启动事件时出错:', error);
+        }
+    }
+
+    /**
+     * 设置注册/登录/登出/创建角色埋点
+     */
+    private setupAuthTracking(): void {
+        // 获取并缓存启动参数(微信小游戏环境)
+        if (typeof wx !== 'undefined' && wx.getLaunchOptionsSync) {
+            try {
+                this.launchOptions = wx.getLaunchOptionsSync() || {};
+            } catch (error) {
+                console.warn('[LaunchScreen] 获取启动参数失败:', error);
+                this.launchOptions = {};
+            }
+        }
+
+        const sceneVal = this.launchOptions?.scene || 0;
+        const queryStr = JSON.stringify(this.launchOptions?.query || {});
+
+        // 首次注册(本地标记判断)
+        try {
+            const firstFlag = localStorage.getItem('first_register_tracked');
+            if (!firstFlag) {
+                Analytics.trackUserFirstRegister({
+                    register_time: Date.now(),
+                    channel: typeof wx !== 'undefined' ? 'wechat_game' : 'web',
+                    scene: sceneVal,
+                    query: queryStr
+                });
+
+                // 小程序注册事件(首次)
+                Analytics.trackMPRegister({
+                    register_time: Date.now(),
+                    scene: sceneVal,
+                    query: queryStr
+                });
+
+                // 创建角色(首次运行时认为创建默认角色)
+                this.trackInitialRoleCreate();
+
+                localStorage.setItem('first_register_tracked', '1');
+            }
+        } catch (e) {
+            console.warn('[LaunchScreen] 处理首次注册标记失败:', e);
+        }
+
+        // 小程序登录事件(每次启动)
+        Analytics.trackMPLogin({
+            login_time: Date.now(),
+            scene: sceneVal,
+            query: queryStr
+        });
+
+        // 小程序登出事件(监听 onHide)
+        if (typeof wx !== 'undefined' && wx.onHide) {
+            try {
+                wx.onHide(() => {
+                    Analytics.trackMPLogout({
+                        logout_time: Date.now(),
+                        scene: sceneVal
+                    });
+                });
+            } catch (error) {
+                console.warn('[LaunchScreen] 注册 onHide 失败:', error);
+            }
+        }
+    }
+
+    /**
+     * 首次运行时创建默认角色并上报
+     */
+    private trackInitialRoleCreate(): void {
+        try {
+            const roleFlag = localStorage.getItem('default_role_created');
+            if (!roleFlag) {
+                Analytics.trackCreateRole({
+                    role_id: 'default_player',
+                    role_name: 'Player',
+                    role_class: 'starter',
+                    create_time: Date.now(),
+                    user_level: 1
+                });
+                localStorage.setItem('default_role_created', '1');
+                console.log('[LaunchScreen] $CreateRole 事件已上报(默认角色创建)');
+            }
+        } catch (e) {
+            console.warn('[LaunchScreen] 创建默认角色标记失败:', e);
+        }
+    }
+
+    /**
+     * 组件销毁时的清理工作
+     */
+    onDestroy(): void {
+        // 追踪场景卸载事件
+        Analytics.trackSceneUnloaded('LaunchScreen');
+    }
 }

+ 0 - 12
assets/scripts/LevelSystem/GameManager.ts

@@ -119,10 +119,6 @@ export class GameManager extends Component {
     // 游戏内管理器引用
     private inGameManager: InGameManager = null;
     
-
-    
-
-
     // 游戏区域的边界
     private gameBounds = {
         left: 0,
@@ -144,8 +140,6 @@ export class GameManager extends Component {
     private pendingSkillSelection: boolean = false;
     private shouldShowNextWavePrompt: boolean = false;
 
-
-
     // === 游戏计时器 ===
     private gameStartTime: number = 0;
     private gameEndTime: number = 0;
@@ -761,7 +755,6 @@ export class GameManager extends Component {
      * 获取当前游戏内状态已迁移到 InGameManager
      * 请使用 InGameManager.getInstance().getCurrentState()
      */
-
     /**
      * 获取InGameManager实例
      * 用于访问游戏内状态和逻辑
@@ -977,7 +970,6 @@ export class GameManager extends Component {
     }
 
     /* ========= 墙体血量 / 等级相关 ========= */
-
     // === 获取墙体血量(委托给InGameManager)===
     private getWallHealth(): number {
         if (this.inGameManager) {
@@ -1012,8 +1004,6 @@ export class GameManager extends Component {
         return this.wallComponent ? this.wallComponent.getCurrentHealth() : 100;
     }
 
-
-
     // 初始化GameBlockSelection组件
     public initGameBlockSelection() {
         console.log('[GameManager] 初始化GameBlockSelection组件');
@@ -1084,6 +1074,4 @@ export class GameManager extends Component {
         // 设置单例实例
         GameManager.setInstance(this);
     }
-
-
 }

+ 28 - 0
assets/scripts/LevelSystem/IN_game.ts

@@ -9,6 +9,9 @@ import { BackgroundManager } from './BackgroundManager';
 import { GroundBurnAreaManager } from '../CombatSystem/BulletEffects/GroundBurnAreaManager';
 import { ScreenShakeManager } from '../CombatSystem/EnemyWeapon/ScreenShakeManager';
 import { Audio } from '../AudioManager/AudioManager';
+import { AnalyticsManager } from '../Utils/AnalyticsManager';
+import { SaveDataManager } from '../LevelSystem/SaveDataManager';
+import { LevelConfigManager } from './LevelConfigManager';
 
 const { ccclass, property } = _decorator;
 
@@ -1197,6 +1200,31 @@ export class InGameManager extends Component {
             await this.backgroundManager.setBackground('LevelBackground/BG1');
             console.log('[InGameManager] 使用默认背景: LevelBackground/BG1');
         }
+
+        // 上报 $StartSection 事件
+        try {
+            const currentLevel = SaveDataManager.getInstance().getCurrentLevel();
+            const userMoney = SaveDataManager.getInstance().getMoney();
+            const levelConfigManager = LevelConfigManager.getInstance();
+            const levelName = levelConfigManager ? levelConfigManager.getLevelName(currentLevel) : `关卡 ${currentLevel}`;
+            
+            const startSectionProperties = {
+                $section_id: currentLevel,
+                $section_name: levelName,
+                $section_type: 'level',
+                total_waves: this.levelWaves.length,
+                total_enemies: this.levelTotalEnemies,
+                energy_max: this.energyMax,
+                user_level: currentLevel,
+                user_money: userMoney,
+                background_image: levelConfig.backgroundImage || 'LevelBackground/BG1'
+            };
+            
+            AnalyticsManager.getInstance().trackStartSection(startSectionProperties);
+            console.log('[InGameManager] $StartSection 事件已上报', startSectionProperties);
+        } catch (error) {
+            console.error('[InGameManager] $StartSection 事件上报失败:', error);
+        }
     }
     /**
      * 同步播放战斗音乐(立即执行,不等待异步操作)

+ 17 - 0
assets/scripts/LevelSystem/SaveDataManager.ts

@@ -1154,6 +1154,23 @@ export class SaveDataManager {
             (this.playerData.statistics[key] as number) += value;
         }
     }
+
+    /**
+     * 设置收藏武器
+     */
+    public setFavoriteWeapon(weaponId: string): void {
+        if (!this.playerData) return;
+        
+        this.playerData.statistics.favoriteWeapon = weaponId;
+        this.savePlayerData();
+    }
+
+    /**
+     * 获取收藏武器
+     */
+    public getFavoriteWeapon(): string {
+        return this.playerData?.statistics.favoriteWeapon || '';
+    }
     
     // === 设置管理 ===
     

+ 680 - 0
assets/scripts/Utils/AnalyticsManager.ts

@@ -0,0 +1,680 @@
+/**
+ * 埋点管理器 - 负责游戏内事件追踪和数据上报
+ */
+// 事件属性类型定义
+export interface EventProperties {
+    [key: string]: string | number | boolean;
+}
+
+// 小程序生命周期事件属性
+export interface MPLifecycleProperties {
+    scene?: number;           // 场景值
+    query?: string;          // 启动参数
+    shareTicket?: string;    // 分享票据
+    referrerInfo?: any;      // 来源信息
+}
+// 场景加载事件属性
+export interface SceneLoadProperties {
+    scene_name: string;      // 场景名称
+    load_time?: number;      // 加载时长(ms)
+    success: boolean;        // 是否成功
+    error_msg?: string;      // 错误信息
+}
+// 游戏进度事件属性
+export interface GameProgressProperties {
+    level?: number;          // 关卡等级
+    score?: number;          // 分数
+    time_spent?: number;     // 耗时(秒)
+    result?: string;         // 结果
+}
+
+// 方块合成事件属性
+export interface BlockMergeProperties extends EventProperties {
+    merge_level: number;     // 合成等级 (2, 3, 4)
+    weapon_type?: string;    // 武器类型
+    shape_type?: string;     // 形状类型
+    session_id?: string;     // 会话ID
+}
+
+// 游戏失败事件属性
+export interface GameFailureProperties extends EventProperties {
+    $event_duration: number;  // 停留时长(秒)
+    $section_id: number;      // 关卡id
+    $section_name: string;    // 关卡名称
+    $section_type: string;    // 关卡类型 (0=主线关卡,1=活动关卡,2=挑战关卡,3=支线关卡)
+}
+
+// 商店打开事件属性
+export interface OpenShopProperties extends EventProperties {
+    shop_type?: string;       // 商店类型
+    entry_point?: string;     // 进入商店的入口点
+    user_level?: number;      // 用户等级
+    user_money?: number;      // 用户金币数量
+    user_diamonds?: number;   // 用户钻石数量
+}
+
+// 小程序分享事件属性
+export interface MPShareProperties extends EventProperties {
+    share_type?: string;      // 分享类型 (game_result, achievement, invite)
+    share_content?: string;   // 分享内容描述
+    share_target?: string;    // 分享目标 (friend, group, timeline)
+    level?: number;           // 当前关卡
+    score?: number;           // 当前分数
+    user_level?: number;      // 用户等级
+}
+
+// 查看商城内容事件属性
+export interface ViewMallContentProperties extends EventProperties {
+    content_type?: string;    // 内容类型 (weapon, skill, decoration)
+    content_id?: string;      // 内容ID
+    content_name?: string;    // 内容名称
+    price?: number;           // 价格
+    currency_type?: string;   // 货币类型 (coin, diamond)
+    user_money?: number;      // 用户当前金币
+    user_diamonds?: number;   // 用户当前钻石
+    view_duration?: number;   // 查看时长(秒)
+}
+
+// 用户首次注册事件属性
+export interface UserFirstRegisterProperties extends EventProperties {
+    register_time?: number;   // 注册时间戳
+    channel?: string;         // 注册渠道(如 wechat_game, web)
+    scene?: number;           // 启动场景值
+    query?: string;           // 启动参数
+}
+
+// 小程序注册事件属性
+export interface MPRegisterProperties extends EventProperties {
+    register_time?: number;   // 注册时间戳
+    scene?: number;           // 启动场景值
+    query?: string;           // 启动参数
+}
+
+// 小程序登录事件属性
+export interface MPLoginProperties extends EventProperties {
+    login_time?: number;      // 登录时间戳
+    scene?: number;           // 启动场景值
+    query?: string;           // 启动参数
+}
+
+// 小程序登出事件属性
+export interface MPLogoutProperties extends EventProperties {
+    logout_time?: number;     // 登出时间戳
+    scene?: number;           // 场景值
+}
+
+// 创建角色事件属性
+export interface CreateRoleProperties extends EventProperties {
+    role_id: string;          // 角色ID
+    role_name?: string;       // 角色名称
+    role_class?: string;      // 角色职业/类型
+    create_time?: number;     // 创建时间戳
+    user_level?: number;      // 用户等级
+}
+
+// 广告展示事件属性
+export interface AdShowProperties extends EventProperties {
+    ad_type?: string;         // 广告类型 (rewarded_video, banner, interstitial)
+    ad_placement?: string;    // 广告位置 (shop_reward, level_reward, extra_coins)
+    ad_source?: string;       // 广告来源
+    reward_type?: string;     // 奖励类型 (coins, diamonds, items)
+    reward_amount?: number;   // 奖励数量
+    user_level?: number;      // 用户等级
+    show_result?: string;     // 展示结果 (success, failed, closed)
+}
+
+// 小程序添加收藏事件属性
+export interface MPAddFavoritesProperties extends EventProperties {
+    favorite_type?: string;   // 收藏类型 (game, achievement, score)
+    trigger_point?: string;   // 触发点 (game_end, achievement_unlock, manual)
+    user_level?: number;      // 用户等级
+    game_progress?: number;   // 游戏进度百分比
+}
+
+// 方块选择点击事件属性
+export interface BlockSelectionClickProperties extends EventProperties {
+    button_type: string;      // 按钮类型 (add_ball, add_coin, refresh_block)
+    button_cost?: number;     // 按钮费用
+    user_money?: number;      // 用户当前金币数量
+    user_level?: number;      // 用户等级
+    usage_count?: number;     // 该按钮的使用次数
+    success: boolean;         // 操作是否成功
+    failure_reason?: string;  // 失败原因(如金币不足)
+}
+
+// 开始关卡事件属性
+export interface StartSectionProperties extends EventProperties {
+    $section_id: number;      // 关卡ID
+    $section_name: string;    // 关卡名称
+    $section_type: string;    // 关卡类型 (0=主线关卡,1=活动关卡,2=挑战关卡,3=支线关卡)
+    total_waves?: number;     // 总波次数
+    total_enemies?: number;   // 总敌人数量
+    energy_max?: number;      // 初始最大能量值
+    user_level?: number;      // 用户等级
+    user_money?: number;      // 用户当前金币数量
+    background_image?: string; // 背景图片
+}
+
+/**
+ * 埋点管理器单例类
+ */
+export class AnalyticsManager {
+    private static instance: AnalyticsManager;
+    private isEnabled: boolean = true;
+    private sessionId: string;
+    private userId: string;
+    private deviceInfo: any = {};
+
+    private constructor() {
+        this.sessionId = this.generateSessionId();
+        this.userId = this.getUserId();
+        this.initDeviceInfo();
+    }
+
+    /**
+     * 获取单例实例
+     */
+    public static getInstance(): AnalyticsManager {
+        if (!AnalyticsManager.instance) {
+            AnalyticsManager.instance = new AnalyticsManager();
+        }
+        return AnalyticsManager.instance;
+    }
+
+    /**
+     * 初始化埋点管理器
+     */
+    public init(): void {
+        console.log('[AnalyticsManager] 埋点管理器初始化完成');
+        console.log('[AnalyticsManager] SessionId:', this.sessionId);
+        console.log('[AnalyticsManager] UserId:', this.userId);
+    }
+    /**
+     * 设置埋点开关
+     */
+    public setEnabled(enabled: boolean): void {
+        this.isEnabled = enabled;
+        console.log('[AnalyticsManager] 埋点功能', enabled ? '已启用' : '已禁用');
+    }
+    /**
+     * 发送事件
+     */
+    public track(eventName: string, properties: EventProperties = {}): void {
+        if (!this.isEnabled) {
+            return;
+        }
+        const eventData = {
+            event_name: eventName,
+            timestamp: Date.now(),
+            session_id: this.sessionId,
+            user_id: this.userId,
+            device_info: this.deviceInfo,
+            properties: properties
+        };
+
+        // 输出到控制台(实际项目中应该发送到服务器)
+        console.log('[AnalyticsManager] 事件追踪:', JSON.stringify(eventData, null, 2));
+        
+        // 这里可以集成第三方埋点SDK,如:
+        // - 微信小游戏数据助手
+        // - TalkingData
+        // - 友盟统计
+        // - 自定义服务器接口
+        this.sendToServer(eventData);
+    }
+
+    // ==================== 小程序生命周期事件 ====================
+
+    /**
+     * 小程序启动事件
+     */
+    public trackMPLaunch(properties: MPLifecycleProperties = {}): void {
+        this.track('$MPLaunch', {
+            scene: properties.scene || 0,
+            query: properties.query || '',
+            shareTicket: properties.shareTicket || '',
+            referrerInfo: JSON.stringify(properties.referrerInfo || {}),
+            ...properties
+        });
+    }
+
+    /**
+     * 小程序显示事件
+     */
+    public trackMPShow(properties: MPLifecycleProperties = {}): void {
+        this.track('$MPShow', {
+            scene: properties.scene || 0,
+            query: properties.query || '',
+            ...properties
+        });
+    }
+
+    /**
+     * 小程序隐藏事件
+     */
+    public trackMPHide(): void {
+        this.track('$MPHide', {
+            hide_time: Date.now()
+        });
+    }
+
+    // ==================== 场景加载事件 ====================
+
+    /**
+     * 场景加载开始事件
+     */
+    public trackSceneLoadStart(sceneName: string): void {
+        this.track('$SceneLoadStart', {
+            scene_name: sceneName,
+            start_time: Date.now()
+        });
+    }
+
+    /**
+     * 场景加载完成事件
+     */
+    public trackSceneLoaded(properties: SceneLoadProperties): void {
+        this.track('$SceneLoaded', {
+            scene_name: properties.scene_name,
+            load_time: properties.load_time || 0,
+            success: properties.success,
+            error_msg: properties.error_msg || '',
+            ...properties
+        });
+    }
+
+    /**
+     * 场景卸载事件
+     */
+    public trackSceneUnloaded(sceneName: string): void {
+        this.track('$SceneUnloaded', {
+            scene_name: sceneName,
+            unload_time: Date.now()
+        });
+    }
+
+    // ==================== 游戏进度事件 ====================
+
+    /**
+     * 完成关卡事件
+     */
+    public trackCompleteSection(properties: GameProgressProperties): void {
+        this.track('$CompleteSection', {
+            level: properties.level || 1,
+            score: properties.score || 0,
+            time_spent: properties.time_spent || 0,
+            result: properties.result || 'success',
+            ...properties
+        });
+    }
+
+    /**
+     * 等级提升事件
+     */
+    public trackUpdateLevel(oldLevel: number, newLevel: number): void {
+        this.track('$UpdateLevel', {
+            old_level: oldLevel,
+            new_level: newLevel,
+            level_diff: newLevel - oldLevel
+        });
+    }
+
+    // ==================== 新手引导事件 ====================
+
+    /**
+     * 新手引导开始事件
+     */
+    public trackTutorialStart(): void {
+        this.track('$TutorialStart', {
+            start_time: Date.now()
+        });
+    }
+
+    /**
+     * 新手引导完成事件
+     */
+    public trackTutorialFinish(timeSpent: number): void {
+        this.track('$TutorialFinish', {
+            time_spent: timeSpent,
+            finish_time: Date.now()
+        });
+    }
+
+    // ==================== 方块合成事件 ====================
+
+    /**
+     * 追踪方块合成等级达成事件
+     * @param mergeLevel 合成等级 (2, 3, 4)
+     * @param weaponType 武器类型
+     * @param shapeType 形状类型
+     */
+    public trackBlockMergeLevel(mergeLevel: number, weaponType?: string, shapeType?: string): void {
+        if (mergeLevel < 2 || mergeLevel > 4) {
+            console.warn(`[Analytics] Invalid merge level: ${mergeLevel}. Only levels 2-4 are tracked.`);
+            return;
+        }
+
+        const properties: BlockMergeProperties = {
+            merge_level: mergeLevel,
+            session_id: this.sessionId
+        };
+
+        if (weaponType) {
+            properties.weapon_type = weaponType;
+        }
+
+        if (shapeType) {
+            properties.shape_type = shapeType;
+        }
+
+        // 上报合成等级达成事件
+        this.track('$block_merge', properties);
+
+        console.log(`[Analytics] Block merge level ${mergeLevel} achieved:`, properties);
+    }
+
+    /**
+     * 追踪方块合成等级2达成
+     */
+    public trackMergeLevel2(weaponType?: string, shapeType?: string): void {
+        this.trackBlockMergeLevel(2, weaponType, shapeType);
+    }
+
+    /**
+     * 追踪方块合成等级3达成
+     */
+    public trackMergeLevel3(weaponType?: string, shapeType?: string): void {
+        this.trackBlockMergeLevel(3, weaponType, shapeType);
+    }
+
+    /**
+     * 追踪方块合成等级4达成
+     */
+    public trackMergeLevel4(weaponType?: string, shapeType?: string): void {
+        this.trackBlockMergeLevel(4, weaponType, shapeType);
+    }
+
+    /**
+     * 追踪游戏失败事件(墙体摧毁)
+     */
+    public trackGameFailure(properties: GameFailureProperties): void {
+        if (!this.isEnabled) {
+            console.log('[AnalyticsManager] 埋点已禁用,跳过 $GameFailure 事件');
+            return;
+        }
+
+        console.log('[AnalyticsManager] 追踪游戏失败事件:', properties);
+        this.track('$GameFailure', properties);
+    }
+
+    /**
+     * 追踪商店打开事件
+     */
+    public trackOpenShop(properties: OpenShopProperties = {}): void {
+        if (!this.isEnabled) {
+            console.log('[AnalyticsManager] 埋点已禁用,跳过 $OpenShop 事件');
+            return;
+        }
+
+        console.log('[AnalyticsManager] 追踪商店打开事件:', properties);
+        this.track('$OpenShop', properties);
+    }
+
+    /**
+     * 追踪方块选择点击事件
+     */
+    public trackBlockSelectionClick(properties: BlockSelectionClickProperties): void {
+        if (!this.isEnabled) {
+            console.log('[AnalyticsManager] 埋点已禁用,跳过 $block_selection_click 事件');
+            return;
+        }
+
+        console.log('[AnalyticsManager] 追踪方块选择点击事件:', properties);
+        this.track('$block_selection_click', properties);
+    }
+
+    /**
+     * 追踪开始关卡事件
+     */
+    public trackStartSection(properties: StartSectionProperties): void {
+        if (!this.isEnabled) {
+            console.log('[AnalyticsManager] 埋点已禁用,跳过 $StartSection 事件');
+            return;
+        }
+
+        console.log('[AnalyticsManager] 追踪开始关卡事件:', properties);
+        this.track('$StartSection', properties);
+    }
+
+    // ==================== 小程序分享事件 ====================
+
+    /**
+     * 追踪小程序分享事件
+     */
+    public trackMPShare(properties: MPShareProperties = {}): void {
+        if (!this.isEnabled) {
+            console.log('[AnalyticsManager] 埋点已禁用,跳过 $MPShare 事件');
+            return;
+        }
+
+        console.log('[AnalyticsManager] 追踪小程序分享事件:', properties);
+        this.track('$MPShare', properties);
+    }
+
+    // ==================== 商城内容查看事件 ====================
+
+    /**
+     * 追踪查看商城内容事件
+     */
+    public trackViewMallContent(properties: ViewMallContentProperties = {}): void {
+        if (!this.isEnabled) {
+            console.log('[AnalyticsManager] 埋点已禁用,跳过 $ViewMallContent 事件');
+            return;
+        }
+
+        console.log('[AnalyticsManager] 追踪查看商城内容事件:', properties);
+        this.track('$ViewMallContent', properties);
+    }
+
+    // ==================== 广告展示事件 ====================
+
+    /**
+     * 追踪广告展示事件
+     */
+    public trackAdShow(properties: AdShowProperties = {}): void {
+        if (!this.isEnabled) {
+            console.log('[AnalyticsManager] 埋点已禁用,跳过 $AdShow 事件');
+            return;
+        }
+
+        console.log('[AnalyticsManager] 追踪广告展示事件:', properties);
+        this.track('$AdShow', properties);
+    }
+
+    // ==================== 小程序添加收藏事件 ====================
+
+    /**
+     * 追踪小程序添加收藏事件
+     */
+    public trackMPAddFavorites(properties: MPAddFavoritesProperties = {}): void {
+        if (!this.isEnabled) {
+            console.log('[AnalyticsManager] 埋点已禁用,跳过 $MPAddFavorites 事件');
+            return;
+        }
+
+        console.log('[AnalyticsManager] 追踪小程序添加收藏事件:', properties);
+        this.track('$MPAddFavorites', properties);
+    }
+
+    // ==================== 用户注册与账号事件 ====================
+
+    /**
+     * 追踪用户首次注册事件
+     */
+    public trackUserFirstRegister(properties: UserFirstRegisterProperties = {}): void {
+        if (!this.isEnabled) {
+            console.log('[AnalyticsManager] 埋点已禁用,跳过 $UserFirstRegister 事件');
+            return;
+        }
+
+        const payload = {
+            register_time: properties.register_time || Date.now(),
+            channel: properties.channel || (typeof wx !== 'undefined' ? 'wechat_game' : 'web'),
+            scene: properties.scene || 0,
+            query: properties.query || '',
+            ...properties
+        };
+
+        console.log('[AnalyticsManager] 追踪用户首次注册事件:', payload);
+        this.track('$UserFirstRegister', payload);
+    }
+
+    /**
+     * 追踪小程序注册事件
+     */
+    public trackMPRegister(properties: MPRegisterProperties = {}): void {
+        if (!this.isEnabled) {
+            console.log('[AnalyticsManager] 埋点已禁用,跳过 $MPRegister 事件');
+            return;
+        }
+
+        const payload = {
+            register_time: properties.register_time || Date.now(),
+            scene: properties.scene || 0,
+            query: properties.query || '',
+            ...properties
+        };
+
+        console.log('[AnalyticsManager] 追踪小程序注册事件:', payload);
+        this.track('$MPRegister', payload);
+    }
+
+    /**
+     * 追踪小程序登录事件
+     */
+    public trackMPLogin(properties: MPLoginProperties = {}): void {
+        if (!this.isEnabled) {
+            console.log('[AnalyticsManager] 埋点已禁用,跳过 $MPLogin 事件');
+            return;
+        }
+
+        const payload = {
+            login_time: properties.login_time || Date.now(),
+            scene: properties.scene || 0,
+            query: properties.query || '',
+            ...properties
+        };
+
+        console.log('[AnalyticsManager] 追踪小程序登录事件:', payload);
+        this.track('$MPLogin', payload);
+    }
+
+    /**
+     * 追踪小程序登出事件
+     */
+    public trackMPLogout(properties: MPLogoutProperties = {}): void {
+        if (!this.isEnabled) {
+            console.log('[AnalyticsManager] 埋点已禁用,跳过 $MPLogout 事件');
+            return;
+        }
+
+        const payload = {
+            logout_time: properties.logout_time || Date.now(),
+            scene: properties.scene || 0,
+            ...properties
+        };
+
+        console.log('[AnalyticsManager] 追踪小程序登出事件:', payload);
+        this.track('$MPLogout', payload);
+    }
+
+    /**
+     * 追踪创建角色事件
+     */
+    public trackCreateRole(properties: CreateRoleProperties): void {
+        if (!this.isEnabled) {
+            console.log('[AnalyticsManager] 埋点已禁用,跳过 $CreateRole 事件');
+            return;
+        }
+
+        const payload = {
+            role_id: properties.role_id,
+            role_name: properties.role_name || '',
+            role_class: properties.role_class || '',
+            create_time: properties.create_time || Date.now(),
+            user_level: properties.user_level || 1,
+            ...properties
+        };
+
+        console.log('[AnalyticsManager] 追踪创建角色事件:', payload);
+        this.track('$CreateRole', payload);
+    }
+
+    // ==================== 私有方法 ====================
+
+    /**
+     * 生成会话ID
+     */
+    private generateSessionId(): string {
+        return 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
+    }
+
+    /**
+     * 获取用户ID
+     */
+    private getUserId(): string {
+        // 尝试从本地存储获取用户ID,如果没有则生成新的
+        let userId = localStorage.getItem('analytics_user_id');
+        if (!userId) {
+            userId = 'user_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
+            localStorage.setItem('analytics_user_id', userId);
+        }
+        return userId;
+    }
+
+    /**
+     * 初始化设备信息
+     */
+    private initDeviceInfo(): void {
+        this.deviceInfo = {
+            platform: 'web',
+            user_agent: navigator.userAgent,
+            screen_width: screen.width,
+            screen_height: screen.height,
+            language: navigator.language,
+            timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
+        };
+
+        // 如果是微信小游戏环境,获取更多信息
+        if (typeof wx !== 'undefined') {
+            this.deviceInfo.platform = 'wechat_game';
+            // 可以调用wx.getSystemInfo等API获取更详细的设备信息
+        }
+    }
+    /**
+     * 发送数据到服务器
+     */
+    private sendToServer(eventData: any): void {
+        // 实际项目中应该发送到服务器
+        // 这里只是模拟发送过程
+        
+        // 示例:使用fetch发送到服务器
+        /*
+        fetch('/api/analytics/track', {
+            method: 'POST',
+            headers: {
+                'Content-Type': 'application/json',
+            },
+            body: JSON.stringify(eventData)
+        }).catch(error => {
+            console.error('[AnalyticsManager] 发送埋点数据失败:', error);
+        });
+        */
+        
+        // 或者集成第三方SDK
+        // 例如:wx.reportAnalytics(eventName, eventData);
+    }
+}
+
+// 导出单例实例,方便全局使用
+export const Analytics = AnalyticsManager.getInstance();

+ 9 - 0
assets/scripts/Utils/AnalyticsManager.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "abbce601-63b9-4257-9e91-066f1dcd0ed0",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 270 - 0
assets/scripts/Utils/AnalyticsTest.ts

@@ -0,0 +1,270 @@
+/**
+ * 埋点功能测试脚本
+ * 用于验证埋点管理器的各项功能是否正常工作
+ */
+
+import { _decorator, Component, Node, Label, Button } from 'cc';
+import { Analytics } from './AnalyticsManager';
+import { MPLifecycle } from './MPLifecycleManager';
+
+const { ccclass, property } = _decorator;
+
+@ccclass('AnalyticsTest')
+export class AnalyticsTest extends Component {
+    @property(Label)
+    statusLabel: Label = null;
+
+    @property(Button)
+    testButton: Button = null;
+
+    private testResults: string[] = [];
+
+    start() {
+        this.updateStatus('埋点测试准备就绪');
+        
+        if (this.testButton) {
+            this.testButton.node.on('click', this.runAllTests, this);
+        }
+    }
+
+    /**
+     * 运行所有测试
+     */
+    public runAllTests(): void {
+        this.testResults = [];
+        this.updateStatus('开始运行埋点测试...');
+
+        // 测试基础埋点功能
+        this.testBasicTracking();
+        
+        // 测试小程序生命周期事件
+        this.testMPLifecycleEvents();
+        
+        // 测试场景加载事件
+        this.testSceneLoadEvents();
+        
+        // 测试游戏进度事件
+        this.testGameProgressEvents();
+        
+        // 显示测试结果
+        this.showTestResults();
+    }
+
+    /**
+     * 测试基础埋点功能
+     */
+    private testBasicTracking(): void {
+        try {
+            // 测试自定义事件
+            Analytics.track('test_custom_event', {
+                test_property: 'test_value',
+                test_number: 123,
+                test_boolean: true
+            });
+            
+            this.addTestResult('✅ 基础埋点功能测试通过');
+        } catch (error) {
+            this.addTestResult('❌ 基础埋点功能测试失败: ' + error.message);
+        }
+    }
+
+    /**
+     * 测试小程序生命周期事件
+     */
+    private testMPLifecycleEvents(): void {
+        try {
+            // 测试启动事件
+            Analytics.trackMPLaunch({
+                scene: 1001,
+                query: '{"test": "value"}',
+                shareTicket: 'test_ticket'
+            });
+            
+            // 测试显示事件
+            Analytics.trackMPShow({
+                scene: 1001,
+                query: '{"test": "value"}'
+            });
+            
+            // 测试隐藏事件
+            Analytics.trackMPHide();
+            
+            this.addTestResult('✅ 小程序生命周期事件测试通过');
+        } catch (error) {
+            this.addTestResult('❌ 小程序生命周期事件测试失败: ' + error.message);
+        }
+    }
+
+    /**
+     * 测试场景加载事件
+     */
+    private testSceneLoadEvents(): void {
+        try {
+            // 测试场景加载开始
+            Analytics.trackSceneLoadStart('TestScene');
+            
+            // 模拟加载时间
+            setTimeout(() => {
+                // 测试场景加载成功
+                Analytics.trackSceneLoaded({
+                    scene_name: 'TestScene',
+                    load_time: 1500,
+                    success: true
+                });
+                
+                // 测试场景加载失败
+                Analytics.trackSceneLoaded({
+                    scene_name: 'FailedScene',
+                    load_time: 3000,
+                    success: false,
+                    error_msg: 'Scene not found'
+                });
+                
+                // 测试场景卸载
+                Analytics.trackSceneUnloaded('TestScene');
+            }, 100);
+            
+            this.addTestResult('✅ 场景加载事件测试通过');
+        } catch (error) {
+            this.addTestResult('❌ 场景加载事件测试失败: ' + error.message);
+        }
+    }
+
+    /**
+     * 测试游戏进度事件
+     */
+    private testGameProgressEvents(): void {
+        try {
+            // 测试完成关卡
+            Analytics.trackCompleteSection({
+                level: 5,
+                score: 8500,
+                time_spent: 120,
+                result: 'success'
+            });
+            
+            // 测试等级提升
+            Analytics.trackUpdateLevel(4, 5);
+            
+            // 测试新手引导
+            Analytics.trackTutorialStart();
+            
+            setTimeout(() => {
+                Analytics.trackTutorialFinish(300);
+            }, 50);
+            
+            this.addTestResult('✅ 游戏进度事件测试通过');
+        } catch (error) {
+            this.addTestResult('❌ 游戏进度事件测试失败: ' + error.message);
+        }
+    }
+
+    /**
+     * 测试小程序生命周期管理器
+     */
+    private testMPLifecycleManager(): void {
+        try {
+            // 测试手动触发显示事件
+            MPLifecycle.triggerShow({
+                scene: 1001,
+                query: '{"manual": "test"}'
+            });
+            
+            // 测试手动触发隐藏事件
+            MPLifecycle.triggerHide();
+            
+            // 测试获取会话时长
+            const sessionDuration = MPLifecycle.getSessionDuration();
+            console.log('[AnalyticsTest] 当前会话时长:', sessionDuration);
+            
+            this.addTestResult('✅ 小程序生命周期管理器测试通过');
+        } catch (error) {
+            this.addTestResult('❌ 小程序生命周期管理器测试失败: ' + error.message);
+        }
+    }
+
+    /**
+     * 添加测试结果
+     */
+    private addTestResult(result: string): void {
+        this.testResults.push(result);
+        console.log('[AnalyticsTest]', result);
+    }
+
+    /**
+     * 显示测试结果
+     */
+    private showTestResults(): void {
+        const summary = this.testResults.join('\n');
+        const passedCount = this.testResults.filter(r => r.indexOf('✅') !== -1).length;
+        const failedCount = this.testResults.filter(r => r.indexOf('❌') !== -1).length;
+        
+        const finalStatus = `埋点测试完成\n通过: ${passedCount} 项\n失败: ${failedCount} 项\n\n${summary}`;
+        
+        this.updateStatus(finalStatus);
+        
+        console.log('[AnalyticsTest] 测试完成');
+        console.log('[AnalyticsTest] 通过:', passedCount, '项');
+        console.log('[AnalyticsTest] 失败:', failedCount, '项');
+    }
+
+    /**
+     * 更新状态显示
+     */
+    private updateStatus(status: string): void {
+        if (this.statusLabel) {
+            this.statusLabel.string = status;
+        }
+        console.log('[AnalyticsTest]', status);
+    }
+
+    /**
+     * 单独测试某个功能(供外部调用)
+     */
+    public testSpecificFeature(featureName: string): void {
+        switch (featureName) {
+            case 'basic':
+                this.testBasicTracking();
+                break;
+            case 'lifecycle':
+                this.testMPLifecycleEvents();
+                break;
+            case 'scene':
+                this.testSceneLoadEvents();
+                break;
+            case 'progress':
+                this.testGameProgressEvents();
+                break;
+            case 'manager':
+                this.testMPLifecycleManager();
+                break;
+            default:
+                console.warn('[AnalyticsTest] 未知的测试功能:', featureName);
+        }
+    }
+}
+
+// 导出测试工具函数,供控制台调用
+(window as any).testAnalytics = {
+    // 快速测试所有功能
+    runAll: () => {
+        const testComponent = new AnalyticsTest();
+        testComponent.runAllTests();
+    },
+    
+    // 测试特定功能
+    test: (feature: string) => {
+        const testComponent = new AnalyticsTest();
+        testComponent.testSpecificFeature(feature);
+    },
+    
+    // 手动发送测试事件
+    sendTestEvent: (eventName: string, properties: any = {}) => {
+        Analytics.track(eventName, properties);
+    },
+    
+    // 获取会话时长
+    getSessionDuration: () => {
+        return MPLifecycle.getSessionDuration();
+    }
+};

+ 9 - 0
assets/scripts/Utils/AnalyticsTest.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "caeec8c3-e185-4e86-94d7-e8a1d5dac9c8",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 243 - 0
assets/scripts/Utils/AnalyticsTestSimple.ts

@@ -0,0 +1,243 @@
+/**
+ * 简化版埋点功能测试脚本
+ * 不依赖Cocos Creator组件,可以在控制台直接运行
+ */
+
+import { Analytics } from './AnalyticsManager';
+import { MPLifecycle } from './MPLifecycleManager';
+
+export class AnalyticsTestSimple {
+    private testResults: string[] = [];
+
+    /**
+     * 运行所有测试
+     */
+    public runAllTests(): void {
+        this.testResults = [];
+        console.log('[AnalyticsTest] 开始运行埋点测试...');
+
+        // 测试基础埋点功能
+        this.testBasicTracking();
+        
+        // 测试小程序生命周期事件
+        this.testMPLifecycleEvents();
+        
+        // 测试场景加载事件
+        this.testSceneLoadEvents();
+        
+        // 测试游戏进度事件
+        this.testGameProgressEvents();
+        
+        // 显示测试结果
+        this.showTestResults();
+    }
+
+    /**
+     * 测试基础埋点功能
+     */
+    private testBasicTracking(): void {
+        try {
+            // 测试自定义事件
+            Analytics.track('test_custom_event', {
+                test_property: 'test_value',
+                test_number: 123,
+                test_boolean: true
+            });
+            
+            this.addTestResult('✅ 基础埋点功能测试通过');
+        } catch (error) {
+            this.addTestResult('❌ 基础埋点功能测试失败: ' + error.message);
+        }
+    }
+
+    /**
+     * 测试小程序生命周期事件
+     */
+    private testMPLifecycleEvents(): void {
+        try {
+            // 测试启动事件
+            Analytics.trackMPLaunch({
+                scene: 1001,
+                query: '{"test": "value"}',
+                shareTicket: 'test_ticket'
+            });
+            
+            // 测试显示事件
+            Analytics.trackMPShow({
+                scene: 1001,
+                query: '{"test": "value"}'
+            });
+            
+            // 测试隐藏事件
+            Analytics.trackMPHide();
+            
+            this.addTestResult('✅ 小程序生命周期事件测试通过');
+        } catch (error) {
+            this.addTestResult('❌ 小程序生命周期事件测试失败: ' + error.message);
+        }
+    }
+
+    /**
+     * 测试场景加载事件
+     */
+    private testSceneLoadEvents(): void {
+        try {
+            // 测试场景加载开始
+            Analytics.trackSceneLoadStart('TestScene');
+            
+            // 测试场景加载成功
+            Analytics.trackSceneLoaded({
+                scene_name: 'TestScene',
+                load_time: 1500,
+                success: true
+            });
+            
+            // 测试场景加载失败
+            Analytics.trackSceneLoaded({
+                scene_name: 'FailedScene',
+                load_time: 3000,
+                success: false,
+                error_msg: 'Scene not found'
+            });
+            
+            // 测试场景卸载
+            Analytics.trackSceneUnloaded('TestScene');
+            
+            this.addTestResult('✅ 场景加载事件测试通过');
+        } catch (error) {
+            this.addTestResult('❌ 场景加载事件测试失败: ' + error.message);
+        }
+    }
+
+    /**
+     * 测试游戏进度事件
+     */
+    private testGameProgressEvents(): void {
+        try {
+            // 测试完成关卡
+            Analytics.trackCompleteSection({
+                level: 5,
+                score: 8500,
+                time_spent: 120,
+                result: 'success'
+            });
+            
+            // 测试等级提升
+            Analytics.trackUpdateLevel(4, 5);
+            
+            // 测试新手引导
+            Analytics.trackTutorialStart();
+            Analytics.trackTutorialFinish(300);
+            
+            this.addTestResult('✅ 游戏进度事件测试通过');
+        } catch (error) {
+            this.addTestResult('❌ 游戏进度事件测试失败: ' + error.message);
+        }
+    }
+
+    /**
+     * 测试小程序生命周期管理器
+     */
+    private testMPLifecycleManager(): void {
+        try {
+            // 测试手动触发显示事件
+            MPLifecycle.triggerShow({
+                scene: 1001,
+                query: '{"manual": "test"}'
+            });
+            
+            // 测试手动触发隐藏事件
+            MPLifecycle.triggerHide();
+            
+            // 测试获取会话时长
+            const sessionDuration = MPLifecycle.getSessionDuration();
+            console.log('[AnalyticsTest] 当前会话时长:', sessionDuration);
+            
+            this.addTestResult('✅ 小程序生命周期管理器测试通过');
+        } catch (error) {
+            this.addTestResult('❌ 小程序生命周期管理器测试失败: ' + error.message);
+        }
+    }
+
+    /**
+     * 添加测试结果
+     */
+    private addTestResult(result: string): void {
+        this.testResults.push(result);
+        console.log('[AnalyticsTest]', result);
+    }
+
+    /**
+     * 显示测试结果
+     */
+    private showTestResults(): void {
+        const summary = this.testResults.join('\n');
+        const passedCount = this.testResults.filter(r => r.indexOf('✅') !== -1).length;
+        const failedCount = this.testResults.filter(r => r.indexOf('❌') !== -1).length;
+        
+        const finalStatus = `埋点测试完成\n通过: ${passedCount} 项\n失败: ${failedCount} 项\n\n${summary}`;
+        
+        console.log('[AnalyticsTest] 测试完成');
+        console.log('[AnalyticsTest] 通过:', passedCount, '项');
+        console.log('[AnalyticsTest] 失败:', failedCount, '项');
+        console.log('[AnalyticsTest] 详细结果:\n', finalStatus);
+    }
+
+    /**
+     * 单独测试某个功能(供外部调用)
+     */
+    public testSpecificFeature(featureName: string): void {
+        switch (featureName) {
+            case 'basic':
+                this.testBasicTracking();
+                break;
+            case 'lifecycle':
+                this.testMPLifecycleEvents();
+                break;
+            case 'scene':
+                this.testSceneLoadEvents();
+                break;
+            case 'progress':
+                this.testGameProgressEvents();
+                break;
+            case 'manager':
+                this.testMPLifecycleManager();
+                break;
+            default:
+                console.warn('[AnalyticsTest] 未知的测试功能:', featureName);
+        }
+    }
+}
+
+// 创建全局测试实例
+const testInstance = new AnalyticsTestSimple();
+
+// 导出测试工具函数,供控制台调用
+(window as any).testAnalytics = {
+    // 快速测试所有功能
+    runAll: () => {
+        testInstance.runAllTests();
+    },
+    
+    // 测试特定功能
+    test: (feature: string) => {
+        testInstance.testSpecificFeature(feature);
+    },
+    
+    // 手动发送测试事件
+    sendTestEvent: (eventName: string, properties: any = {}) => {
+        Analytics.track(eventName, properties);
+    },
+    
+    // 获取会话时长
+    getSessionDuration: () => {
+        return MPLifecycle.getSessionDuration();
+    },
+    
+    // 初始化埋点系统
+    init: () => {
+        Analytics.init();
+        MPLifecycle.init();
+        console.log('[AnalyticsTest] 埋点系统初始化完成');
+    }
+};

+ 9 - 0
assets/scripts/Utils/AnalyticsTestSimple.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "1ad535b4-aa67-4043-a3d5-34d751754df9",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 92 - 0
assets/scripts/Utils/BlockMergeTrackingGuide.md

@@ -0,0 +1,92 @@
+# 合成等级埋点功能使用指南
+
+## 功能概述
+
+本功能实现了对方块合成等级的追踪,专门用于统计:
+- 最高合成等级到达2级的人数和次数
+- 最高合成等级到达3级的人数和次数  
+- 最高合成等级到达4级的人数和次数
+
+## 实现位置
+
+### 1. 埋点管理器 (AnalyticsManager.ts)
+- 新增 `BlockMergeProperties` 接口定义合成事件属性
+- 新增 `trackBlockMergeLevel()` 主要追踪方法
+- 新增 `trackMergeLevel2()`, `trackMergeLevel3()`, `trackMergeLevel4()` 具体等级追踪方法
+
+### 2. 方块管理器 (BlockManager.ts)
+- 在 `performMerge()` 方法中集成埋点调用
+- 当稀有度升级到 uncommon(2级)、rare(3级)、epic(4级) 时自动触发埋点
+
+### 3. 测试模块 (BlockMergeTrackingTest.ts)
+- 提供完整的测试套件验证埋点功能
+- 支持控制台直接调用测试函数
+
+## 稀有度等级映射
+
+```
+common -> 1级 (不追踪)
+uncommon -> 2级 (追踪)
+rare -> 3级 (追踪)
+epic -> 4级 (追踪)
+```
+
+## 事件属性
+
+每个合成埋点事件包含以下属性:
+- `merge_level`: 合成等级 (2, 3, 4)
+- `weapon_type`: 武器类型 (如 'pea_shooter', 'sharp_carrot' 等)
+- `shape_type`: 方块形状 (如 'I', 'L', 'S', 'H-I', 'D-T')
+- `session_id`: 会话ID,用于区分不同用户
+
+## 测试方法
+
+### 1. 控制台测试
+在浏览器控制台中执行以下命令:
+
+```javascript
+// 运行完整测试套件
+testBlockMergeTracking();
+
+// 单独测试各等级
+testMergeLevel2();
+testMergeLevel3();
+testMergeLevel4();
+```
+
+### 2. 游戏内测试
+1. 启动游戏进入关卡
+2. 放置相同武器和形状的方块到网格中
+3. 让方块重叠触发合成
+4. 观察控制台输出的埋点日志
+
+### 3. 验证要点
+- 检查控制台是否输出埋点追踪日志
+- 确认合成等级、武器类型、形状类型信息正确
+- 验证只有2-4级合成会触发埋点(1级不会)
+- 确认同一用户多次合成会正确累计次数
+
+## 日志输出示例
+
+正常合成时会看到类似日志:
+```
+[BlockManager] 合成成功,稀有度升级: common -> uncommon
+[BlockManager] 埋点追踪: 合成等级2, 武器=pea_shooter, 形状=I
+[Analytics] 追踪合成等级2: 武器=pea_shooter, 形状=I
+```
+
+## 注意事项
+
+1. 只追踪2-4级合成,1级合成不会触发埋点
+2. 埋点会自动获取当前方块的武器类型和形状信息
+3. 每次合成成功都会触发一次埋点事件
+4. 系统会自动处理用户去重和次数统计
+5. 如果获取武器或形状信息失败,会使用 'unknown' 作为默认值
+
+## 数据统计
+
+通过埋点数据可以分析:
+- 各等级合成的达成率
+- 不同武器类型的合成偏好
+- 不同形状的合成难度
+- 用户合成行为模式

+ 11 - 0
assets/scripts/Utils/BlockMergeTrackingGuide.md.meta

@@ -0,0 +1,11 @@
+{
+  "ver": "1.0.1",
+  "importer": "text",
+  "imported": true,
+  "uuid": "1d1558b2-e855-4567-89c0-958f19f1ef19",
+  "files": [
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

+ 143 - 0
assets/scripts/Utils/BlockMergeTrackingTest.ts

@@ -0,0 +1,143 @@
+import { _decorator, Component, Node } from 'cc';
+import { Analytics } from './AnalyticsManager';
+
+const { ccclass, property } = _decorator;
+
+/**
+ * 合成等级埋点测试类
+ * 用于测试合成等级追踪功能是否正常工作
+ */
+@ccclass('BlockMergeTrackingTest')
+export class BlockMergeTrackingTest extends Component {
+
+    /**
+     * 测试所有合成等级的埋点功能
+     */
+    public static testAllMergeLevels(): void {
+        console.log('[BlockMergeTrackingTest] 开始测试合成等级埋点功能...');
+        
+        // 测试2级合成
+        this.testMergeLevel2();
+        
+        // 测试3级合成
+        this.testMergeLevel3();
+        
+        // 测试4级合成
+        this.testMergeLevel4();
+        
+        console.log('[BlockMergeTrackingTest] 合成等级埋点测试完成');
+    }
+
+    /**
+     * 测试2级合成埋点
+     */
+    public static testMergeLevel2(): void {
+        console.log('[BlockMergeTrackingTest] 测试2级合成埋点...');
+        
+        // 模拟不同武器和形状的2级合成
+        Analytics.trackBlockMergeLevel(2, 'pea_shooter', 'I');
+        Analytics.trackBlockMergeLevel(2, 'sharp_carrot', 'L');
+        Analytics.trackBlockMergeLevel(2, 'saw_grass', 'S');
+        
+        console.log('[BlockMergeTrackingTest] 2级合成埋点测试完成');
+    }
+
+    /**
+     * 测试3级合成埋点
+     */
+    public static testMergeLevel3(): void {
+        console.log('[BlockMergeTrackingTest] 测试3级合成埋点...');
+        
+        // 模拟不同武器和形状的3级合成
+        Analytics.trackBlockMergeLevel(3, 'watermelon_bomb', 'H-I');
+        Analytics.trackBlockMergeLevel(3, 'boomerang_plant', 'D-T');
+        Analytics.trackBlockMergeLevel(3, 'hot_pepper', 'I');
+        
+        console.log('[BlockMergeTrackingTest] 3级合成埋点测试完成');
+    }
+
+    /**
+     * 测试4级合成埋点
+     */
+    public static testMergeLevel4(): void {
+        console.log('[BlockMergeTrackingTest] 测试4级合成埋点...');
+        
+        // 模拟不同武器和形状的4级合成
+        Analytics.trackBlockMergeLevel(4, 'cactus_shotgun', 'L');
+        Analytics.trackBlockMergeLevel(4, 'okra_missile', 'S');
+        Analytics.trackBlockMergeLevel(4, 'mace_club', 'I');
+        
+        console.log('[BlockMergeTrackingTest] 4级合成埋点测试完成');
+    }
+
+    /**
+     * 测试同一用户多次达成相同等级的情况
+     */
+    public static testMultipleMergesForSameUser(): void {
+        console.log('[BlockMergeTrackingTest] 测试同一用户多次合成相同等级...');
+        
+        // 模拟同一用户多次达成2级合成
+        for (let i = 0; i < 5; i++) {
+            Analytics.trackBlockMergeLevel(2, 'pea_shooter', 'I');
+        }
+        
+        // 模拟同一用户多次达成3级合成
+        for (let i = 0; i < 3; i++) {
+            Analytics.trackBlockMergeLevel(3, 'sharp_carrot', 'L');
+        }
+        
+        // 模拟同一用户多次达成4级合成
+        for (let i = 0; i < 2; i++) {
+            Analytics.trackBlockMergeLevel(4, 'watermelon_bomb', 'S');
+        }
+        
+        console.log('[BlockMergeTrackingTest] 多次合成测试完成');
+    }
+
+    /**
+     * 测试边界情况
+     */
+    public static testEdgeCases(): void {
+        console.log('[BlockMergeTrackingTest] 测试边界情况...');
+        
+        // 测试无效等级(应该被忽略)
+        Analytics.trackBlockMergeLevel(1, 'pea_shooter', 'I'); // 1级不应该被追踪
+        Analytics.trackBlockMergeLevel(5, 'pea_shooter', 'I'); // 5级不存在
+        
+        // 测试未知武器和形状
+        Analytics.trackBlockMergeLevel(2, 'unknown_weapon', 'unknown_shape');
+        
+        console.log('[BlockMergeTrackingTest] 边界情况测试完成');
+    }
+
+    /**
+     * 运行完整的测试套件
+     */
+    public static runFullTestSuite(): void {
+        console.log('=== 合成等级埋点测试套件开始 ===');
+        
+        this.testAllMergeLevels();
+        this.testMultipleMergesForSameUser();
+        this.testEdgeCases();
+        
+        console.log('=== 合成等级埋点测试套件完成 ===');
+        console.log('请检查控制台输出,确认所有埋点事件都已正确发送');
+    }
+}
+
+// 全局测试函数,可在控制台直接调用
+(window as any).testBlockMergeTracking = () => {
+    BlockMergeTrackingTest.runFullTestSuite();
+};
+
+(window as any).testMergeLevel2 = () => {
+    BlockMergeTrackingTest.testMergeLevel2();
+};
+
+(window as any).testMergeLevel3 = () => {
+    BlockMergeTrackingTest.testMergeLevel3();
+};
+
+(window as any).testMergeLevel4 = () => {
+    BlockMergeTrackingTest.testMergeLevel4();
+};

+ 9 - 0
assets/scripts/Utils/BlockMergeTrackingTest.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "837b587b-e644-43c6-8c62-cce10d90886c",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 212 - 0
assets/scripts/Utils/MPLifecycleManager.ts

@@ -0,0 +1,212 @@
+/**
+ * 小程序生命周期管理器
+ * 负责监听小程序的显示、隐藏等生命周期事件,并触发相应的埋点
+ */
+
+import { Analytics } from './AnalyticsManager';
+
+export class MPLifecycleManager {
+    private static instance: MPLifecycleManager;
+    private isInitialized: boolean = false;
+    private showTime: number = 0;
+    private hideTime: number = 0;
+
+    private constructor() {}
+
+    /**
+     * 获取单例实例
+     */
+    public static getInstance(): MPLifecycleManager {
+        if (!MPLifecycleManager.instance) {
+            MPLifecycleManager.instance = new MPLifecycleManager();
+        }
+        return MPLifecycleManager.instance;
+    }
+
+    /**
+     * 初始化小程序生命周期监听
+     */
+    public init(): void {
+        if (this.isInitialized) {
+            return;
+        }
+
+        console.log('[MPLifecycleManager] 初始化小程序生命周期监听');
+
+        // 如果是微信小游戏环境,注册生命周期监听
+        if (typeof wx !== 'undefined') {
+            this.setupWechatListeners();
+        } else {
+            // Web环境下监听页面可见性变化
+            this.setupWebListeners();
+        }
+
+        this.isInitialized = true;
+    }
+
+    /**
+     * 设置微信小游戏生命周期监听
+     */
+    private setupWechatListeners(): void {
+        // 监听小程序显示事件
+        if (wx.onShow) {
+            wx.onShow((options: any) => {
+                console.log('[MPLifecycleManager] 小程序显示', options);
+                this.showTime = Date.now();
+                
+                // 计算后台时长
+                const backgroundTime = this.hideTime > 0 ? this.showTime - this.hideTime : 0;
+                
+                // 追踪显示事件
+                Analytics.trackMPShow({
+                    scene: options.scene || 0,
+                    query: JSON.stringify(options.query || {}),
+                    shareTicket: options.shareTicket || '',
+                    referrerInfo: options.referrerInfo || {}
+                });
+                
+                // 检测是否通过分享进入,如果是则追踪分享事件
+                if (options.shareTicket) {
+                    this.trackMPShareFromShow(options);
+                }
+                
+                // 单独记录后台时长
+                if (backgroundTime > 0) {
+                    Analytics.track('$MPBackgroundTime', {
+                        background_time: backgroundTime
+                    });
+                }
+            });
+        }
+
+        // 监听小程序隐藏事件
+        if (wx.onHide) {
+            wx.onHide(() => {
+                console.log('[MPLifecycleManager] 小程序隐藏');
+                this.hideTime = Date.now();
+                
+                // 计算前台时长
+                const foregroundTime = this.showTime > 0 ? this.hideTime - this.showTime : 0;
+                
+                Analytics.trackMPHide();
+                
+                // 可以添加额外的隐藏事件属性
+                Analytics.track('$MPHide', {
+                    hide_time: this.hideTime,
+                    foreground_time: foregroundTime
+                });
+            });
+        }
+    }
+
+    /**
+     * 追踪通过分享进入的事件
+     */
+    private trackMPShareFromShow(options: any): void {
+        try {
+            Analytics.trackMPShare({
+                share_type: 'show_from_share', // 分享类型:从分享显示
+                share_target: 'unknown', // 分享目标:未知(显示时无法确定)
+                share_content: options.shareTicket || '', // 分享内容:shareTicket
+                share_time: Date.now(), // 分享时间戳
+                scene: options.scene || 0, // 显示场景
+                query: JSON.stringify(options.query || {}) // 显示参数
+            });
+            console.log('[MPLifecycleManager] $MPShare 事件已上报(从分享显示):', options);
+        } catch (error) {
+            console.error('[MPLifecycleManager] 追踪分享显示事件时出错:', error);
+        }
+    }
+
+    /**
+     * 设置Web环境生命周期监听
+     */
+    private setupWebListeners(): void {
+        // 监听页面可见性变化
+        document.addEventListener('visibilitychange', () => {
+            if (document.hidden) {
+                // 页面隐藏
+                console.log('[MPLifecycleManager] 页面隐藏');
+                this.hideTime = Date.now();
+                
+                const foregroundTime = this.showTime > 0 ? this.hideTime - this.showTime : 0;
+                
+                Analytics.track('$MPHide', {
+                    hide_time: this.hideTime,
+                    foreground_time: foregroundTime,
+                    platform: 'web'
+                });
+            } else {
+                // 页面显示
+                console.log('[MPLifecycleManager] 页面显示');
+                this.showTime = Date.now();
+                
+                const backgroundTime = this.hideTime > 0 ? this.showTime - this.hideTime : 0;
+                
+                Analytics.track('$MPShow', {
+                    show_time: this.showTime,
+                    background_time: backgroundTime,
+                    platform: 'web'
+                });
+            }
+        });
+
+        // 监听页面加载完成
+        window.addEventListener('load', () => {
+            console.log('[MPLifecycleManager] 页面加载完成');
+            this.showTime = Date.now();
+            
+            Analytics.track('$MPShow', {
+                show_time: this.showTime,
+                platform: 'web',
+                event_type: 'page_load'
+            });
+        });
+
+        // 监听页面卸载
+        window.addEventListener('beforeunload', () => {
+            console.log('[MPLifecycleManager] 页面即将卸载');
+            this.hideTime = Date.now();
+            
+            const foregroundTime = this.showTime > 0 ? this.hideTime - this.showTime : 0;
+            
+            Analytics.track('$MPHide', {
+                hide_time: this.hideTime,
+                foreground_time: foregroundTime,
+                platform: 'web',
+                event_type: 'page_unload'
+            });
+        });
+    }
+
+    /**
+     * 获取当前会话时长
+     */
+    public getSessionDuration(): number {
+        if (this.showTime === 0) {
+            return 0;
+        }
+        
+        const currentTime = Date.now();
+        return currentTime - this.showTime;
+    }
+
+    /**
+     * 手动触发显示事件(用于特殊场景)
+     */
+    public triggerShow(options: any = {}): void {
+        this.showTime = Date.now();
+        Analytics.trackMPShow(options);
+    }
+
+    /**
+     * 手动触发隐藏事件(用于特殊场景)
+     */
+    public triggerHide(): void {
+        this.hideTime = Date.now();
+        Analytics.trackMPHide();
+    }
+}
+
+// 导出单例实例
+export const MPLifecycle = MPLifecycleManager.getInstance();

+ 9 - 0
assets/scripts/Utils/MPLifecycleManager.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "a0943fdc-82bf-4065-98ec-071ccb45d64b",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 157 - 0
assets/scripts/Utils/埋点测试说明.md

@@ -0,0 +1,157 @@
+# 埋点功能测试说明
+
+## 概述
+
+本文档说明如何测试已实现的埋点功能,确保事件追踪正常工作。
+
+## 已实现的埋点功能
+
+### 1. 埋点管理器 (AnalyticsManager)
+- 基础事件追踪功能
+- 小程序生命周期事件追踪
+- 场景加载事件追踪
+- 游戏进度事件追踪
+- 新手引导事件追踪
+
+### 2. 小程序生命周期管理器 (MPLifecycleManager)
+- 自动监听小程序显示/隐藏事件
+- 自动监听Web页面可见性变化
+- 会话时长统计
+
+### 3. LaunchScreen 集成
+- 启动页面埋点集成
+- 场景加载进度追踪
+- 小程序生命周期事件追踪
+
+## 测试方法
+
+### 方法一:控制台测试(推荐)
+
+1. 在游戏运行时,打开浏览器开发者工具
+2. 在控制台中运行以下命令:
+
+```javascript
+// 初始化埋点系统
+testAnalytics.init();
+
+// 运行所有测试
+testAnalytics.runAll();
+
+// 测试特定功能
+testAnalytics.test('basic');      // 基础功能
+testAnalytics.test('lifecycle');  // 生命周期事件
+testAnalytics.test('scene');      // 场景加载事件
+testAnalytics.test('progress');   // 游戏进度事件
+testAnalytics.test('manager');    // 生命周期管理器
+
+// 手动发送测试事件
+testAnalytics.sendTestEvent('custom_event', {
+    property1: 'value1',
+    property2: 123
+});
+
+// 获取当前会话时长
+testAnalytics.getSessionDuration();
+```
+
+### 方法二:代码集成测试
+
+在需要测试的场景中导入并使用测试类:
+
+```typescript
+import { AnalyticsTestSimple } from './Utils/AnalyticsTestSimple';
+
+// 创建测试实例
+const tester = new AnalyticsTestSimple();
+
+// 运行所有测试
+tester.runAllTests();
+
+// 测试特定功能
+tester.testSpecificFeature('basic');
+```
+
+## 预期的测试结果
+
+### 成功的测试输出示例:
+```
+[AnalyticsTest] 开始运行埋点测试...
+[AnalyticsTest] ✅ 基础埋点功能测试通过
+[AnalyticsTest] ✅ 小程序生命周期事件测试通过
+[AnalyticsTest] ✅ 场景加载事件测试通过
+[AnalyticsTest] ✅ 游戏进度事件测试通过
+[AnalyticsTest] ✅ 小程序生命周期管理器测试通过
+[AnalyticsTest] 测试完成
+[AnalyticsTest] 通过: 5 项
+[AnalyticsTest] 失败: 0 项
+```
+
+### 埋点事件输出示例:
+```
+[Analytics] 发送事件: test_custom_event
+[Analytics] 发送事件: $MPLaunch
+[Analytics] 发送事件: $MPShow
+[Analytics] 发送事件: $MPHide
+[Analytics] 发送事件: $SceneLoadStart
+[Analytics] 发送事件: $SceneLoaded
+[Analytics] 发送事件: $SceneUnloaded
+[Analytics] 发送事件: $CompleteSection
+[Analytics] 发送事件: $UpdateLevel
+[Analytics] 发送事件: $TutorialStart
+[Analytics] 发送事件: $TutorialFinish
+```
+
+## 实际游戏中的埋点验证
+
+### 1. 启动页面埋点
+- 启动游戏时应该看到 `$MPLaunch` 和 `$MPShow` 事件
+- 场景加载时应该看到 `$SceneLoadStart` 和 `$SceneLoaded` 事件
+
+### 2. 小程序生命周期埋点
+- 切换到后台时应该看到 `$MPHide` 事件
+- 回到前台时应该看到 `$MPShow` 事件和 `$MPBackgroundTime` 事件
+
+### 3. 场景切换埋点
+- 场景切换时应该看到 `$SceneUnloaded` 和新场景的 `$SceneLoadStart`、`$SceneLoaded` 事件
+
+## 事件数据表对照
+
+根据 `弹弹守卫战_事件数据表.xlsx` 中的定义,以下事件已实现:
+
+| 事件名 | 实现状态 | 说明 |
+|--------|----------|------|
+| $MPLaunch | ✅ | 小程序启动事件 |
+| $MPShow | ✅ | 小程序显示事件 |
+| $MPHide | ✅ | 小程序隐藏事件 |
+| $SceneLoadStart | ✅ | 场景开始加载事件 |
+| $SceneLoaded | ✅ | 场景加载完成事件 |
+| $SceneUnloaded | ✅ | 场景卸载事件 |
+| $CompleteSection | ✅ | 完成关卡事件 |
+| $UpdateLevel | ✅ | 等级提升事件 |
+| $TutorialStart | ✅ | 新手引导开始事件 |
+| $TutorialFinish | ✅ | 新手引导完成事件 |
+
+## 注意事项
+
+1. **开发环境测试**:当前实现使用控制台输出,实际部署时需要替换为真实的数据上报接口
+2. **数据格式**:所有事件数据都包含设备信息、会话ID、用户ID等基础属性
+3. **错误处理**:埋点系统包含错误处理机制,不会影响游戏正常运行
+4. **性能影响**:埋点操作是异步的,不会阻塞游戏主线程
+
+## 故障排除
+
+### 常见问题:
+
+1. **控制台没有埋点输出**
+   - 检查 `Analytics.setEnabled(true)` 是否被调用
+   - 确认埋点管理器已正确初始化
+
+2. **测试函数不存在**
+   - 确认 `AnalyticsTestSimple.ts` 已被正确加载
+   - 检查 `testAnalytics` 全局对象是否存在
+
+3. **事件属性不正确**
+   - 对照事件数据表检查属性名称和类型
+   - 查看控制台输出的完整事件数据
+
+如有其他问题,请检查浏览器控制台的错误信息。

+ 11 - 0
assets/scripts/Utils/埋点测试说明.md.meta

@@ -0,0 +1,11 @@
+{
+  "ver": "1.0.1",
+  "importer": "text",
+  "imported": true,
+  "uuid": "9bb9e4ac-66d0-4b2d-af1c-92ade12018b3",
+  "files": [
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů