@chiyou/minigame-framework 1.3.0 → 1.3.2
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.
- package/package.json +1 -1
- package/src/Framework/Adapter/PlatformAdapter/PlatformAdapterBilibili.ts +28 -2
- package/src/Framework/Adapter/PlatformAdapter/PlatformAdapterDesktopBrowser.ts +8 -1
- package/src/Framework/Adapter/PlatformAdapter/PlatformAdapterDouYin.ts +28 -2
- package/src/Framework/Adapter/PlatformAdapter/PlatformAdapterHonor.ts +31 -5
- package/src/Framework/Adapter/PlatformAdapter/PlatformAdapterHuaWei.ts +34 -1
- package/src/Framework/Adapter/PlatformAdapter/PlatformAdapterKuaiShou.ts +28 -2
- package/src/Framework/Adapter/PlatformAdapter/PlatformAdapterOppo.ts +17 -2
- package/src/Framework/Adapter/PlatformAdapter/PlatformAdapterTapTap.ts +31 -5
- package/src/Framework/Adapter/PlatformAdapter/PlatformAdapterVivo.ts +32 -4
- package/src/Framework/Adapter/PlatformAdapter/PlatformAdapterWeiXin.ts +28 -2
- package/src/Framework/Adapter/PlatformAdapter/PlatformAdapterXiaoMi.ts +31 -5
- package/src/Framework/Adapter/PlatformAdapter/PlatformAdapterZhiFuBao.ts +31 -5
- package/src/Framework/Definition/AnalyticsDefinition.ts +107 -0
- package/src/Framework/Definition/FwkErrorDefinition.ts +18 -2
- package/src/Framework/Definition/SystemDefinition.ts +12 -1
- package/src/Framework/Definition/UIDefinition.ts +6 -0
- package/src/Framework/Manager/AnalyticsMgr.ts +351 -0
- package/src/Framework/Manager/AudioMgr.ts +56 -7
- package/src/Framework/Manager/InputMgr.ts +61 -0
- package/src/Framework/Manager/NodePoolMgr.ts +59 -1
- package/src/Framework/Manager/TimerMgr.ts +49 -0
- package/src/Framework/Manager/UIMgr.ts +75 -9
- package/src/Framework/SDK/Umeng/umtrack-wx-game.ts +107 -0
- package/src/Framework/Utils/LogUtils.ts +27 -0
- package/src/Framework/Utils/ObjectUtils.ts +111 -0
- package/src/index.ts +2 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import { BaseMgr } from "./BaseMgr";
|
|
2
|
+
import { LogUtils } from "../Utils/LogUtils";
|
|
3
|
+
import { ServiceLocator } from "../Utils/ServiceLocator";
|
|
4
|
+
import { FwkErrorCode } from "../Definition/FwkErrorDefinition";
|
|
5
|
+
import { PlatformID } from "../Definition/SystemDefinition";
|
|
6
|
+
import { UmengConfig, EventParams, UserProperties, AnalyticsEventId, AnalyticsParamKey } from "../Definition/AnalyticsDefinition";
|
|
7
|
+
import { UmaInitConfig, UmaSDK, uma as wxUma } from "../SDK/Umeng/umtrack-wx-game";
|
|
8
|
+
// 后续新增平台 SDK 在此导入,例如:
|
|
9
|
+
// import { uma as ttUma } from "../SDK/Umeng/umtrack-tt-game";
|
|
10
|
+
|
|
11
|
+
export class AnalyticsMgr extends BaseMgr {
|
|
12
|
+
/** 单例实例 */
|
|
13
|
+
public static Instance: AnalyticsMgr = null;
|
|
14
|
+
/** 日志标签 */
|
|
15
|
+
static TAG: string = "AnalyticsMgr";
|
|
16
|
+
|
|
17
|
+
/** 是否已初始化 */
|
|
18
|
+
private _isInited: boolean = false;
|
|
19
|
+
/** 是否启用统计 */
|
|
20
|
+
private _isEnabled: boolean = false;
|
|
21
|
+
/** 友盟配置 */
|
|
22
|
+
private _umengConfig: UmengConfig = null;
|
|
23
|
+
/** 当前平台的 appKey */
|
|
24
|
+
private _appKey: string = "";
|
|
25
|
+
/** 当前平台对应的 SDK 实例 */
|
|
26
|
+
private _sdk: UmaSDK = null;
|
|
27
|
+
/** 当前页面名称(用于 trackPageStart/End 配对) */
|
|
28
|
+
private _currentPage: string = "";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 平台 → SDK 实例映射
|
|
32
|
+
* 新增平台时:
|
|
33
|
+
* 1. 在 SDK/Umeng/ 下添加对应的 SDK 文件
|
|
34
|
+
* 2. 在顶部 import 并在此注册
|
|
35
|
+
*/
|
|
36
|
+
private static _sdkMap: Map<PlatformID, UmaSDK> = new Map([
|
|
37
|
+
[PlatformID.ID_MiniGame_WeiXin, wxUma],
|
|
38
|
+
// 新增平台示例:
|
|
39
|
+
// [PlatformID.ID_MiniGame_DouYin, ttUma],
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
onLoad(): void {
|
|
43
|
+
super.onLoad();
|
|
44
|
+
|
|
45
|
+
if (AnalyticsMgr.Instance === null) {
|
|
46
|
+
AnalyticsMgr.Instance = this;
|
|
47
|
+
} else {
|
|
48
|
+
this.destroy();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
ServiceLocator.Instance.register("AnalyticsMgr", this);
|
|
53
|
+
|
|
54
|
+
// 注意:SDK 内部已注册 wx.onShow/onHide 自动调用 resume/pause
|
|
55
|
+
// 无需在 AnalyticsMgr 监听生命周期事件
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
onDestroy(): void {
|
|
59
|
+
super.onDestroy();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 初始化统计管理器
|
|
64
|
+
* @param umengConfig 友盟 SDK 配置
|
|
65
|
+
*
|
|
66
|
+
* 自动判断当前平台是否在 appKeyMap 中配置了 appKey:
|
|
67
|
+
* - 有配置 → 启用统计并初始化 SDK
|
|
68
|
+
* - 无配置 → 禁用统计,跳过初始化
|
|
69
|
+
*/
|
|
70
|
+
public init(umengConfig: UmengConfig): void {
|
|
71
|
+
if (this._isInited) {
|
|
72
|
+
LogUtils.Instance.warn(AnalyticsMgr.TAG, "重复初始化");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!umengConfig) {
|
|
77
|
+
LogUtils.Instance.error(AnalyticsMgr.TAG, FwkErrorCode.Analytics.ConfigError, {
|
|
78
|
+
operation: "init",
|
|
79
|
+
reason: "友盟配置缺失",
|
|
80
|
+
});
|
|
81
|
+
this._isInited = true;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
this._umengConfig = umengConfig;
|
|
86
|
+
|
|
87
|
+
// 根据当前平台是否配置了 appKey 和 SDK 来决定是否启用
|
|
88
|
+
const platformID: PlatformID = BaseMgr.Instance.getCurrentPlatformID();
|
|
89
|
+
const appKey = umengConfig.appKeyMap.get(platformID);
|
|
90
|
+
const sdk = AnalyticsMgr._sdkMap.get(platformID);
|
|
91
|
+
|
|
92
|
+
if (appKey && sdk) {
|
|
93
|
+
this._appKey = appKey;
|
|
94
|
+
this._sdk = sdk;
|
|
95
|
+
this._isEnabled = true;
|
|
96
|
+
this._initUmeng();
|
|
97
|
+
} else {
|
|
98
|
+
this._isEnabled = false;
|
|
99
|
+
if (!sdk) {
|
|
100
|
+
LogUtils.Instance.info(AnalyticsMgr.TAG, "当前平台无友盟SDK,统计功能已禁用", {
|
|
101
|
+
platformID: PlatformID[platformID],
|
|
102
|
+
});
|
|
103
|
+
} else {
|
|
104
|
+
LogUtils.Instance.info(AnalyticsMgr.TAG, "当前平台未配置友盟appKey,统计功能已禁用", {
|
|
105
|
+
platformID: PlatformID[platformID],
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this._isInited = true;
|
|
111
|
+
LogUtils.Instance.info(AnalyticsMgr.TAG, "初始化完成", {
|
|
112
|
+
enabled: this._isEnabled,
|
|
113
|
+
platformID: PlatformID[platformID],
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ==================== 生命周期 ====================
|
|
118
|
+
// 注意:SDK 内部已自动处理 wx.onShow/onHide 的 resume/pause 调用
|
|
119
|
+
|
|
120
|
+
// ==================== 事件上报 ====================
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 上报自定义事件
|
|
124
|
+
* @param eventId 事件 ID
|
|
125
|
+
* @param params 事件参数
|
|
126
|
+
*/
|
|
127
|
+
public trackEvent(eventId: string | AnalyticsEventId, params?: EventParams): void {
|
|
128
|
+
if (!this._isEnabled) return;
|
|
129
|
+
try {
|
|
130
|
+
this._sdk.trackEvent(eventId, params);
|
|
131
|
+
LogUtils.Instance.info(AnalyticsMgr.TAG, "事件上报", {
|
|
132
|
+
eventId: eventId,
|
|
133
|
+
});
|
|
134
|
+
} catch (e) {
|
|
135
|
+
LogUtils.Instance.error(AnalyticsMgr.TAG, FwkErrorCode.Analytics.SDKCallFailed, {
|
|
136
|
+
operation: "trackEvent",
|
|
137
|
+
reason: String(e),
|
|
138
|
+
eventId: eventId,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 页面开始追踪
|
|
145
|
+
* @param pageName 页面名称
|
|
146
|
+
*/
|
|
147
|
+
public trackPageStart(pageName: string): void {
|
|
148
|
+
if (!this._isEnabled) return;
|
|
149
|
+
try {
|
|
150
|
+
this._sdk.trackPageStart(pageName);
|
|
151
|
+
this._currentPage = pageName;
|
|
152
|
+
LogUtils.Instance.info(AnalyticsMgr.TAG, "页面开始", {
|
|
153
|
+
pageName: pageName,
|
|
154
|
+
});
|
|
155
|
+
} catch (e) {
|
|
156
|
+
LogUtils.Instance.error(AnalyticsMgr.TAG, FwkErrorCode.Analytics.SDKCallFailed, {
|
|
157
|
+
operation: "trackPageStart",
|
|
158
|
+
reason: String(e),
|
|
159
|
+
pageName: pageName,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* 页面结束追踪
|
|
166
|
+
* @param pageName 页面名称
|
|
167
|
+
*/
|
|
168
|
+
public trackPageEnd(pageName: string): void {
|
|
169
|
+
if (!this._isEnabled) return;
|
|
170
|
+
try {
|
|
171
|
+
this._sdk.trackPageEnd(pageName);
|
|
172
|
+
this._currentPage = "";
|
|
173
|
+
LogUtils.Instance.info(AnalyticsMgr.TAG, "页面结束", {
|
|
174
|
+
pageName: pageName,
|
|
175
|
+
});
|
|
176
|
+
} catch (e) {
|
|
177
|
+
LogUtils.Instance.error(AnalyticsMgr.TAG, FwkErrorCode.Analytics.SDKCallFailed, {
|
|
178
|
+
operation: "trackPageEnd",
|
|
179
|
+
reason: String(e),
|
|
180
|
+
pageName: pageName,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ==================== 用户追踪 ====================
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 设置用户 ID
|
|
189
|
+
* @param userId 用户 ID
|
|
190
|
+
* @param provider 提供商
|
|
191
|
+
*/
|
|
192
|
+
public setUserId(userId: string, provider?: string): void {
|
|
193
|
+
if (!this._isEnabled) return;
|
|
194
|
+
try {
|
|
195
|
+
this._sdk.setUserid(userId, provider);
|
|
196
|
+
LogUtils.Instance.info(AnalyticsMgr.TAG, "设置用户ID", {
|
|
197
|
+
userId: userId,
|
|
198
|
+
provider: provider,
|
|
199
|
+
});
|
|
200
|
+
} catch (e) {
|
|
201
|
+
LogUtils.Instance.error(AnalyticsMgr.TAG, FwkErrorCode.Analytics.SDKCallFailed, {
|
|
202
|
+
operation: "setUserId",
|
|
203
|
+
reason: String(e),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* 设置 OpenID
|
|
210
|
+
* @param openid 微信 openid
|
|
211
|
+
*/
|
|
212
|
+
public setOpenId(openid: string): void {
|
|
213
|
+
if (!this._isEnabled) return;
|
|
214
|
+
try {
|
|
215
|
+
this._sdk.setOpenid(openid);
|
|
216
|
+
LogUtils.Instance.info(AnalyticsMgr.TAG, "设置OpenID");
|
|
217
|
+
} catch (e) {
|
|
218
|
+
LogUtils.Instance.error(AnalyticsMgr.TAG, FwkErrorCode.Analytics.SDKCallFailed, {
|
|
219
|
+
operation: "setOpenId",
|
|
220
|
+
reason: String(e),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* 设置用户属性
|
|
227
|
+
* @param properties 用户属性
|
|
228
|
+
*/
|
|
229
|
+
public setUserProperties(properties: UserProperties): void {
|
|
230
|
+
if (!this._isEnabled) return;
|
|
231
|
+
try {
|
|
232
|
+
this._sdk.setUserInfo(properties);
|
|
233
|
+
LogUtils.Instance.info(AnalyticsMgr.TAG, "设置用户属性");
|
|
234
|
+
} catch (e) {
|
|
235
|
+
LogUtils.Instance.error(AnalyticsMgr.TAG, FwkErrorCode.Analytics.SDKCallFailed, {
|
|
236
|
+
operation: "setUserProperties",
|
|
237
|
+
reason: String(e),
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ==================== 游戏特定事件 ====================
|
|
243
|
+
|
|
244
|
+
/** 关卡开始 */
|
|
245
|
+
public trackLevelStart(level: string | number, extraParams?: EventParams): void {
|
|
246
|
+
const params: Record<string, any> = {
|
|
247
|
+
[AnalyticsParamKey.Level]: level,
|
|
248
|
+
...this._flattenParams(extraParams),
|
|
249
|
+
};
|
|
250
|
+
this.trackEvent(AnalyticsEventId.LevelStart, params);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** 关卡完成 */
|
|
254
|
+
public trackLevelComplete(level: string | number, score?: number, duration?: number, extraParams?: EventParams): void {
|
|
255
|
+
const params: Record<string, any> = {
|
|
256
|
+
[AnalyticsParamKey.Level]: level,
|
|
257
|
+
...(score !== undefined ? { [AnalyticsParamKey.Score]: score } : {}),
|
|
258
|
+
...(duration !== undefined ? { [AnalyticsParamKey.Duration]: duration } : {}),
|
|
259
|
+
...this._flattenParams(extraParams),
|
|
260
|
+
};
|
|
261
|
+
this.trackEvent(AnalyticsEventId.LevelComplete, params);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** 关卡失败 */
|
|
265
|
+
public trackLevelFail(level: string | number, reason?: string, extraParams?: EventParams): void {
|
|
266
|
+
const params: Record<string, any> = {
|
|
267
|
+
[AnalyticsParamKey.Level]: level,
|
|
268
|
+
...(reason ? { [AnalyticsParamKey.Reason]: reason } : {}),
|
|
269
|
+
...this._flattenParams(extraParams),
|
|
270
|
+
};
|
|
271
|
+
this.trackEvent(AnalyticsEventId.LevelFail, params);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** 广告展示 */
|
|
275
|
+
public trackAdShow(adType: string, adId: string, extraParams?: EventParams): void {
|
|
276
|
+
const params: Record<string, any> = {
|
|
277
|
+
[AnalyticsParamKey.AdType]: adType,
|
|
278
|
+
[AnalyticsParamKey.AdId]: adId,
|
|
279
|
+
...this._flattenParams(extraParams),
|
|
280
|
+
};
|
|
281
|
+
this.trackEvent(AnalyticsEventId.AdShow, params);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** 广告点击 */
|
|
285
|
+
public trackAdClick(adType: string, adId: string, extraParams?: EventParams): void {
|
|
286
|
+
const params: Record<string, any> = {
|
|
287
|
+
[AnalyticsParamKey.AdType]: adType,
|
|
288
|
+
[AnalyticsParamKey.AdId]: adId,
|
|
289
|
+
...this._flattenParams(extraParams),
|
|
290
|
+
};
|
|
291
|
+
this.trackEvent(AnalyticsEventId.AdClick, params);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** 分享 */
|
|
295
|
+
public trackShare(shareType: string, shareContent?: string): void {
|
|
296
|
+
const params: Record<string, any> = {
|
|
297
|
+
[AnalyticsParamKey.ShareType]: shareType,
|
|
298
|
+
...(shareContent ? { [AnalyticsParamKey.ShareType]: shareContent } : {}),
|
|
299
|
+
};
|
|
300
|
+
this.trackEvent(AnalyticsEventId.Share, params);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ==================== 状态查询 ====================
|
|
304
|
+
|
|
305
|
+
/** 是否已初始化 */
|
|
306
|
+
public get isInited(): boolean {
|
|
307
|
+
return this._isInited;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** 是否启用 */
|
|
311
|
+
public get isEnabled(): boolean {
|
|
312
|
+
return this._isEnabled;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** 当前页面 */
|
|
316
|
+
|
|
317
|
+
// ==================== 私有方法 ====================
|
|
318
|
+
|
|
319
|
+
/** 初始化友盟 SDK */
|
|
320
|
+
private _initUmeng(): void {
|
|
321
|
+
try {
|
|
322
|
+
const sdkConfig: UmaInitConfig = {
|
|
323
|
+
appKey: this._appKey,
|
|
324
|
+
useOpenid: this._umengConfig.useOpenid,
|
|
325
|
+
autoGetOpenid: this._umengConfig.autoGetOpenid,
|
|
326
|
+
debug: this._umengConfig.debug,
|
|
327
|
+
};
|
|
328
|
+
this._sdk.init(sdkConfig);
|
|
329
|
+
LogUtils.Instance.info(AnalyticsMgr.TAG, "友盟SDK初始化成功", {
|
|
330
|
+
appKey: this._appKey,
|
|
331
|
+
useOpenid: sdkConfig.useOpenid,
|
|
332
|
+
debug: sdkConfig.debug,
|
|
333
|
+
});
|
|
334
|
+
} catch (e) {
|
|
335
|
+
LogUtils.Instance.error(AnalyticsMgr.TAG, FwkErrorCode.Analytics.SDKInitFailed, {
|
|
336
|
+
operation: "_initUmeng",
|
|
337
|
+
reason: String(e),
|
|
338
|
+
appKey: this._appKey,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
/** 展开 EventParams 为普通对象 */
|
|
346
|
+
private _flattenParams(params?: EventParams): Record<string, any> {
|
|
347
|
+
if (!params) return {};
|
|
348
|
+
if (typeof params === 'string') return { value: params };
|
|
349
|
+
return params as Record<string, any>;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AudioClip, AudioSource } from "cc";
|
|
1
|
+
import { AudioClip, AudioSource, Node } from "cc";
|
|
2
2
|
import { LogUtils } from "../Utils/LogUtils";
|
|
3
3
|
import { PlatformID } from "../Definition/SystemDefinition";
|
|
4
4
|
import { FrameworkBase } from "../Definition/FrameworkBase";
|
|
@@ -15,7 +15,9 @@ export class AudioMgr extends BaseMgr {
|
|
|
15
15
|
static TAG: string = "AudioMgr";
|
|
16
16
|
|
|
17
17
|
private effectSwitch: boolean = false;
|
|
18
|
-
private
|
|
18
|
+
private effectPool: AudioSource[] = [];
|
|
19
|
+
/** 音效池最大同时播放数量 */
|
|
20
|
+
private static readonly EFFECT_POOL_SIZE: number = 8;
|
|
19
21
|
|
|
20
22
|
private musicSwitch: boolean = false;
|
|
21
23
|
private musicAudioSource: AudioSource = null;
|
|
@@ -39,11 +41,11 @@ export class AudioMgr extends BaseMgr {
|
|
|
39
41
|
* 初始化音频管理器
|
|
40
42
|
*/
|
|
41
43
|
public init(): void {
|
|
42
|
-
this.effectAudioSource = this.node.addComponent(AudioSource);
|
|
43
44
|
this.musicAudioSource = this.node.addComponent(AudioSource);
|
|
44
45
|
|
|
46
|
+
this._initEffectPool();
|
|
47
|
+
|
|
45
48
|
this.effectSwitch = false;
|
|
46
|
-
this.effectAudioSource.volume = 0;
|
|
47
49
|
|
|
48
50
|
this.musicSwitch = false;
|
|
49
51
|
this.musicAudioSource.volume = 0;
|
|
@@ -163,7 +165,9 @@ export class AudioMgr extends BaseMgr {
|
|
|
163
165
|
LogUtils.Instance.info(AudioMgr.TAG, "开启音效");
|
|
164
166
|
|
|
165
167
|
this.effectSwitch = true;
|
|
166
|
-
this.
|
|
168
|
+
for (let i = 0; i < this.effectPool.length; i++) {
|
|
169
|
+
this.effectPool[i].volume = 1;
|
|
170
|
+
}
|
|
167
171
|
}
|
|
168
172
|
|
|
169
173
|
/** 关闭音效 */
|
|
@@ -175,7 +179,52 @@ export class AudioMgr extends BaseMgr {
|
|
|
175
179
|
LogUtils.Instance.info(AudioMgr.TAG, "关闭音效");
|
|
176
180
|
|
|
177
181
|
this.effectSwitch = false;
|
|
178
|
-
this.
|
|
182
|
+
for (let i = 0; i < this.effectPool.length; i++) {
|
|
183
|
+
this.effectPool[i].volume = 0;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 初始化音效池(内部方法)
|
|
189
|
+
*/
|
|
190
|
+
private _initEffectPool(): void {
|
|
191
|
+
for (let i = 0; i < AudioMgr.EFFECT_POOL_SIZE; i++) {
|
|
192
|
+
let node: Node = new Node("EffectAudio_" + i);
|
|
193
|
+
node.setParent(this.node);
|
|
194
|
+
let audioSource: AudioSource = node.addComponent(AudioSource);
|
|
195
|
+
audioSource.volume = 0;
|
|
196
|
+
// 监听 ended 事件,播完后清 clip 以便下次复用
|
|
197
|
+
audioSource.node.on(AudioSource.EventType.ENDED, () => {
|
|
198
|
+
audioSource.clip = null;
|
|
199
|
+
});
|
|
200
|
+
this.effectPool.push(audioSource);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* 从池中找空闲 AudioSource 播放音效(内部方法)
|
|
206
|
+
*/
|
|
207
|
+
private _playEffectWithPool(clip: AudioClip): void {
|
|
208
|
+
// 找一个当前未在播放的 AudioSource
|
|
209
|
+
let audioSource: AudioSource = null;
|
|
210
|
+
for (let i = 0; i < this.effectPool.length; i++) {
|
|
211
|
+
if (!this.effectPool[i].playing) {
|
|
212
|
+
audioSource = this.effectPool[i];
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// 池已满,跳过此次播放
|
|
218
|
+
if (audioSource === null) {
|
|
219
|
+
LogUtils.Instance.warn(AudioMgr.TAG, "音效池已满(8个全部占用),跳过此次播放", {
|
|
220
|
+
operation: "_playEffectWithPool",
|
|
221
|
+
poolSize: this.effectPool.length
|
|
222
|
+
});
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
audioSource.clip = clip;
|
|
227
|
+
audioSource.play();
|
|
179
228
|
}
|
|
180
229
|
|
|
181
230
|
/**
|
|
@@ -201,7 +250,7 @@ export class AudioMgr extends BaseMgr {
|
|
|
201
250
|
});
|
|
202
251
|
return;
|
|
203
252
|
}
|
|
204
|
-
this.
|
|
253
|
+
this._playEffectWithPool(audioClip);
|
|
205
254
|
}
|
|
206
255
|
}
|
|
207
256
|
|
|
@@ -625,6 +625,36 @@ export class InputMgr extends BaseMgr {
|
|
|
625
625
|
}
|
|
626
626
|
}
|
|
627
627
|
|
|
628
|
+
/**
|
|
629
|
+
* 监听键盘按下(一次性)
|
|
630
|
+
* 触发一次后自动移除监听
|
|
631
|
+
* @param keyCode 按键码(KeyCode),KeyCode.NONE 表示监听所有按键
|
|
632
|
+
* @param callback 回调函数
|
|
633
|
+
* @param target 回调目标
|
|
634
|
+
*/
|
|
635
|
+
public onceKeyDown(keyCode: KeyCode, callback: (event: EventKeyboard) => void, target: any): void {
|
|
636
|
+
let onceCallback = (event: EventKeyboard) => {
|
|
637
|
+
this.offKeyDown(keyCode, onceCallback, target);
|
|
638
|
+
callback.apply(target, [event]);
|
|
639
|
+
};
|
|
640
|
+
this.onKeyDown(keyCode, onceCallback, target);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* 监听键盘抬起(一次性)
|
|
645
|
+
* 触发一次后自动移除监听
|
|
646
|
+
* @param keyCode 按键码(KeyCode),KeyCode.NONE 表示监听所有按键
|
|
647
|
+
* @param callback 回调函数
|
|
648
|
+
* @param target 回调目标
|
|
649
|
+
*/
|
|
650
|
+
public onceKeyUp(keyCode: KeyCode, callback: (event: EventKeyboard) => void, target: any): void {
|
|
651
|
+
let onceCallback = (event: EventKeyboard) => {
|
|
652
|
+
this.offKeyUp(keyCode, onceCallback, target);
|
|
653
|
+
callback.apply(target, [event]);
|
|
654
|
+
};
|
|
655
|
+
this.onKeyUp(keyCode, onceCallback, target);
|
|
656
|
+
}
|
|
657
|
+
|
|
628
658
|
/**
|
|
629
659
|
* 判断按键是否处于按下状态
|
|
630
660
|
* @param keyCode 按键码(KeyCode)
|
|
@@ -891,6 +921,22 @@ export class InputMgr extends BaseMgr {
|
|
|
891
921
|
}
|
|
892
922
|
}
|
|
893
923
|
|
|
924
|
+
/**
|
|
925
|
+
* 监听按钮点击(一次性)
|
|
926
|
+
* 触发一次后自动移除监听
|
|
927
|
+
* @param button 按钮节点
|
|
928
|
+
* @param callback 回调函数
|
|
929
|
+
* @param target 回调目标
|
|
930
|
+
* @param preventAccidental 是否防误触(连续点击),默认 true
|
|
931
|
+
*/
|
|
932
|
+
public onceButtonClick(button: Node, callback: () => void, target: any, preventAccidental: boolean = true): void {
|
|
933
|
+
let onceCallback = () => {
|
|
934
|
+
this.offButtonClick(button, onceCallback, target);
|
|
935
|
+
callback.apply(target);
|
|
936
|
+
};
|
|
937
|
+
this.onButtonClick(button, onceCallback, target, preventAccidental);
|
|
938
|
+
}
|
|
939
|
+
|
|
894
940
|
// ==================== 节点触摸事件(对外) ====================
|
|
895
941
|
|
|
896
942
|
/**
|
|
@@ -1019,6 +1065,21 @@ export class InputMgr extends BaseMgr {
|
|
|
1019
1065
|
}
|
|
1020
1066
|
}
|
|
1021
1067
|
|
|
1068
|
+
/**
|
|
1069
|
+
* 监听节点触摸结束(一次性)
|
|
1070
|
+
* 触发一次后自动移除监听
|
|
1071
|
+
* @param node 节点
|
|
1072
|
+
* @param callback 回调函数
|
|
1073
|
+
* @param target 回调目标
|
|
1074
|
+
*/
|
|
1075
|
+
public onceNodeTouchEnd(node: Node, callback: (touch: Touch) => void, target: any): void {
|
|
1076
|
+
let onceCallback = (touch: Touch) => {
|
|
1077
|
+
this.offNodeTouchEnd(node, onceCallback, target);
|
|
1078
|
+
callback.apply(target, [touch]);
|
|
1079
|
+
};
|
|
1080
|
+
this.onNodeTouchEnd(node, onceCallback, target);
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1022
1083
|
/**
|
|
1023
1084
|
* 移除节点触摸事件
|
|
1024
1085
|
*/
|
|
@@ -25,6 +25,8 @@ export interface IPoolStatistics {
|
|
|
25
25
|
nowActiveCount: number;
|
|
26
26
|
/** 历史最大活跃节点数 */
|
|
27
27
|
maxActiveCount: number;
|
|
28
|
+
/** 当前可用节点数 */
|
|
29
|
+
availableCount: number;
|
|
28
30
|
/** 最后访问时间 */
|
|
29
31
|
lastAccessTime: number;
|
|
30
32
|
}
|
|
@@ -49,6 +51,7 @@ export class PoolInfo {
|
|
|
49
51
|
reusedCount: 0,
|
|
50
52
|
nowActiveCount: 0,
|
|
51
53
|
maxActiveCount: 0,
|
|
54
|
+
availableCount: 0,
|
|
52
55
|
lastAccessTime: Date.now(),
|
|
53
56
|
};
|
|
54
57
|
}
|
|
@@ -229,8 +232,9 @@ export class NodePoolMgr extends BaseMgr {
|
|
|
229
232
|
reusedCount: 0,
|
|
230
233
|
nowActiveCount: 0,
|
|
231
234
|
maxActiveCount: 0,
|
|
235
|
+
availableCount: 0,
|
|
232
236
|
lastAccessTime: Date.now(),
|
|
233
|
-
}
|
|
237
|
+
};
|
|
234
238
|
}
|
|
235
239
|
}
|
|
236
240
|
|
|
@@ -475,6 +479,13 @@ export class NodePoolMgr extends BaseMgr {
|
|
|
475
479
|
}
|
|
476
480
|
|
|
477
481
|
let poolInfo: PoolInfo = this._poolInfoMap.get(poolName);
|
|
482
|
+
if (!poolInfo) {
|
|
483
|
+
LogUtils.Instance.error(NodePoolMgr.TAG, FwkErrorCode.NodePool.NotFound, {
|
|
484
|
+
operation: "_destoryNode",
|
|
485
|
+
poolName: poolName,
|
|
486
|
+
reason: "节点池信息获取失败"
|
|
487
|
+
});
|
|
488
|
+
}
|
|
478
489
|
|
|
479
490
|
if (this.uiMgr === null) {
|
|
480
491
|
this.uiMgr = ServiceLocator.Instance.get<UIMgr>("UIMgr");
|
|
@@ -545,6 +556,52 @@ export class NodePoolMgr extends BaseMgr {
|
|
|
545
556
|
node.scale = Vec3.ONE;
|
|
546
557
|
}
|
|
547
558
|
|
|
559
|
+
/**
|
|
560
|
+
* 检查节点池是否存在
|
|
561
|
+
* @param poolName 节点池名称
|
|
562
|
+
*/
|
|
563
|
+
public hasPool(poolName: string): boolean {
|
|
564
|
+
return this._poolInfoMap.has(poolName);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* 获取指定节点池的统计信息
|
|
569
|
+
* @param poolName 节点池名称
|
|
570
|
+
*/
|
|
571
|
+
public getPoolStats(poolName: string): IPoolStatistics | null {
|
|
572
|
+
let poolInfo: PoolInfo = this._poolInfoMap.get(poolName);
|
|
573
|
+
if (!poolInfo) {
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
return {
|
|
578
|
+
createdCount: poolInfo.statistics.createdCount,
|
|
579
|
+
reusedCount: poolInfo.statistics.reusedCount,
|
|
580
|
+
nowActiveCount: poolInfo.statistics.nowActiveCount,
|
|
581
|
+
maxActiveCount: poolInfo.statistics.maxActiveCount,
|
|
582
|
+
availableCount: poolInfo.pool.size(),
|
|
583
|
+
lastAccessTime: poolInfo.statistics.lastAccessTime,
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* 获取所有节点池的统计信息
|
|
589
|
+
*/
|
|
590
|
+
public getAllPoolStats(): Map<string, IPoolStatistics> {
|
|
591
|
+
let result: Map<string, IPoolStatistics> = new Map<string, IPoolStatistics>();
|
|
592
|
+
this._poolInfoMap.forEach((poolInfo: PoolInfo, poolName: string) => {
|
|
593
|
+
result.set(poolName, {
|
|
594
|
+
createdCount: poolInfo.statistics.createdCount,
|
|
595
|
+
reusedCount: poolInfo.statistics.reusedCount,
|
|
596
|
+
nowActiveCount: poolInfo.statistics.nowActiveCount,
|
|
597
|
+
maxActiveCount: poolInfo.statistics.maxActiveCount,
|
|
598
|
+
availableCount: poolInfo.pool.size(),
|
|
599
|
+
lastAccessTime: poolInfo.statistics.lastAccessTime,
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
return result;
|
|
603
|
+
}
|
|
604
|
+
|
|
548
605
|
/** 打印节点池状态(调试用) */
|
|
549
606
|
private _printPoolState(poolName: string): void {
|
|
550
607
|
let poolInfo: PoolInfo = this._poolInfoMap.get(poolName);
|
|
@@ -573,6 +630,7 @@ export class NodePoolMgr extends BaseMgr {
|
|
|
573
630
|
reusedCount: poolInfo.statistics.reusedCount,
|
|
574
631
|
nowActiveCount: poolInfo.statistics.nowActiveCount,
|
|
575
632
|
maxActiveCount: poolInfo.statistics.maxActiveCount,
|
|
633
|
+
availableCount: poolInfo.pool.size(),
|
|
576
634
|
lastAccessTime: poolInfo.statistics.lastAccessTime
|
|
577
635
|
});
|
|
578
636
|
}
|