@chiyou/minigame-framework 1.2.69 → 1.3.1
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/EventDefinition.ts +7 -0
- package/src/Framework/Definition/SystemDefinition.ts +12 -1
- package/src/Framework/Definition/TimerDefinition.ts +9 -0
- package/src/Framework/Definition/UIDefinition.ts +6 -0
- package/src/Framework/Manager/AudioMgr.ts +56 -7
- package/src/Framework/Manager/EventMgr.ts +31 -0
- package/src/Framework/Manager/InputMgr.ts +61 -0
- package/src/Framework/Manager/NodePoolMgr.ts +83 -4
- package/src/Framework/Manager/ResMgr.ts +41 -2
- package/src/Framework/Manager/TimerMgr.ts +53 -3
- package/src/Framework/Manager/UIMgr.ts +89 -9
- package/src/Framework/Utils/LogUtils.ts +27 -0
- package/src/Framework/Utils/ObjectUtils.ts +111 -0
- package/src/index.ts +2 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LogUtils } from '../Utils/LogUtils';
|
|
2
|
+
import { EventListenerInfo } from '../Definition/EventDefinition';
|
|
2
3
|
import { BaseMgr } from './BaseMgr';
|
|
3
4
|
|
|
4
5
|
/** 事件项 */
|
|
@@ -73,6 +74,20 @@ export class EventMgr extends BaseMgr {
|
|
|
73
74
|
this.eventList.set(eventName, array);
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
/**
|
|
78
|
+
* 监听事件(只触发一次,触发后自动移除)
|
|
79
|
+
* @param eventName 事件名称
|
|
80
|
+
* @param callback 回调函数
|
|
81
|
+
* @param target 回调目标对象
|
|
82
|
+
*/
|
|
83
|
+
public once(eventName: string, callback: Function, target: any): void {
|
|
84
|
+
const wrappedCallback = (...data: any[]) => {
|
|
85
|
+
this.off(eventName, wrappedCallback, target);
|
|
86
|
+
callback.apply(target, data);
|
|
87
|
+
};
|
|
88
|
+
this.on(eventName, wrappedCallback, target);
|
|
89
|
+
}
|
|
90
|
+
|
|
76
91
|
/**
|
|
77
92
|
* 取消监听事件
|
|
78
93
|
* @param eventName 事件名称
|
|
@@ -128,6 +143,22 @@ export class EventMgr extends BaseMgr {
|
|
|
128
143
|
}
|
|
129
144
|
}
|
|
130
145
|
|
|
146
|
+
/**
|
|
147
|
+
* 获取指定事件的所有监听者(调试用)
|
|
148
|
+
* @param eventName 事件名称
|
|
149
|
+
* @return 监听者信息数组
|
|
150
|
+
*/
|
|
151
|
+
public getListeners(eventName: string): EventListenerInfo[] {
|
|
152
|
+
const array = this.eventList.get(eventName);
|
|
153
|
+
if (!array || array.length === 0) {
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
return array.map(item => ({
|
|
157
|
+
callback: item.callback,
|
|
158
|
+
target: item.target
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
|
|
131
162
|
/**
|
|
132
163
|
* 触发事件
|
|
133
164
|
* @param eventName 事件名称
|
|
@@ -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
|
*/
|
|
@@ -3,7 +3,7 @@ import { BaseMgr } from './BaseMgr';
|
|
|
3
3
|
import { LogUtils } from '../Utils/LogUtils';
|
|
4
4
|
import { FwkErrorCode } from '../Definition/FwkErrorDefinition';
|
|
5
5
|
import { ServiceLocator } from '../Utils/ServiceLocator';
|
|
6
|
-
import
|
|
6
|
+
import { UIMgr, UICtrl } from './UIMgr';
|
|
7
7
|
|
|
8
8
|
/** 节点池配置接口 */
|
|
9
9
|
export interface IPoolConfig {
|
|
@@ -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
|
|
|
@@ -334,6 +338,15 @@ export class NodePoolMgr extends BaseMgr {
|
|
|
334
338
|
let node: Node = poolInfo.pool.get() as Node;
|
|
335
339
|
poolInfo.usingNodeSet.add(node);
|
|
336
340
|
|
|
341
|
+
// 重置 Transform(_resetNode 只管 Transform,不管业务组件)
|
|
342
|
+
this._resetNode(node);
|
|
343
|
+
|
|
344
|
+
// 调用业务层钩子 onPoolGet(顺序:_resetNode → onPoolGet → addChild → active)
|
|
345
|
+
let ctrl: UICtrl = node.getComponent(UICtrl);
|
|
346
|
+
if (ctrl && typeof ctrl.onPoolGet === 'function' && node.isValid) {
|
|
347
|
+
ctrl.onPoolGet(node);
|
|
348
|
+
}
|
|
349
|
+
|
|
337
350
|
if (parent) {
|
|
338
351
|
parent.addChild(node);
|
|
339
352
|
}
|
|
@@ -466,6 +479,13 @@ export class NodePoolMgr extends BaseMgr {
|
|
|
466
479
|
}
|
|
467
480
|
|
|
468
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
|
+
}
|
|
469
489
|
|
|
470
490
|
if (this.uiMgr === null) {
|
|
471
491
|
this.uiMgr = ServiceLocator.Instance.get<UIMgr>("UIMgr");
|
|
@@ -480,7 +500,10 @@ export class NodePoolMgr extends BaseMgr {
|
|
|
480
500
|
}
|
|
481
501
|
}
|
|
482
502
|
|
|
483
|
-
/**
|
|
503
|
+
/**
|
|
504
|
+
* 回收节点(内部方法)
|
|
505
|
+
* 调用顺序:NodePoolMgr.putNode → UICtrl.onPoolPut → NodePoolMgr._resetNode → node.active = false → 入池
|
|
506
|
+
*/
|
|
484
507
|
private _recycleNode(poolName: string, node: Node, resetNode: boolean = true): void {
|
|
485
508
|
if (!node || !node.isValid) {
|
|
486
509
|
return;
|
|
@@ -494,7 +517,12 @@ export class NodePoolMgr extends BaseMgr {
|
|
|
494
517
|
node.removeFromParent();
|
|
495
518
|
}
|
|
496
519
|
|
|
520
|
+
// 调用业务层钩子 onPoolPut(顺序:onPoolPut → _resetNode → active → 入池)
|
|
497
521
|
if (resetNode) {
|
|
522
|
+
let ctrl: UICtrl = node.getComponent(UICtrl);
|
|
523
|
+
if (ctrl && typeof ctrl.onPoolPut === 'function' && node.isValid) {
|
|
524
|
+
ctrl.onPoolPut(node);
|
|
525
|
+
}
|
|
498
526
|
this._resetNode(node);
|
|
499
527
|
}
|
|
500
528
|
|
|
@@ -513,7 +541,11 @@ export class NodePoolMgr extends BaseMgr {
|
|
|
513
541
|
}
|
|
514
542
|
}
|
|
515
543
|
|
|
516
|
-
/**
|
|
544
|
+
/**
|
|
545
|
+
* 重置节点 Transform(内部方法)
|
|
546
|
+
* 注意:只重置 position/rotation/scale,不管业务组件(如 UIOpacity 等)。
|
|
547
|
+
* 业务组件的清理由 onPoolPut 钩子兜底。
|
|
548
|
+
*/
|
|
517
549
|
private _resetNode(node: Node): void {
|
|
518
550
|
if (!node || !node.isValid) {
|
|
519
551
|
return;
|
|
@@ -524,6 +556,52 @@ export class NodePoolMgr extends BaseMgr {
|
|
|
524
556
|
node.scale = Vec3.ONE;
|
|
525
557
|
}
|
|
526
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
|
+
|
|
527
605
|
/** 打印节点池状态(调试用) */
|
|
528
606
|
private _printPoolState(poolName: string): void {
|
|
529
607
|
let poolInfo: PoolInfo = this._poolInfoMap.get(poolName);
|
|
@@ -552,6 +630,7 @@ export class NodePoolMgr extends BaseMgr {
|
|
|
552
630
|
reusedCount: poolInfo.statistics.reusedCount,
|
|
553
631
|
nowActiveCount: poolInfo.statistics.nowActiveCount,
|
|
554
632
|
maxActiveCount: poolInfo.statistics.maxActiveCount,
|
|
633
|
+
availableCount: poolInfo.pool.size(),
|
|
555
634
|
lastAccessTime: poolInfo.statistics.lastAccessTime
|
|
556
635
|
});
|
|
557
636
|
}
|
|
@@ -575,11 +575,12 @@ export class ResMgr extends BaseMgr {
|
|
|
575
575
|
}
|
|
576
576
|
|
|
577
577
|
/**
|
|
578
|
-
*
|
|
578
|
+
* 获取资源(同步)
|
|
579
|
+
* @description 必须先通过 preloadResBatch 预加载或 loadRes/loadResAsync 加载目标资源后,才能使用 getAsset 获取。同一帧内不支持同步触发加载。
|
|
579
580
|
* @param bundleName AssetBundle 名称
|
|
580
581
|
* @param url 资源路径
|
|
581
582
|
* @param assetType 资源类型
|
|
582
|
-
* @return
|
|
583
|
+
* @return 资源实例(未加载时返回 null)
|
|
583
584
|
*/
|
|
584
585
|
public getAsset<T extends Asset>(bundleName: string, url: string, assetType?: typeof Asset): T | null {
|
|
585
586
|
if (!bundleName || bundleName.trim() === "") {
|
|
@@ -739,6 +740,44 @@ export class ResMgr extends BaseMgr {
|
|
|
739
740
|
});
|
|
740
741
|
}
|
|
741
742
|
|
|
743
|
+
/**
|
|
744
|
+
* 动态加载资源(Promise 版本)
|
|
745
|
+
* @param bundleName AssetBundle 名称
|
|
746
|
+
* @param url 资源路径
|
|
747
|
+
* @param assetType 资源类型
|
|
748
|
+
* @return Promise<Asset> 资源实例
|
|
749
|
+
*/
|
|
750
|
+
public loadResAsync<T extends Asset>(bundleName: string, url: string, assetType: typeof Asset): Promise<T> {
|
|
751
|
+
return new Promise<T>((resolve, reject) => {
|
|
752
|
+
this.loadRes(bundleName, url, assetType, (success: boolean) => {
|
|
753
|
+
if (success) {
|
|
754
|
+
let asset = this.getAsset<T>(bundleName, url, assetType);
|
|
755
|
+
if (asset) {
|
|
756
|
+
resolve(asset);
|
|
757
|
+
} else {
|
|
758
|
+
LogUtils.Instance.error(ResMgr.TAG, FwkErrorCode.Res.NotFound, {
|
|
759
|
+
operation: "loadResAsync",
|
|
760
|
+
reason: "loadRes 成功但 getAsset 返回 null(可能原因:图集子资源 URL 不正确,或资源类型不匹配)",
|
|
761
|
+
bundleName: bundleName,
|
|
762
|
+
url: url,
|
|
763
|
+
assetType: assetType?.name || "未指定"
|
|
764
|
+
});
|
|
765
|
+
reject(new Error(`loadResAsync: getAsset returned null, bundle=${bundleName}, url=${url}`));
|
|
766
|
+
}
|
|
767
|
+
} else {
|
|
768
|
+
LogUtils.Instance.error(ResMgr.TAG, FwkErrorCode.Res.LoadAssetFailed, {
|
|
769
|
+
operation: "loadResAsync",
|
|
770
|
+
reason: "loadRes 加载失败",
|
|
771
|
+
bundleName: bundleName,
|
|
772
|
+
url: url,
|
|
773
|
+
assetType: assetType?.name || "未指定"
|
|
774
|
+
});
|
|
775
|
+
reject(new Error(`loadResAsync failed: bundle=${bundleName}, url=${url}`));
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
|
|
742
781
|
/**
|
|
743
782
|
* 加载远程图片
|
|
744
783
|
* @param imageUrl 图片 URL
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { FwkErrorCode } from "../Definition/FwkErrorDefinition";
|
|
2
|
+
import { FrameworkBase } from "../Definition/FrameworkBase";
|
|
3
|
+
import { TimerRepeat } from "../Definition/TimerDefinition";
|
|
2
4
|
import { LogUtils } from "../Utils/LogUtils";
|
|
3
5
|
import { ServiceLocator } from "../Utils/ServiceLocator";
|
|
4
6
|
import { BaseMgr } from "./BaseMgr";
|
|
7
|
+
import { EventMgr } from "./EventMgr";
|
|
5
8
|
|
|
6
9
|
interface ITimer {
|
|
7
10
|
tick: number;
|
|
@@ -18,6 +21,7 @@ export class TimerMgr extends BaseMgr {
|
|
|
18
21
|
static TAG: string = "TimerMgr";
|
|
19
22
|
|
|
20
23
|
private timers: ITimer[] = [];
|
|
24
|
+
private _paused: boolean = false;
|
|
21
25
|
|
|
22
26
|
onLoad(): void {
|
|
23
27
|
super.onLoad();
|
|
@@ -30,6 +34,10 @@ export class TimerMgr extends BaseMgr {
|
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
ServiceLocator.Instance.register("TimerMgr", this);
|
|
37
|
+
|
|
38
|
+
// 监听生命周期事件,切后台暂停定时器
|
|
39
|
+
EventMgr.Instance.on(FrameworkBase.Message.LifeCycle_onGameHide, this._onGameHide, this);
|
|
40
|
+
EventMgr.Instance.on(FrameworkBase.Message.LifeCycle_onGameShow, this._onGameShow, this);
|
|
33
41
|
}
|
|
34
42
|
|
|
35
43
|
/**
|
|
@@ -37,23 +45,63 @@ export class TimerMgr extends BaseMgr {
|
|
|
37
45
|
*/
|
|
38
46
|
public init(): void {
|
|
39
47
|
this.timers = [];
|
|
48
|
+
this._paused = false;
|
|
40
49
|
|
|
41
50
|
LogUtils.Instance.info(TimerMgr.TAG, "初始化完成");
|
|
42
51
|
}
|
|
43
52
|
|
|
53
|
+
/**
|
|
54
|
+
* 暂停所有定时器
|
|
55
|
+
*/
|
|
56
|
+
public pauseAll(): void {
|
|
57
|
+
if (this._paused) return;
|
|
58
|
+
this._paused = true;
|
|
59
|
+
LogUtils.Instance.info(TimerMgr.TAG, "定时器已暂停");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 恢复所有定时器
|
|
64
|
+
*/
|
|
65
|
+
public resumeAll(): void {
|
|
66
|
+
if (!this._paused) return;
|
|
67
|
+
this._paused = false;
|
|
68
|
+
LogUtils.Instance.info(TimerMgr.TAG, "定时器已恢复");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 定时器是否暂停中
|
|
73
|
+
*/
|
|
74
|
+
public isPaused(): boolean {
|
|
75
|
+
return this._paused;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private _onGameHide(): void {
|
|
79
|
+
this.pauseAll();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private _onGameShow(): void {
|
|
83
|
+
this.resumeAll();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
onDestroy(): void {
|
|
87
|
+
EventMgr.Instance.off(FrameworkBase.Message.LifeCycle_onGameHide, this._onGameHide, this);
|
|
88
|
+
EventMgr.Instance.off(FrameworkBase.Message.LifeCycle_onGameShow, this._onGameShow, this);
|
|
89
|
+
super.onDestroy();
|
|
90
|
+
}
|
|
91
|
+
|
|
44
92
|
/**
|
|
45
93
|
* 添加定时器
|
|
46
94
|
* @param delay 延迟时间(秒)
|
|
47
95
|
* @param interval 间隔时间(秒)
|
|
48
|
-
* @param repeat
|
|
96
|
+
* @param repeat 重复次数:TimerRepeat.Once(1)= 执行一次后移除,TimerRepeat.Infinite(-1)= 无限循环,0 = 不执行(立即跳过),>0 = 执行指定次数
|
|
49
97
|
* @param func 回调函数
|
|
50
98
|
* @param target 回调目标对象
|
|
51
99
|
*/
|
|
52
|
-
public addTimer(delay: number, interval: number, repeat: number, func: Function, target: Object) {
|
|
100
|
+
public addTimer(delay: number, interval: number, repeat: TimerRepeat | number, func: Function, target: Object) {
|
|
53
101
|
if (repeat === 0) {
|
|
54
102
|
LogUtils.Instance.error(TimerMgr.TAG, FwkErrorCode.Timer.InvalidState, {
|
|
55
103
|
operation: "addTimer",
|
|
56
|
-
reason: "
|
|
104
|
+
reason: "repeat=0 拒绝添加(不支持),应使用 TimerRepeat.Once",
|
|
57
105
|
delay: delay,
|
|
58
106
|
interval: interval,
|
|
59
107
|
repeat: repeat
|
|
@@ -117,6 +165,8 @@ export class TimerMgr extends BaseMgr {
|
|
|
117
165
|
}
|
|
118
166
|
|
|
119
167
|
protected update(dt: number): void {
|
|
168
|
+
if (this._paused) return;
|
|
169
|
+
|
|
120
170
|
for (let index = 0; index < this.timers.length; index++) {
|
|
121
171
|
const item = this.timers[index];
|
|
122
172
|
item.tick += dt;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { Component, Node, Button, Prefab, instantiate, screen, UITransform, v3, view, ResolutionPolicy, math, Tween, tween, Label, Color, Sprite, SpriteFrame, BlockInputEvents, Texture2D } from 'cc';
|
|
1
|
+
import { Component, Node, Button, Prefab, instantiate, screen, UITransform, v3, view, ResolutionPolicy, math, Tween, tween, Label, Color, Sprite, SpriteFrame, BlockInputEvents, Texture2D, Vec3 } from 'cc';
|
|
2
2
|
import { FwkErrorCode } from '../Definition/FwkErrorDefinition';
|
|
3
3
|
import { LogUtils } from '../Utils/LogUtils';
|
|
4
|
-
import { ToastDuration } from '../Definition/UIDefinition';
|
|
4
|
+
import { ToastDuration, ToastPosition } from '../Definition/UIDefinition';
|
|
5
5
|
import { BaseMgr } from './BaseMgr';
|
|
6
6
|
import { ServiceLocator } from '../Utils/ServiceLocator';
|
|
7
7
|
import type { ResMgr } from './ResMgr';
|
|
8
|
+
import type { SystemMgr } from './SystemMgr';
|
|
9
|
+
import type { InputMgr } from './InputMgr';
|
|
8
10
|
|
|
9
11
|
/** UI 控制器基类 */
|
|
10
12
|
export class UICtrl extends Component {
|
|
@@ -16,6 +18,20 @@ export class UICtrl extends Component {
|
|
|
16
18
|
this.load_all_object(this.node, "");
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
/**
|
|
22
|
+
* 节点从池中取出时调用(UICtrl 子类可 override)
|
|
23
|
+
* 调用顺序:NodePoolMgr.getNode → NodePoolMgr._resetNode → UICtrl.onPoolGet → parent.addChild → node.active = true
|
|
24
|
+
* @param node 从池中取出的节点
|
|
25
|
+
*/
|
|
26
|
+
public onPoolGet?(node: Node): void;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 节点归还到池中时调用(UICtrl 子类可 override)
|
|
30
|
+
* 调用顺序:NodePoolMgr.putNode → UICtrl.onPoolPut → NodePoolMgr._resetNode → node.active = false → 入池
|
|
31
|
+
* @param node 归还到池中的节点
|
|
32
|
+
*/
|
|
33
|
+
public onPoolPut?(node: Node): void;
|
|
34
|
+
|
|
19
35
|
/** 加载所有子节点到视图映射 */
|
|
20
36
|
private load_all_object(root: Node, path: string): void {
|
|
21
37
|
for (let i = 0; i < root.children.length; i++) {
|
|
@@ -111,6 +127,8 @@ export class UIMgr extends BaseMgr {
|
|
|
111
127
|
private toastHistoryMaxCount: number = 10;
|
|
112
128
|
|
|
113
129
|
private resMgr: ResMgr = null;
|
|
130
|
+
private inputMgr: InputMgr = null;
|
|
131
|
+
private systemMgr: SystemMgr = null;
|
|
114
132
|
|
|
115
133
|
/** 打印 UI 映射(调试用) */
|
|
116
134
|
public print(): void {
|
|
@@ -269,6 +287,10 @@ export class UIMgr extends BaseMgr {
|
|
|
269
287
|
this.resMgr = ServiceLocator.Instance.get<ResMgr>("ResMgr");
|
|
270
288
|
}
|
|
271
289
|
|
|
290
|
+
if (this.inputMgr === null) {
|
|
291
|
+
this.inputMgr = ServiceLocator.Instance.get<InputMgr>("InputMgr");
|
|
292
|
+
}
|
|
293
|
+
|
|
272
294
|
if (!this.resMgr) {
|
|
273
295
|
return null;
|
|
274
296
|
}
|
|
@@ -420,12 +442,18 @@ export class UIMgr extends BaseMgr {
|
|
|
420
442
|
* 显示 Toast
|
|
421
443
|
* @param content 显示内容
|
|
422
444
|
* @param toastDuration 显示时长
|
|
445
|
+
* @param position 显示位置(ToastPosition 枚举或世界坐标 Vec3),默认居中
|
|
423
446
|
*/
|
|
424
|
-
public showToast(content: string, toastDuration: ToastDuration = ToastDuration.Duration_Short): void {
|
|
447
|
+
public showToast(content: string, toastDuration: ToastDuration = ToastDuration.Duration_Short, position?: ToastPosition | Vec3): void {
|
|
425
448
|
if (this.node_toastRoot === null) {
|
|
426
449
|
return;
|
|
427
450
|
}
|
|
428
451
|
|
|
452
|
+
// 延迟加载 SystemMgr(避免循环依赖)
|
|
453
|
+
if (this.systemMgr === null) {
|
|
454
|
+
this.systemMgr = ServiceLocator.Instance.get<SystemMgr>("SystemMgr");
|
|
455
|
+
}
|
|
456
|
+
|
|
429
457
|
// 记录 Toast 历史
|
|
430
458
|
this.toastHistory.push(content);
|
|
431
459
|
if (this.toastHistory.length > this.toastHistoryMaxCount) {
|
|
@@ -449,6 +477,33 @@ export class UIMgr extends BaseMgr {
|
|
|
449
477
|
|
|
450
478
|
this.node_toastRoot.active = true;
|
|
451
479
|
this.node_toastRoot.getComponent(UITransform).width = textWidth + 80;
|
|
480
|
+
|
|
481
|
+
// 设置 Toast 位置
|
|
482
|
+
if (position !== undefined) {
|
|
483
|
+
if (position instanceof Vec3) {
|
|
484
|
+
// Vec3 世界坐标,转换为节点坐标
|
|
485
|
+
let localPos: Vec3 = this.node_toastRoot.getComponent(UITransform).convertToNodeSpaceAR(position);
|
|
486
|
+
this.node_toastRoot.setPosition(localPos.x, localPos.y);
|
|
487
|
+
} else {
|
|
488
|
+
// ToastPosition 枚举
|
|
489
|
+
let x: number = 0;
|
|
490
|
+
let y: number = 0;
|
|
491
|
+
if (this.systemMgr) {
|
|
492
|
+
let screenInfo = this.systemMgr.getScreenInfo();
|
|
493
|
+
let screenH: number = screenInfo.windowHeight;
|
|
494
|
+
let edgeOffset: number = 150; // 距屏幕边缘的距离
|
|
495
|
+
if (position === ToastPosition.Top) {
|
|
496
|
+
y = screenH / 2 - edgeOffset;
|
|
497
|
+
} else if (position === ToastPosition.Bottom) {
|
|
498
|
+
y = -screenH / 2 + edgeOffset;
|
|
499
|
+
}
|
|
500
|
+
// Center: x=0, y=0
|
|
501
|
+
}
|
|
502
|
+
// systemMgr 为空时默认居中(x=0, y=0)
|
|
503
|
+
this.node_toastRoot.setPosition(x, y);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
452
507
|
this.tween_toast = tween(this.node_toastRoot)
|
|
453
508
|
.delay(showTime)
|
|
454
509
|
.call(() => {
|
|
@@ -469,13 +524,10 @@ export class UIMgr extends BaseMgr {
|
|
|
469
524
|
/**
|
|
470
525
|
* 打开 Dialog
|
|
471
526
|
* @param ui_name UI 名称
|
|
472
|
-
* @param maskColor
|
|
473
|
-
* @
|
|
527
|
+
* @param maskColor 遮罩颜色(默认半透明黑)
|
|
528
|
+
* @param closeOnMaskClick 点击遮罩是否关闭 Dialog(默认 false)
|
|
474
529
|
*/
|
|
475
|
-
public openDialog(ui_name: string, maskColor
|
|
476
|
-
if (!maskColor) {
|
|
477
|
-
maskColor = new Color(51, 51, 51, 230);
|
|
478
|
-
}
|
|
530
|
+
public openDialog(ui_name: string, maskColor: Color = new Color(51, 51, 51, 230), closeOnMaskClick: boolean = false): Node {
|
|
479
531
|
|
|
480
532
|
const whitePixelData = new Uint8Array([maskColor.r, maskColor.g, maskColor.b, maskColor.a]);
|
|
481
533
|
const texture = new Texture2D();
|
|
@@ -505,6 +557,18 @@ export class UIMgr extends BaseMgr {
|
|
|
505
557
|
if (node_dialog) {
|
|
506
558
|
this.node_dialogRoot.addChild(node_agent);
|
|
507
559
|
this.node_dialogRoot.active = true;
|
|
560
|
+
|
|
561
|
+
// 点击遮罩关闭 Dialog
|
|
562
|
+
if (closeOnMaskClick) {
|
|
563
|
+
let maskTouchCallback = () => {
|
|
564
|
+
this.closeDialog(ui_name, node_dialog);
|
|
565
|
+
};
|
|
566
|
+
// 将回调存到 node_mask 上,方便 closeDialog 时清理
|
|
567
|
+
(node_mask as any)._maskTouchCallback = maskTouchCallback;
|
|
568
|
+
if (this.inputMgr) {
|
|
569
|
+
this.inputMgr.onNodeTouchEnd(node_mask, maskTouchCallback, this);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
508
572
|
} else {
|
|
509
573
|
node_agent.destroy();
|
|
510
574
|
}
|
|
@@ -532,6 +596,14 @@ export class UIMgr extends BaseMgr {
|
|
|
532
596
|
|
|
533
597
|
if (hasNode) {
|
|
534
598
|
let node_agent: Node = node.parent;
|
|
599
|
+
// 清理点击遮罩关闭的监听器(如果存在)
|
|
600
|
+
let node_mask: Node = node_agent.getChildByName("Node_Mask");
|
|
601
|
+
if (node_mask && (node_mask as any)._maskTouchCallback) {
|
|
602
|
+
if (this.inputMgr) {
|
|
603
|
+
this.inputMgr.offNodeTouchEnd(node_mask, (node_mask as any)._maskTouchCallback, this);
|
|
604
|
+
}
|
|
605
|
+
(node_mask as any)._maskTouchCallback = null;
|
|
606
|
+
}
|
|
535
607
|
this.destroy_ui(ui_name, node);
|
|
536
608
|
node_agent.destroy();
|
|
537
609
|
}
|
|
@@ -560,6 +632,14 @@ export class UIMgr extends BaseMgr {
|
|
|
560
632
|
if (child.name !== "Node_Mask") {
|
|
561
633
|
let uiName = this._nodeToUIName(child);
|
|
562
634
|
if (uiName) {
|
|
635
|
+
// 清理点击遮罩关闭的监听器(如果存在)
|
|
636
|
+
let node_mask: Node = agent.getChildByName("Node_Mask");
|
|
637
|
+
if (node_mask && (node_mask as any)._maskTouchCallback) {
|
|
638
|
+
if (this.inputMgr) {
|
|
639
|
+
this.inputMgr.offNodeTouchEnd(node_mask, (node_mask as any)._maskTouchCallback, this);
|
|
640
|
+
}
|
|
641
|
+
(node_mask as any)._maskTouchCallback = null;
|
|
642
|
+
}
|
|
563
643
|
this.destroy_ui(uiName, child);
|
|
564
644
|
closedCount++;
|
|
565
645
|
break;
|
|
@@ -9,6 +9,7 @@ export class LogUtils {
|
|
|
9
9
|
static TAG: string = "LogUtils";
|
|
10
10
|
|
|
11
11
|
private isDebug: boolean = false;
|
|
12
|
+
private _timers: Map<string, number> = new Map();
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* 初始化日志模块
|
|
@@ -133,6 +134,32 @@ export class LogUtils {
|
|
|
133
134
|
return `[${level}][${tag}][${timestamp}]: ${content}`;
|
|
134
135
|
}
|
|
135
136
|
|
|
137
|
+
/**
|
|
138
|
+
* 开始计时
|
|
139
|
+
* @param label 计时标签
|
|
140
|
+
*/
|
|
141
|
+
public timeStart(label: string): void {
|
|
142
|
+
if (!this.isDebug) return;
|
|
143
|
+
this._timers.set(label, Date.now());
|
|
144
|
+
console.log(this.formatLog("TIMER", label, "计时开始"));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 结束计时并输出耗时
|
|
149
|
+
* @param label 计时标签(需与 timeStart 匹配)
|
|
150
|
+
*/
|
|
151
|
+
public timeEnd(label: string): void {
|
|
152
|
+
if (!this.isDebug) return;
|
|
153
|
+
const startTime = this._timers.get(label);
|
|
154
|
+
if (startTime === undefined) {
|
|
155
|
+
console.warn(this.formatLog("TIMER", label, "未找到计时起点"));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const elapsed = Date.now() - startTime;
|
|
159
|
+
this._timers.delete(label);
|
|
160
|
+
console.log(this.formatLog("TIMER", label, `耗时 ${elapsed}ms`));
|
|
161
|
+
}
|
|
162
|
+
|
|
136
163
|
/**
|
|
137
164
|
* 格式化值
|
|
138
165
|
*/
|