drgame-cc 1.0.49 → 1.0.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,4 +39,33 @@ export enum AnalyticsEvent {
39
39
  export enum AnalyticsAction {
40
40
  Exposure = "exposure",
41
41
  Click = "click",
42
+ }
43
+
44
+ /** 事件定义映射(element_position + 曝光/点击事件名) */
45
+ export const AnalyticsEventMap: Record<AnalyticsEvent, { ep: string; expose: string; click?: string }> = {
46
+ [AnalyticsEvent.GameLoading]: { ep: "game_loading", expose: "game_loading_expose" },
47
+ [AnalyticsEvent.StartGameButton]: { ep: "start_game_button", expose: "start_game_expose", click: "start_game_click" },
48
+ [AnalyticsEvent.WatchVideoContinueButton]: { ep: "watch_video_continue_button", expose: "watch_video_continue_expose", click: "watch_video_continue_click" },
49
+ [AnalyticsEvent.ReplayButton]: { ep: "replay_button", expose: "replay_expose", click: "replay_click" },
50
+ [AnalyticsEvent.RewardVideoAd]: { ep: "reward_video_ad", expose: "ad_expose" },
51
+ };
52
+
53
+ /** 奖励模式 */
54
+ export enum AwardModel {
55
+ /** 视频奖励 */
56
+ Video = "video",
57
+ /** 免费 */
58
+ Free = "free",
59
+ /** 付费时间限制 */
60
+ TimeLimit = "time_limit",
61
+ }
62
+
63
+ /** 渠道类型 */
64
+ export enum EAppItem {
65
+ /** 小米 */
66
+ Xiaomi = "25",
67
+ /** 乐窝 */
68
+ LeWo = "29",
69
+ /** 咪咕 */
70
+ Migu = "30",
42
71
  }
@@ -1,12 +1,10 @@
1
- import { AdEvent, AdPlatform, AdSdk, AdType } from "./ad-sdk/ad-sdk";
2
- import { BtnExtra } from "./component/BtnExtra";
1
+ import { AdEvent, AdPlatform, AdSdk, AdType, ActionType, Reporter } from "./ad-sdk/ad-sdk";
2
+ import BtnExtra from "./component/BtnExtra";
3
3
  import HelperWin from "./component/HelperWin";
4
- import { AnalyticsAction, AnalyticsEvent, EVideoScene, FingerStyle } from "./Define";
5
- import FocusVisualLayer from "./focus/FocusVisualLayer";
6
- import GameAnalytics from "./GameAnalytics";
4
+ import { AnalyticsAction, AnalyticsEvent, AnalyticsEventMap, AwardModel, EAppItem, EVideoScene, FingerStyle } from "./Define";
7
5
  import { Loader } from "./Loader";
6
+ import { PlatformUtil } from "./PlatformUtil";
8
7
  import { UIAdapter } from "./UIAdapter";
9
- import { WebBridge } from "./WebBridge";
10
8
 
11
9
  /** 平台适配器,公共 API 入口 */
12
10
  export class PlatformAdapter {
@@ -16,6 +14,10 @@ export class PlatformAdapter {
16
14
  public static back = UIAdapter.back;
17
15
  /** WebBridge */
18
16
  public static get webBridge() { return UIAdapter.webBridge; }
17
+ /** 渠道类型 */
18
+ private static appItem: EAppItem = EAppItem.Xiaomi;
19
+ /** 是否是会员 */
20
+ private static isVIP: boolean = false;
19
21
 
20
22
  private static contentId: string = "";
21
23
 
@@ -26,54 +28,72 @@ export class PlatformAdapter {
26
28
  /** 初始化
27
29
  * @param cnName 中文名称
28
30
  * @param minigameID 小游戏ID
31
+ * @param focusIcon 焦点的样式
29
32
  */
30
33
  public static async init(cnName: string, minigameID: number, focusIcon: FingerStyle) {
31
- FocusVisualLayer.setStyle(focusIcon);
34
+ // ── 初始化渠道类型 ──
35
+ this.appItem = PlatformUtil.getUrlParam('appItem') as EAppItem || EAppItem.Xiaomi;
36
+ this.isVIP = PlatformUtil.getUrlParam('isVIP') === '1';
37
+
38
+ // ── UI 初始化 ──
39
+ this.focus.setFocusStyle(focusIcon);
32
40
  UIAdapter._zhName = cnName;
33
- var platform: AdPlatform = AdPlatform.None;
34
- UIAdapter.webBridge = new WebBridge();
35
- if (UIAdapter.webBridge.isAvailable()) {
36
- } else if (this.isXiaomi) {
37
- platform = AdPlatform.Xiaomi;
38
- }
41
+ await UIAdapter.init();
42
+
39
43
  this.contentId = minigameID.toString();
40
- console.log(`[sdk-version] ${UIAdapter.version}`);
41
- UIAdapter.init({ focusEnabled: true });
42
-
43
- GameAnalytics.init({
44
- app_item: "25",
45
- content_type: "游戏",
46
- content_id: this.contentId,
47
- content_title: cnName,
44
+
45
+ // ── 初始化广告 SDK ──
46
+ await AdSdk.init({
47
+ platform: AdPlatform.Xiaomi,
48
+ adapters: { [AdPlatform.Xiaomi]: { cpId: this.contentId } },
49
+ reporter: {
50
+ app_item: "25",
51
+ content_type: "游戏",
52
+ content_id: this.contentId,
53
+ content_title: cnName,
54
+ },
48
55
  });
49
- if (PlatformAdapter.isCocos) {
50
- UIAdapter.toastPool = new cc.NodePool();
51
- UIAdapter.prefab = await UIAdapter.loadPrefab('drres/prefab/Toast');
52
- }
53
- let adapters: any = {};
54
- adapters[platform] = { cpId: this.contentId };
55
- await AdSdk.init({ platform: AdPlatform.Xiaomi, adapters: adapters });
56
- AdSdk.onGameReady(UIAdapter.version);
56
+
57
+ // ── 注册广告事件 ──
57
58
  if (this.canWatchVideo) {
58
59
  AdSdk.on(AdEvent.Closed, (data: any) => {
59
- if (!data.isCompleted) {
60
- PlatformAdapter.showTip("必须看完视频,才能获取奖励");
61
- }
60
+ if (!data.isCompleted) this.showTip("必须看完视频,才能获取奖励");
62
61
  });
63
- // 监听失败
64
62
  AdSdk.on(AdEvent.Error, (data: any) => {
65
- PlatformAdapter.showTip(`广告暂不可用,请稍后再试(${data.message})`);
63
+ this.showTip(`广告暂不可用,请稍后再试(${data.message})`);
66
64
  });
67
65
  }
68
- // 预加载激励视频广告,让 native 侧提前准备,减少首次拉取失败率
69
- UIAdapter.hookLoadScene();
70
- UIAdapter.back.setFallback(() => UIAdapter.backHandler());
66
+
67
+ AdSdk.onGameReady(UIAdapter.version);
71
68
  }
72
69
 
73
- public static get isXiaomi() {
70
+ // ========================================================================
71
+ // 属性
72
+ // ========================================================================
73
+
74
+ /** 是否 Cocos 环境 */
75
+ private static get isCocos() { return window['cc'] != undefined; }
76
+ /** 是否基座环境 */
77
+ private static get isWebBridge() { return this.webBridge.isAvailable(); }
78
+ /** 是否可以观看视频 */
79
+ private static get canWatchVideo() { return this.isXiaomi; }
80
+ /** 是否小米平台 */
81
+ private static get isXiaomi() {
74
82
  if (typeof window === 'undefined') return null;
75
83
  return window['AppBridge'] || null;
76
84
  }
85
+ /** 是否需要观看视频 */
86
+ private static get needWatchVideo() {
87
+ return this.canWatchVideo && !this.isVIP;
88
+ }
89
+ private static get needTimeLimit() {
90
+ return !this.isVIP && this.appItem === EAppItem.Migu;
91
+ }
92
+
93
+ /** 奖励模式 */
94
+ public static get awardModel(): AwardModel {
95
+ return this.needWatchVideo ? AwardModel.Video : this.needTimeLimit ? AwardModel.TimeLimit : AwardModel.Free;
96
+ }
77
97
 
78
98
  // ========================================================================
79
99
  // Toast
@@ -87,13 +107,6 @@ export class PlatformAdapter {
87
107
  // 广告
88
108
  // ========================================================================
89
109
 
90
- /**
91
- * 显示奖励视频
92
- * @param scene 视频场景
93
- * @param rewardType 奖励类型
94
- * @param callback 回调函数
95
- * @param target 回调函数的this指向
96
- */
97
110
  public static async showRewardVideo(scene: EVideoScene, rewardType: string, callback: Function, target?: any) {
98
111
  let finishFired = false;
99
112
  let fireFinish = () => {
@@ -102,15 +115,14 @@ export class PlatformAdapter {
102
115
  };
103
116
  let onSuccess = () => {
104
117
  try {
105
- PlatformAdapter.report(AnalyticsEvent.RewardVideoAd, AnalyticsAction.Exposure);
118
+ this.report(AnalyticsEvent.RewardVideoAd, AnalyticsAction.Exposure);
106
119
  callback && callback.call(target);
107
120
  } finally {
108
121
  fireFinish();
109
122
  }
110
123
  };
111
124
 
112
- if (PlatformAdapter.canWatchVideo) {
113
- // 监听奖励(用户完整看完广告触发)
125
+ if (this.canWatchVideo) {
114
126
  try {
115
127
  const result = await AdSdk.requestAd({
116
128
  adType: AdType.RewardVideo,
@@ -137,54 +149,34 @@ export class PlatformAdapter {
137
149
  // 按钮注册
138
150
  // ========================================================================
139
151
 
140
- /**
141
- * 注册按钮事件,有 BtnExtra 组件会自动切换按钮图片
142
- * @param button 按钮节点
143
- */
144
152
  public static registerButton(button: cc.Node) {
145
153
  var sprite = button.getComponent(cc.Sprite);
146
154
  var btnExtra = button.getComponent(BtnExtra);
147
155
  var onFocus = () => { sprite.spriteFrame = btnExtra.hoverSprite; };
148
156
  var onRomove = () => { sprite.spriteFrame = btnExtra.normalSprite; };
149
- UIAdapter.focus.register(button, { onFocus: onFocus, onBlur: onRomove });
157
+ this.focus.register(button, { onFocus: onFocus, onBlur: onRomove });
150
158
  }
151
159
 
152
- /**
153
- * 按名称注册按钮
154
- * @param name 按钮名称
155
- * @param parent 查找的父节点,默认场景节点
156
- */
157
160
  public static registerButtonByName(name: string, parent: cc.Node = null) {
158
161
  if (!parent) parent = cc.director.getScene();
159
- let button = PlatformAdapter.findChildByName(parent, name);
160
- PlatformAdapter.registerButton(button);
162
+ let button = this.findChildByName(parent, name);
163
+ this.registerButton(button);
161
164
  }
162
165
 
163
- /**
164
- * 导航到指定名称的按钮
165
- * @param name 按钮名称
166
- * @param parent 查找的父节点,默认场景节点
167
- */
168
166
  public static focusButtonByName(name: string, parent: cc.Node = null) {
169
167
  if (!parent) parent = cc.director.getScene();
170
- let button = PlatformAdapter.findChildByName(parent, name);
171
- UIAdapter.focus.focus(button);
168
+ let button = this.findChildByName(parent, name);
169
+ this.focus.focus(button);
172
170
  }
173
171
 
174
- /** 导航到指定按钮 */
175
172
  public static focusButton(btn: cc.Node) {
176
- UIAdapter.focus.focus(btn);
173
+ this.focus.focus(btn);
177
174
  }
178
175
 
179
- /**
180
- * 递归查找指定名称的子节点
181
- * @param parent 父节点
182
- * @param name 子节点名称
183
- */
184
176
  public static findChildByName(parent: cc.Node, name: string): cc.Node | null {
185
177
  if (parent.name === name) return parent;
186
178
  for (let i = 0; i < parent.children.length; i++) {
187
- let result = PlatformAdapter.findChildByName(parent.children[i], name);
179
+ let result = this.findChildByName(parent.children[i], name);
188
180
  if (result) return result;
189
181
  }
190
182
  return null;
@@ -194,31 +186,20 @@ export class PlatformAdapter {
194
186
  // 场景 / 窗口 栈
195
187
  // ========================================================================
196
188
 
197
- /** 显示预制体场景(压入场景栈,返回键可回溯)
198
- * @param onBack 自定义返回处理,返回 false 表示不关闭,保留当前场景
199
- */
200
189
  public static showScene(scene: cc.Node, onBack?: () => boolean | void) { UIAdapter.showScene(scene, onBack); }
201
- /** 关闭预制体场景(从栈中移除,不销毁) */
202
190
  public static hideScene(scene: cc.Node) { UIAdapter.hideScene(scene); }
203
- /** 打开界面
204
- * @param onBack 自定义返回处理,返回 false 表示不关闭,保留当前窗口
205
- */
206
191
  public static showWindow(window: cc.Node, onBack?: () => boolean | void) { UIAdapter.showWindow(window, onBack); }
207
- /** 关闭界面 */
208
192
  public static hideWindow(window: cc.Node) { UIAdapter.hideWindow(window); }
209
- /** 刷新导航按钮 */
210
193
  public static refreshFocus(window: cc.Node) { UIAdapter.refreshFocus(window); }
211
194
 
212
195
  // ========================================================================
213
196
  // 进度 / 加载
214
197
  // ========================================================================
215
198
 
216
- /** 设置启动页进度条 */
217
199
  public static setProgress(progress: number, tip: string) {
218
200
  if (window["setProgress"]) window["setProgress"](progress, tip);
219
201
  }
220
202
 
221
- /** 关闭启动页 */
222
203
  public static closeLoading() {
223
204
  if (window["closeLoading"]) window["closeLoading"]();
224
205
  }
@@ -227,35 +208,43 @@ export class PlatformAdapter {
227
208
  // 上报
228
209
  // ========================================================================
229
210
 
230
- /**
231
- * 上报事件(缓存)
232
- * @param event 事件类型
233
- * @param action 操作类型,默认点击
234
- * @param adRequestId 广告请求ID
235
- */
236
211
  public static report(event: AnalyticsEvent, action: AnalyticsAction = AnalyticsAction.Click, adRequestId?: string) {
237
- GameAnalytics.report(event, action, adRequestId);
212
+ const def = AnalyticsEventMap[event];
213
+ if (!def) {
214
+ console.warn("[PlatformAdapter] unsupported event", event);
215
+ return;
216
+ }
217
+ const eventName = action === AnalyticsAction.Exposure ? def.expose : def.click;
218
+ if (!eventName) {
219
+ console.warn("[PlatformAdapter] unsupported action", action, "for event", event);
220
+ return;
221
+ }
222
+ const reportItem: any = {
223
+ element_position: def.ep,
224
+ action_type: action === AnalyticsAction.Exposure ? ActionType.Exposure : ActionType.Click,
225
+ event_name: eventName,
226
+ };
227
+ if (event === AnalyticsEvent.RewardVideoAd && adRequestId) {
228
+ reportItem.ad_request_id = adRequestId;
229
+ }
230
+ Reporter.report(reportItem);
238
231
  }
239
232
 
240
- /** 批量上报事件 */
241
233
  public static reportBatch(events: AnalyticsEvent[]) {
242
- GameAnalytics.reportBatch(events);
234
+ if (events.length === 0) return;
235
+ const items: any[] = [];
236
+ for (const e of events) {
237
+ const def = AnalyticsEventMap[e];
238
+ if (!def) continue;
239
+ items.push({
240
+ element_position: def.ep,
241
+ action_type: ActionType.Exposure,
242
+ event_name: def.expose,
243
+ });
244
+ }
245
+ if (items.length > 0) Reporter.reportBatch(items);
243
246
  }
244
247
 
245
- // ========================================================================
246
- // 属性
247
- // ========================================================================
248
-
249
- /** 是否 Cocos 环境 */
250
- public static get isCocos() { return window['cc'] != undefined; }
251
- /** 是否基座环境 */
252
- public static get isWebBridge() { return UIAdapter.webBridge.isAvailable(); }
253
- /** 是否可以观看视频 */
254
- public static get canWatchVideo() { return this.isXiaomi; }
255
- /** 是否 VIP 用户 */
256
- public static get isVIP() { return UIAdapter._isVIP; }
257
-
258
- /** 显示帮助窗口 */
259
248
  public static async showHelperWin(url: string, cb: () => void) {
260
249
  var key = `${UIAdapter._zhName}-isFirst`;
261
250
  var isFirst = cc.sys.localStorage.getItem(key);
@@ -0,0 +1,30 @@
1
+ export class PlatformUtil {
2
+ static isBrowser: boolean = cc.sys.isBrowser;
3
+ private _browserParams: Map<string, string> = new Map();
4
+
5
+ /** 获取浏览器 URL 查询参数 */
6
+ private get browserParams(): Map<string, string> {
7
+ if (this._browserParams.size > 0) return this._browserParams;
8
+
9
+ const query = window.location.search.substring(1);
10
+ const params = new Map<string, string>();
11
+ if (!query) return params;
12
+
13
+ const vars = query.split("&");
14
+ for (let i = 0; i < vars.length; i++) {
15
+ const pair = vars[i].split("=");
16
+ const key = decodeURIComponent(pair[0]);
17
+ const value = decodeURIComponent(pair[1] || "");
18
+ params.set(key, value);
19
+ }
20
+ this._browserParams = params;
21
+ return params;
22
+ }
23
+
24
+ /** 获取指定 URL 参数的值 */
25
+ static getUrlParam(key: string): string | null {
26
+ if (!PlatformUtil.isBrowser) return null;
27
+ const params = new PlatformUtil().browserParams;
28
+ return params.get(key) || null;
29
+ }
30
+ }