LaunchScreen.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. import { _decorator, Component, Node, director, assetManager, ProgressBar, Label, Sprite, SpriteFrame, resources } from 'cc';
  2. import { Analytics } from './Utils/AnalyticsManager';
  3. import { MPLifecycle } from './Utils/MPLifecycleManager';
  4. import './Utils/BlockMergeTrackingTest';
  5. const { ccclass, property } = _decorator;
  6. // 微信小游戏API类型声明
  7. declare global {
  8. interface Window {
  9. wx?: {
  10. loadSubpackage?: (options: {
  11. name: string;
  12. success?: () => void;
  13. fail?: (err: any) => void;
  14. }) => any;
  15. getLaunchOptionsSync?: () => any;
  16. onShow?: (callback: (options: any) => void) => void;
  17. onHide?: (callback: () => void) => void;
  18. };
  19. }
  20. const wx: {
  21. loadSubpackage?: (options: {
  22. name: string;
  23. success?: () => void;
  24. fail?: (err: any) => void;
  25. }) => any;
  26. getLaunchOptionsSync?: () => any;
  27. onShow?: (callback: (options: any) => void) => void;
  28. onHide?: (callback: () => void) => void;
  29. } | undefined;
  30. }
  31. @ccclass('LaunchScreen')
  32. export class LaunchScreen extends Component {
  33. @property(ProgressBar)
  34. progressBar: ProgressBar = null;
  35. @property(Label)
  36. progressLabel: Label = null;
  37. @property(Sprite)
  38. backgroundSprite: Sprite = null;
  39. @property(SpriteFrame)
  40. launchBackground: SpriteFrame = null;
  41. private totalAssets = 0;
  42. private loadedAssets = 0;
  43. private sceneLoadStartTime = 0;
  44. private launchOptions: any = {};
  45. start() {
  46. console.log('[LaunchScreen] 启动页开始初始化');
  47. // 初始化埋点管理器
  48. Analytics.init();
  49. // 初始化小程序生命周期管理器
  50. MPLifecycle.init();
  51. // 注册与登录埋点
  52. this.setupAuthTracking();
  53. // 追踪小程序启动事件
  54. this.trackMPLaunch();
  55. // 设置启动页背景
  56. this.setupBackground();
  57. // 开始预加载游戏资源
  58. this.preloadGameAssets();
  59. }
  60. /**
  61. * 设置启动页背景
  62. */
  63. private setupBackground() {
  64. if (this.backgroundSprite && this.launchBackground) {
  65. this.backgroundSprite.spriteFrame = this.launchBackground;
  66. console.log('[LaunchScreen] 启动页背景设置完成');
  67. }
  68. }
  69. /**
  70. * 预加载游戏资源
  71. */
  72. private async preloadGameAssets() {
  73. try {
  74. console.log('[LaunchScreen] 开始预加载游戏资源');
  75. // 更新进度显示
  76. this.updateProgress(0, '正在加载游戏资源...');
  77. // 直接预加载GameLevel场景(不使用分包)
  78. await this.preloadGameLevelScene();
  79. // 预加载完成,跳转到游戏场景
  80. this.loadGameScene();
  81. } catch (error) {
  82. console.error('[LaunchScreen] 资源预加载失败:', error);
  83. this.updateProgress(0, '资源加载失败,请重试');
  84. }
  85. }
  86. /**
  87. * 直接预加载GameLevel场景
  88. */
  89. private preloadGameLevelScene(): Promise<void> {
  90. return new Promise<void>((resolve, reject) => {
  91. console.log('[LaunchScreen] 开始预加载GameLevel场景');
  92. // 追踪场景加载开始事件
  93. this.sceneLoadStartTime = Date.now();
  94. Analytics.trackSceneLoadStart('GameLevel');
  95. // 直接预加载场景
  96. director.preloadScene('GameLevel', (finished: number, total: number) => {
  97. const progress = finished / total;
  98. this.updateProgress(progress * 100, `正在加载游戏场景... ${Math.floor(progress * 100)}%`);
  99. }, (err) => {
  100. const loadTime = Date.now() - this.sceneLoadStartTime;
  101. if (err) {
  102. console.error('[LaunchScreen] GameLevel场景预加载失败:', err);
  103. // 追踪场景加载失败事件
  104. Analytics.trackSceneLoaded({
  105. scene_name: 'GameLevel',
  106. load_time: loadTime,
  107. success: false,
  108. error_msg: err.toString()
  109. });
  110. // 预加载失败时,仍然尝试直接跳转(可能场景存在但预加载失败)
  111. console.log('[LaunchScreen] 预加载失败,但仍将尝试直接加载场景');
  112. this.updateProgress(100, '准备进入游戏...');
  113. resolve();
  114. return;
  115. }
  116. console.log('[LaunchScreen] GameLevel场景预加载完成');
  117. // 追踪场景加载成功事件
  118. Analytics.trackSceneLoaded({
  119. scene_name: 'GameLevel',
  120. load_time: loadTime,
  121. success: true
  122. });
  123. this.updateProgress(100, '加载完成!');
  124. resolve();
  125. });
  126. });
  127. }
  128. /**
  129. * 更新加载进度
  130. */
  131. private updateProgress(progress: number, message: string) {
  132. if (this.progressBar) {
  133. this.progressBar.progress = progress / 100;
  134. }
  135. if (this.progressLabel) {
  136. this.progressLabel.string = message;
  137. }
  138. console.log(`[LaunchScreen] ${message} (${progress.toFixed(1)}%)`);
  139. }
  140. /**
  141. * 跳转到游戏场景
  142. */
  143. private loadGameScene() {
  144. console.log('[LaunchScreen] 准备跳转到GameLevel场景');
  145. // 延迟一秒后跳转,让用户看到加载完成的提示
  146. this.scheduleOnce(() => {
  147. // 直接加载GameLevel场景
  148. director.loadScene('GameLevel', (err) => {
  149. if (err) {
  150. console.error('[LaunchScreen] GameLevel场景加载失败:', err);
  151. console.log('[LaunchScreen] 尝试降级方案:停留在启动页面');
  152. this.updateProgress(0, 'GameLevel场景不存在,请检查场景配置');
  153. // 提供重试按钮或其他交互方式
  154. this.scheduleOnce(() => {
  155. this.updateProgress(0, '点击屏幕重试或检查场景配置');
  156. }, 2.0);
  157. return;
  158. }
  159. console.log('[LaunchScreen] 成功切换到GameLevel场景');
  160. });
  161. }, 1.0);
  162. }
  163. // ==================== 埋点相关方法 ====================
  164. /**
  165. * 追踪小程序启动事件
  166. */
  167. private trackMPLaunch(): void {
  168. // 使用已获取的启动参数
  169. const opts = this.launchOptions || {};
  170. Analytics.trackMPLaunch({
  171. scene: opts.scene || 0,
  172. query: JSON.stringify(opts.query || {}),
  173. shareTicket: opts.shareTicket || '',
  174. referrerInfo: opts.referrerInfo || {}
  175. });
  176. // 检测是否通过分享启动,如果是则追踪分享事件
  177. if (opts.shareTicket) {
  178. this.trackMPShareFromLaunch(opts);
  179. }
  180. }
  181. /**
  182. * 追踪通过分享启动的事件
  183. */
  184. private trackMPShareFromLaunch(launchOptions: any): void {
  185. try {
  186. Analytics.trackMPShare({
  187. share_type: 'launch_from_share', // 分享类型:从分享启动
  188. share_target: 'unknown', // 分享目标:未知(启动时无法确定)
  189. share_content: launchOptions.shareTicket || '', // 分享内容:shareTicket
  190. share_time: Date.now(), // 分享时间戳
  191. scene: launchOptions.scene || 0, // 启动场景
  192. query: JSON.stringify(launchOptions.query || {}) // 启动参数
  193. });
  194. console.log('[LaunchScreen] $MPShare 事件已上报(从分享启动):', launchOptions);
  195. } catch (error) {
  196. console.error('[LaunchScreen] 追踪分享启动事件时出错:', error);
  197. }
  198. }
  199. /**
  200. * 设置注册/登录/登出/创建角色埋点
  201. */
  202. private setupAuthTracking(): void {
  203. // 获取并缓存启动参数(微信小游戏环境)
  204. if (typeof wx !== 'undefined' && wx.getLaunchOptionsSync) {
  205. try {
  206. this.launchOptions = wx.getLaunchOptionsSync() || {};
  207. } catch (error) {
  208. console.warn('[LaunchScreen] 获取启动参数失败:', error);
  209. this.launchOptions = {};
  210. }
  211. }
  212. const sceneVal = this.launchOptions?.scene || 0;
  213. const queryStr = JSON.stringify(this.launchOptions?.query || {});
  214. // 首次注册(本地标记判断)
  215. try {
  216. const firstFlag = localStorage.getItem('first_register_tracked');
  217. if (!firstFlag) {
  218. Analytics.trackUserFirstRegister({
  219. register_time: Date.now(),
  220. channel: typeof wx !== 'undefined' ? 'wechat_game' : 'web',
  221. scene: sceneVal,
  222. query: queryStr
  223. });
  224. // 小程序注册事件(首次)
  225. Analytics.trackMPRegister({
  226. register_time: Date.now(),
  227. scene: sceneVal,
  228. query: queryStr
  229. });
  230. // 创建角色(首次运行时认为创建默认角色)
  231. this.trackInitialRoleCreate();
  232. localStorage.setItem('first_register_tracked', '1');
  233. }
  234. } catch (e) {
  235. console.warn('[LaunchScreen] 处理首次注册标记失败:', e);
  236. }
  237. // 小程序登录事件(每次启动)
  238. Analytics.trackMPLogin({
  239. login_time: Date.now(),
  240. scene: sceneVal,
  241. query: queryStr
  242. });
  243. // 小程序登出事件(监听 onHide)
  244. if (typeof wx !== 'undefined' && wx.onHide) {
  245. try {
  246. wx.onHide(() => {
  247. Analytics.trackMPLogout({
  248. logout_time: Date.now(),
  249. scene: sceneVal
  250. });
  251. });
  252. } catch (error) {
  253. console.warn('[LaunchScreen] 注册 onHide 失败:', error);
  254. }
  255. }
  256. }
  257. /**
  258. * 首次运行时创建默认角色并上报
  259. */
  260. private trackInitialRoleCreate(): void {
  261. try {
  262. const roleFlag = localStorage.getItem('default_role_created');
  263. if (!roleFlag) {
  264. Analytics.trackCreateRole({
  265. role_id: 'default_player',
  266. role_name: 'Player',
  267. role_class: 'starter',
  268. create_time: Date.now(),
  269. user_level: 1
  270. });
  271. localStorage.setItem('default_role_created', '1');
  272. console.log('[LaunchScreen] $CreateRole 事件已上报(默认角色创建)');
  273. }
  274. } catch (e) {
  275. console.warn('[LaunchScreen] 创建默认角色标记失败:', e);
  276. }
  277. }
  278. /**
  279. * 组件销毁时的清理工作
  280. */
  281. onDestroy(): void {
  282. // 追踪场景卸载事件
  283. Analytics.trackSceneUnloaded('LaunchScreen');
  284. }
  285. }