first
This commit is contained in:
142
extensions/app/assets/Core.ts
Normal file
142
extensions/app/assets/Core.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import { Component, Director, director, EventTarget, js } from 'cc';
|
||||
import { DEV, EDITOR } from 'cc/env';
|
||||
import * as debug from './lib/debug/debug';
|
||||
import logger from './lib/logger/logger';
|
||||
import storage from './lib/storage/storage';
|
||||
import task from './lib/task/task';
|
||||
import EventManager from './manager/event/EventManager';
|
||||
import LoaderManager from './manager/loader/LoaderManager';
|
||||
import SoundManager from './manager/sound/SoundManager';
|
||||
import TimerManager from './manager/timer/TimerManager';
|
||||
import UIManager from './manager/ui/UIManager';
|
||||
|
||||
enum EventType {
|
||||
/**AppInit准备完毕 */
|
||||
EVENT_APPINIT_FINISHED = 'EVENT_APPINIT_FINISHED',
|
||||
/**系统Manager初始化完毕 */
|
||||
EVENT_SYS_MANAGER_INITED = 'EVENT_SYS_MANAGER_INITED',
|
||||
/**用户Manager初始化完毕 */
|
||||
EVENT_USER_MANAGER_INITED = 'EVENT_USER_MANAGER_INITED',
|
||||
/**所有Manager初始化完毕 */
|
||||
EVENT_MANAGER_INITED = 'EVENT_MANAGER_INITED',
|
||||
/**所有Manager准备完毕 */
|
||||
EVENT_MANAGER_FINISHED = 'EVENT_MANAGER_FINISHED'
|
||||
}
|
||||
|
||||
type IData = { [key in string]: any };
|
||||
type IConfig = { [key in string]: any };
|
||||
type IStore = { [key in string]: any };
|
||||
type ICtrl = { [key in string]: any };
|
||||
|
||||
interface ITypeofManager {
|
||||
Loader: Omit<typeof LoaderManager, keyof Component>,
|
||||
Event: Omit<typeof EventManager, keyof Component>,
|
||||
Sound: Omit<typeof SoundManager, keyof Component>,
|
||||
Timer: Omit<typeof TimerManager, keyof Component>,
|
||||
UI: Omit<typeof UIManager, keyof Component>
|
||||
}
|
||||
|
||||
interface IManager {
|
||||
loader: Omit<LoaderManager, keyof Component>,
|
||||
event: Omit<EventManager, keyof Component>,
|
||||
sound: Omit<SoundManager<any, any>, keyof Component>,
|
||||
timer: Omit<TimerManager, keyof Component>,
|
||||
ui: Omit<UIManager<any, any>, keyof Component>
|
||||
}
|
||||
|
||||
interface ICore {
|
||||
data: IData,
|
||||
config: IConfig,
|
||||
store: IStore,
|
||||
controller: ICtrl,
|
||||
Controller: ICtrl,
|
||||
manager: IManager,
|
||||
Manager: ITypeofManager
|
||||
}
|
||||
|
||||
const Lib = { task, storage, debug, logger };
|
||||
const Config = {};
|
||||
const Data = {};
|
||||
const Store = {};
|
||||
const controller = {};
|
||||
const Controller = {};
|
||||
const Manager = {};
|
||||
const manager = {};
|
||||
|
||||
const eventTarget = new EventTarget();
|
||||
let EventMap = {};
|
||||
|
||||
director.on(Director.EVENT_RESET, () => {
|
||||
js.clear(Config);
|
||||
js.clear(Data);
|
||||
js.clear(Store);
|
||||
js.clear(controller);
|
||||
js.clear(Controller);
|
||||
js.clear(Manager);
|
||||
js.clear(manager);
|
||||
|
||||
EventMap = {};
|
||||
});
|
||||
|
||||
export default class Core<T extends ICore> {
|
||||
static EventType = EventType;
|
||||
|
||||
protected static _inst: Core<ICore> | undefined;
|
||||
static get inst() {
|
||||
if (!this._inst) this._inst = new Core();
|
||||
return this._inst;
|
||||
}
|
||||
|
||||
lib = Lib;
|
||||
config: T['config'] = null;
|
||||
data: T['data'] = null;
|
||||
store: T['store'] = null;
|
||||
Controller: T['Controller'] = null;
|
||||
controller: T['controller'] = null;
|
||||
Manager: T['Manager'] = null;
|
||||
manager: T['manager'] = null;
|
||||
|
||||
constructor() {
|
||||
this.config = Config;
|
||||
this.data = Data;
|
||||
this.store = Store;
|
||||
this.Controller = Controller;
|
||||
this.controller = controller;
|
||||
this.Manager = Manager as any;
|
||||
this.manager = manager as any;
|
||||
if (!EDITOR || DEV) {
|
||||
if (this.constructor !== Core && !js.getClassById('App')) {
|
||||
js.setClassAlias(this.constructor as any, 'App');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
on(event: keyof typeof EventType, callback: (...any: any[]) => void, target?: any): any {
|
||||
if (EventMap[event]) callback.call(target);
|
||||
eventTarget.on(event, callback, target);
|
||||
}
|
||||
|
||||
once(event: keyof typeof EventType, callback: Function, target?: any): any {
|
||||
if (EventMap[event]) {
|
||||
callback.call(target);
|
||||
} else {
|
||||
eventTarget.once(event, callback as any, target);
|
||||
}
|
||||
}
|
||||
|
||||
off(event: keyof typeof EventType, callback: (...any: any[]) => void, target?: any): any {
|
||||
eventTarget.off(event, callback, target);
|
||||
}
|
||||
|
||||
targetOff(target: any) {
|
||||
eventTarget.targetOff(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请不要手动调用
|
||||
*/
|
||||
static emit(event: keyof typeof EventType, ...args: any[]): any {
|
||||
EventMap[event] = true;
|
||||
eventTarget.emit(event, ...args);
|
||||
}
|
||||
}
|
||||
9
extensions/app/assets/Core.ts.meta
Normal file
9
extensions/app/assets/Core.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "b4a49372-fa9e-4448-b91b-29bc9701d9ff",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
9
extensions/app/assets/app.ts.meta
Normal file
9
extensions/app/assets/app.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "0ceacfb9-e86a-4e83-bd5d-15a4a27185c8",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
12
extensions/app/assets/base.meta
Normal file
12
extensions/app/assets/base.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "2fb050e0-159f-4a3b-b24c-3d76e43ac54d",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
275
extensions/app/assets/base/BaseAppInit.ts
Normal file
275
extensions/app/assets/base/BaseAppInit.ts
Normal file
@@ -0,0 +1,275 @@
|
||||
import { Button, Component, EventTouch, Node, Settings, _decorator, assetManager, isValid, settings, warn } from 'cc';
|
||||
import { EDITOR } from 'cc/env';
|
||||
import Core from '../Core';
|
||||
import BaseManager from './BaseManager';
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
const AdminBundleName = 'app-admin';
|
||||
const ModelBundleName = 'app-model';
|
||||
const ControlBundleName = 'app-control';
|
||||
const ControllerBundleName = 'app-controller';
|
||||
const ManagerBundleName = 'app-manager';
|
||||
const DontRewriteFuns = ['startInit', 'nextInit'];
|
||||
|
||||
@ccclass('BaseAppInit')
|
||||
export default abstract class BaseAppInit extends Component {
|
||||
private get _base_mgr_total() {
|
||||
return Math.max(0, BaseManager.getTotalAssetNum(assetManager.getBundle(ManagerBundleName)));
|
||||
}
|
||||
private get _base_user_total() {
|
||||
return Math.max(0, this.getUserAssetNum());
|
||||
}
|
||||
private get _base_total() {
|
||||
return this._base_mgr_total + this._base_user_total;
|
||||
}
|
||||
|
||||
private _base_mgr_completed = 0;
|
||||
private _base_user_completed = 0;
|
||||
private get _base_completed() {
|
||||
return this._base_mgr_completed + Math.min(this._base_user_total, this._base_user_completed);
|
||||
}
|
||||
|
||||
private _base_inited = false;
|
||||
private _base_finished = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
if (EDITOR) {
|
||||
DontRewriteFuns.forEach((funName) => {
|
||||
if (BaseAppInit.prototype[funName] !== this[funName]) {
|
||||
warn(`[AppInit] 不应该重写父类方法{${funName}}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [避免重写] 开始初始化
|
||||
*/
|
||||
protected startInit() {
|
||||
const projectBundles = settings.querySettings(Settings.Category.ASSETS, 'projectBundles') as string[];
|
||||
Core.inst.lib.task.createAny()
|
||||
// 预加载control、model、admin、manager
|
||||
.add([
|
||||
(next, retry) => {
|
||||
// 预加载control(废弃)
|
||||
if (projectBundles.indexOf(ControlBundleName) === -1) return next();
|
||||
assetManager.preloadAny({ url: ControlBundleName }, { ext: 'bundle' }, null, (err) => {
|
||||
if (err) return retry(0.1);
|
||||
next();
|
||||
});
|
||||
},
|
||||
(next, retry) => {
|
||||
// 预加载controller
|
||||
if (projectBundles.indexOf(ControllerBundleName) === -1) return next();
|
||||
assetManager.preloadAny({ url: ControllerBundleName }, { ext: 'bundle' }, null, (err) => {
|
||||
if (err) return retry(0.1);
|
||||
next();
|
||||
});
|
||||
},
|
||||
(next, retry) => {
|
||||
// 预加载model
|
||||
if (projectBundles.indexOf(ModelBundleName) === -1) return next();
|
||||
assetManager.preloadAny({ url: ModelBundleName }, { ext: 'bundle' }, null, (err) => {
|
||||
if (err) return retry(0.1);
|
||||
next();
|
||||
});
|
||||
},
|
||||
(next, retry) => {
|
||||
// 预加载admin
|
||||
if (projectBundles.indexOf(AdminBundleName) === -1) return next();
|
||||
assetManager.preloadAny({ url: AdminBundleName }, { ext: 'bundle' }, null, (err) => {
|
||||
if (err) return retry(0.1);
|
||||
next();
|
||||
});
|
||||
},
|
||||
(next, retry) => {
|
||||
// 预加载manage
|
||||
if (projectBundles.indexOf(ManagerBundleName) === -1) return next();
|
||||
assetManager.preloadAny({ url: ManagerBundleName }, { ext: 'bundle' }, null, (err) => {
|
||||
if (err) return retry(0.1);
|
||||
next();
|
||||
});
|
||||
}
|
||||
])
|
||||
// 加载control(废弃)
|
||||
.add((next, retry) => {
|
||||
if (projectBundles.indexOf(ControlBundleName) === -1) return next();
|
||||
assetManager.loadBundle(ControlBundleName, (err) => {
|
||||
if (err) return retry(0.1);
|
||||
next();
|
||||
});
|
||||
})
|
||||
// 加载controller
|
||||
.add((next, retry) => {
|
||||
if (projectBundles.indexOf(ControllerBundleName) === -1) return next();
|
||||
assetManager.loadBundle(ControllerBundleName, (err) => {
|
||||
if (err) return retry(0.1);
|
||||
next();
|
||||
});
|
||||
})
|
||||
// 加载model
|
||||
.add((next, retry) => {
|
||||
if (projectBundles.indexOf(ModelBundleName) === -1) return next();
|
||||
assetManager.loadBundle(ModelBundleName, (err) => {
|
||||
if (err) return retry(0.1);
|
||||
next();
|
||||
});
|
||||
})
|
||||
// 加载admin
|
||||
.add((next, retry) => {
|
||||
if (projectBundles.indexOf(AdminBundleName) === -1) return next();
|
||||
assetManager.loadBundle(AdminBundleName, (err) => {
|
||||
if (err) return retry(0.1);
|
||||
next();
|
||||
});
|
||||
})
|
||||
// 加载manager
|
||||
.add((next, retry) => {
|
||||
if (projectBundles.indexOf(ManagerBundleName) === -1) return next();
|
||||
assetManager.loadBundle(ManagerBundleName, (err) => {
|
||||
if (err) return retry(0.1);
|
||||
next();
|
||||
});
|
||||
})
|
||||
.start(() => {
|
||||
this._base_inited = true;
|
||||
this.onProgress(0, this._base_total);
|
||||
|
||||
// 初始化app, 使用complete来实现onUserInit的切换以确保manager已完全加载
|
||||
BaseManager.init(
|
||||
assetManager.getBundle(ManagerBundleName),
|
||||
() => {
|
||||
this.innerNextInit();
|
||||
},
|
||||
() => {
|
||||
this.onUserInit();
|
||||
if (this._base_completed < this._base_total) return;
|
||||
// 全部加载完成
|
||||
this.innerFinished();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* [不可重写] 用于内部初始化完成
|
||||
*/
|
||||
private innerFinished() {
|
||||
if (this._base_finished) return;
|
||||
this._base_finished = true;
|
||||
Core.emit(Core.EventType.EVENT_APPINIT_FINISHED);
|
||||
// 默认音效(Button点击触发, 这个方案可以正常触发input事件)
|
||||
if (Core.inst.Manager.Sound.setting.defaultEffectName) {
|
||||
const playDefaultEffect = function (e: EventTouch) {
|
||||
// SoundManager.setButtonEffect会将Button所在节点的useDefaultEffect设为false
|
||||
if (e.target['useDefaultEffect'] === false) return;
|
||||
Core.inst.manager.ui.onceUserInterface(Node.EventType.TOUCH_END, function (event: EventTouch) {
|
||||
if (!event.target.getComponent(Button)) return;
|
||||
setTimeout(() => {
|
||||
if (!isValid(Core.inst.manager.sound)) return;
|
||||
// 如果是scrollView中的button,在滑动后不播放点击音效
|
||||
if (event.eventPhase === EventTouch.CAPTURING_PHASE) return;
|
||||
Core.inst.manager.sound.playDefaultEffect();
|
||||
});
|
||||
}, null, true);
|
||||
};
|
||||
const onEnable = Button.prototype.onEnable;
|
||||
Button.prototype.onEnable = function () {
|
||||
onEnable.call(this);
|
||||
this.node.on(Node.EventType.TOUCH_START, playDefaultEffect);
|
||||
};
|
||||
const onDisable = Button.prototype.onDisable;
|
||||
Button.prototype.onDisable = function () {
|
||||
this.node.off(Node.EventType.TOUCH_START, playDefaultEffect);
|
||||
onDisable.call(this);
|
||||
};
|
||||
}
|
||||
return Core.inst.manager.ui.showDefault(() => {
|
||||
// 初始化完成
|
||||
this.onFinish();
|
||||
// 默认音乐(默认播放)
|
||||
if (Core.inst.Manager.Sound.setting.defaultMusicName) {
|
||||
const onTouch = function () {
|
||||
if (!isValid(Core.inst.manager.sound)) return;
|
||||
if (Core.inst.manager.sound.isMusicPlaying && !Core.inst.manager.sound.isMusicPaused) {
|
||||
Core.inst.manager.sound.replayMusic(() => {
|
||||
Core.inst.manager.ui.offUserInterface(Node.EventType.TOUCH_START, onTouch, this, true);
|
||||
});
|
||||
} else {
|
||||
Core.inst.manager.ui.offUserInterface(Node.EventType.TOUCH_START, onTouch, this, true);
|
||||
}
|
||||
};
|
||||
Core.inst.manager.ui.onUserInterface(Node.EventType.TOUCH_START, onTouch, this, true);
|
||||
Core.inst.manager.sound.playDefaultMusic(() => {
|
||||
Core.inst.manager.ui.offUserInterface(Node.EventType.TOUCH_START, onTouch, this, true);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* [不可重写] 用于内部初始化
|
||||
*/
|
||||
private innerNextInit() {
|
||||
// 完成+1
|
||||
this._base_mgr_completed += 1;
|
||||
// 进度回调
|
||||
this.onProgress(this._base_completed, this._base_total);
|
||||
}
|
||||
|
||||
/**
|
||||
* [避免重写] 初始化下一步,用户部分每完成一步需要调用一次
|
||||
*/
|
||||
protected nextInit(): any {
|
||||
if (this._base_finished) return;
|
||||
|
||||
if (!this._base_inited) {
|
||||
// 完成+1
|
||||
this._base_user_completed += 1;
|
||||
// 进度回调
|
||||
this.onProgress(this._base_completed, this._base_total);
|
||||
return;
|
||||
}
|
||||
|
||||
// 完成+1
|
||||
this._base_user_completed += 1;
|
||||
// 进度回调
|
||||
this.onProgress(this._base_completed, this._base_total);
|
||||
|
||||
// 全部加载完成
|
||||
if (this._base_completed >= this._base_total) {
|
||||
this.innerFinished();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////// 以下可重写 ////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* [可以重写] 默认start调用startInit,可以重写后自定义时机
|
||||
*/
|
||||
protected start(): any { this.startInit(); }
|
||||
|
||||
/**
|
||||
* [可以重写] 获得用户资源总量,这里返回几,就需要用户自行调用几次nextInit
|
||||
*/
|
||||
protected getUserAssetNum(): number { return 0; }
|
||||
|
||||
/**
|
||||
* [可以重写] 用户初始化函数,会在框架初始化完成后调用
|
||||
*/
|
||||
protected onUserInit(): any { return 0; }
|
||||
|
||||
/**
|
||||
* [可以重写] 监听进度
|
||||
* @param {Number} completed
|
||||
* @param {Number} total
|
||||
*/
|
||||
protected onProgress(completed: number, total: number): any { return completed / total; }
|
||||
|
||||
/**
|
||||
* [可以重写] 初始化完成
|
||||
*/
|
||||
protected onFinish() { }
|
||||
}
|
||||
9
extensions/app/assets/base/BaseAppInit.ts.meta
Normal file
9
extensions/app/assets/base/BaseAppInit.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "2110b07a-1c6c-430e-b9d9-926db06c8b30",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
179
extensions/app/assets/base/BaseControl.ts
Normal file
179
extensions/app/assets/base/BaseControl.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
class CallbackInfo {
|
||||
public callback: Function = null;
|
||||
public target: unknown = null;
|
||||
public once = false;
|
||||
|
||||
public constructor(callback: Function, target: unknown = null, once: boolean = false) {
|
||||
this.callback = callback;
|
||||
this.target = target;
|
||||
this.once = once;
|
||||
}
|
||||
}
|
||||
|
||||
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : any;
|
||||
type AnyFunc = (...args: any[]) => any;
|
||||
|
||||
class CallbackList {
|
||||
private callbacks: CallbackInfo[] = [];
|
||||
|
||||
public size() {
|
||||
return this.callbacks.length;
|
||||
}
|
||||
|
||||
public add(callback: Function, target: unknown = null, once: boolean = false) {
|
||||
this.callbacks.push(new CallbackInfo(callback, target, once));
|
||||
}
|
||||
|
||||
public emit(args: any[]) {
|
||||
for (let index = 0; index < this.callbacks.length; index++) {
|
||||
const info = this.callbacks[index];
|
||||
// 先移除
|
||||
if (info.once) {
|
||||
this.callbacks.splice(index, 1);
|
||||
--index;
|
||||
}
|
||||
if (info.callback) {
|
||||
info.callback.apply(info.target, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public call(args: any[]) {
|
||||
if (this.callbacks.length === 0) return;
|
||||
const info = this.callbacks[0];
|
||||
|
||||
// 先移除
|
||||
if (info.once) this.callbacks.splice(0, 1);
|
||||
if (!info.callback) return;
|
||||
|
||||
return info.callback.apply(info.target, args);
|
||||
}
|
||||
|
||||
public remove(callback: Function, target: unknown = null) {
|
||||
for (let index = this.callbacks.length - 1; index >= 0; index--) {
|
||||
const info = this.callbacks[index];
|
||||
if (info.callback !== callback || info.target !== target) continue;
|
||||
this.callbacks.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public removeByCallback(callback: Function) {
|
||||
for (let index = this.callbacks.length - 1; index >= 0; index--) {
|
||||
const info = this.callbacks[index];
|
||||
if (info.callback !== callback) continue;
|
||||
this.callbacks.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public removeByTarget(target: unknown) {
|
||||
for (let index = this.callbacks.length - 1; index >= 0; index--) {
|
||||
const info = this.callbacks[index];
|
||||
if (info.target !== target) continue;
|
||||
this.callbacks.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EventEmitter {
|
||||
private listeners: { [key in string]: CallbackList } = {};
|
||||
|
||||
public on(event: string | number, cb: (...data: any[]) => void, target?: unknown) {
|
||||
if (!event.toString() || !cb) return;
|
||||
if (!this.listeners[event]) this.listeners[event] = new CallbackList();
|
||||
this.listeners[event].add(cb, target);
|
||||
}
|
||||
|
||||
public once(event: string | number, cb: (...data: any[]) => void, target?: unknown) {
|
||||
if (!event.toString() || !cb) return;
|
||||
if (!this.listeners[event]) this.listeners[event] = new CallbackList();
|
||||
this.listeners[event].add(cb, target, true);
|
||||
}
|
||||
|
||||
public off(event: string | number, cb: (...data: any[]) => void, target?: unknown) {
|
||||
if (!event.toString() || !cb) return;
|
||||
if (!this.listeners[event]) return;
|
||||
|
||||
this.listeners[event].remove(cb, target);
|
||||
}
|
||||
|
||||
public targetOff(target?: unknown) {
|
||||
if (!target) return;
|
||||
|
||||
for (const key in this.listeners) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.listeners, key)) {
|
||||
const element = this.listeners[key];
|
||||
element.removeByTarget(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public emit(event: string | number, args: any[]) {
|
||||
if (!event.toString()) return;
|
||||
if (!this.listeners[event]) return;
|
||||
this.listeners[event].emit(args);
|
||||
}
|
||||
|
||||
public call(event: string | number, args: any[]) {
|
||||
if (!event.toString()) return;
|
||||
if (!this.listeners[event]) return;
|
||||
return this.listeners[event].call(args);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IBaseControl<C, E, T extends { [key in keyof E]?: AnyFunc }> {
|
||||
readonly inst: Readonly<C>
|
||||
|
||||
//用于类型提示推导////////////////
|
||||
new(): SuperBaseControl<E, T>//
|
||||
///////////////////////////////
|
||||
}
|
||||
|
||||
class SuperBaseControl<E, T extends { [key in keyof E]?: AnyFunc }> {
|
||||
//用于类型提示推导//
|
||||
private e: E;////
|
||||
private t: T;////
|
||||
/////////////////
|
||||
|
||||
private event = new EventEmitter();
|
||||
|
||||
protected call<K extends keyof E & keyof T>(key: E[K], ...args: Parameters<T[K]>): ReturnType<T[K]> {
|
||||
return this.event.call.call(this.event, key, args);
|
||||
}
|
||||
|
||||
protected emit<K extends keyof E & keyof T>(key: E[K], ...args: Parameters<T[K]>): void {
|
||||
return this.event.emit.call(this.event, key, args);
|
||||
}
|
||||
|
||||
private on(...args: any[]): void {
|
||||
return this.event.on.apply(this.event, args);
|
||||
}
|
||||
|
||||
private once(...args: any[]): void {
|
||||
return this.event.once.apply(this.event, args);
|
||||
}
|
||||
|
||||
private off(...args: any[]): void {
|
||||
return this.event.off.apply(this.event, args);
|
||||
}
|
||||
|
||||
private targetOff(target: any): void {
|
||||
return this.event.targetOff.call(this.event, target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 废弃,请使用Controller代替Control
|
||||
*/
|
||||
export default function BaseControl<C, E = any, T extends { [key in keyof E & string]?: AnyFunc } = any>(Event?: E) {
|
||||
return class BaseControl extends SuperBaseControl<E, T> {
|
||||
public static Event = Event;
|
||||
|
||||
private static _base_inst: Readonly<C> = null;
|
||||
public static get inst() {
|
||||
if (this._base_inst === null) {
|
||||
this._base_inst = new this() as C;
|
||||
}
|
||||
return this._base_inst;
|
||||
}
|
||||
};
|
||||
}
|
||||
9
extensions/app/assets/base/BaseControl.ts.meta
Normal file
9
extensions/app/assets/base/BaseControl.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "fb1039b9-17f2-43b5-ad26-8d74e691b4f5",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
211
extensions/app/assets/base/BaseController.ts
Normal file
211
extensions/app/assets/base/BaseController.ts
Normal file
@@ -0,0 +1,211 @@
|
||||
import { DEV } from 'cc/env';
|
||||
import { IReadOnly } from '../../../../assets/app-builtin/app-admin/executor';
|
||||
import { Logger } from '../lib/logger/logger';
|
||||
|
||||
class CallbackInfo {
|
||||
public callback: Function = null;
|
||||
public target: unknown = null;
|
||||
public once = false;
|
||||
|
||||
public constructor(callback: Function, target: unknown = null, once: boolean = false) {
|
||||
this.callback = callback;
|
||||
this.target = target;
|
||||
this.once = once;
|
||||
}
|
||||
}
|
||||
|
||||
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : any;
|
||||
type AnyFunc = (...args: any[]) => any;
|
||||
|
||||
class CallbackList {
|
||||
private callbacks: CallbackInfo[] = [];
|
||||
|
||||
public size() {
|
||||
return this.callbacks.length;
|
||||
}
|
||||
|
||||
public add(callback: Function, target: unknown = null, once: boolean = false) {
|
||||
this.callbacks.push(new CallbackInfo(callback, target, once));
|
||||
}
|
||||
|
||||
public emit(args: any[]) {
|
||||
for (let index = 0; index < this.callbacks.length; index++) {
|
||||
const info = this.callbacks[index];
|
||||
// 先移除
|
||||
if (info.once) {
|
||||
this.callbacks.splice(index, 1);
|
||||
--index;
|
||||
}
|
||||
if (info.callback) {
|
||||
info.callback.apply(info.target, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public call(args: any[]) {
|
||||
if (this.callbacks.length === 0) return;
|
||||
const info = this.callbacks[0];
|
||||
|
||||
// 先移除
|
||||
if (info.once) this.callbacks.splice(0, 1);
|
||||
if (!info.callback) return;
|
||||
|
||||
return info.callback.apply(info.target, args);
|
||||
}
|
||||
|
||||
public remove(callback: Function, target: unknown = null) {
|
||||
for (let index = this.callbacks.length - 1; index >= 0; index--) {
|
||||
const info = this.callbacks[index];
|
||||
if (info.callback !== callback || info.target !== target) continue;
|
||||
this.callbacks.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public removeByCallback(callback: Function) {
|
||||
for (let index = this.callbacks.length - 1; index >= 0; index--) {
|
||||
const info = this.callbacks[index];
|
||||
if (info.callback !== callback) continue;
|
||||
this.callbacks.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public removeByTarget(target: unknown) {
|
||||
for (let index = this.callbacks.length - 1; index >= 0; index--) {
|
||||
const info = this.callbacks[index];
|
||||
if (info.target !== target) continue;
|
||||
this.callbacks.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EventEmitter {
|
||||
private listeners: { [key in string]: CallbackList } = {};
|
||||
|
||||
public on(event: string | number, cb: (...data: any[]) => void, target?: unknown) {
|
||||
if (!event.toString() || !cb) return;
|
||||
if (!this.listeners[event]) this.listeners[event] = new CallbackList();
|
||||
this.listeners[event].add(cb, target);
|
||||
}
|
||||
|
||||
public once(event: string | number, cb: (...data: any[]) => void, target?: unknown) {
|
||||
if (!event.toString() || !cb) return;
|
||||
if (!this.listeners[event]) this.listeners[event] = new CallbackList();
|
||||
this.listeners[event].add(cb, target, true);
|
||||
}
|
||||
|
||||
public off(event: string | number, cb: (...data: any[]) => void, target?: unknown) {
|
||||
if (!event.toString() || !cb) return;
|
||||
if (!this.listeners[event]) return;
|
||||
|
||||
this.listeners[event].remove(cb, target);
|
||||
}
|
||||
|
||||
public targetOff(target?: unknown) {
|
||||
if (!target) return;
|
||||
|
||||
for (const key in this.listeners) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.listeners, key)) {
|
||||
const element = this.listeners[key];
|
||||
element.removeByTarget(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public emit(event: string | number, args: any[]) {
|
||||
if (!event.toString()) return;
|
||||
if (!this.listeners[event]) return;
|
||||
this.listeners[event].emit(args);
|
||||
}
|
||||
|
||||
public call(event: string | number, args: any[]) {
|
||||
if (!event.toString()) return;
|
||||
if (!this.listeners[event]) return;
|
||||
return this.listeners[event].call(args);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IBaseController<C, T extends { [key in string]?: AnyFunc }> {
|
||||
readonly inst: Readonly<C>
|
||||
|
||||
//用于类型提示推导////////////////
|
||||
new(): SuperBaseController<T>//
|
||||
///////////////////////////////
|
||||
}
|
||||
|
||||
class SuperBaseController<T extends { [key in string]?: AnyFunc }> {
|
||||
//用于类型提示推导//
|
||||
private t: T;////
|
||||
/////////////////
|
||||
|
||||
private event = new EventEmitter();
|
||||
|
||||
/**获取第一个事件回调的返回值 */
|
||||
protected call<K extends keyof T>(key: K, ...args: Parameters<T[K]>): ReturnType<T[K]> {
|
||||
return this.event.call.call(this.event, key, args);
|
||||
}
|
||||
|
||||
/**发射事件 */
|
||||
protected emit<K extends keyof T>(key: K, ...args: Parameters<T[K]>): void {
|
||||
return this.event.emit.call(this.event, key, args);
|
||||
}
|
||||
|
||||
private on(...args: any[]): void {
|
||||
return this.event.on.apply(this.event, args);
|
||||
}
|
||||
|
||||
private once(...args: any[]): void {
|
||||
return this.event.once.apply(this.event, args);
|
||||
}
|
||||
|
||||
private off(...args: any[]): void {
|
||||
return this.event.off.apply(this.event, args);
|
||||
}
|
||||
|
||||
private targetOff(target: any): void {
|
||||
return this.event.targetOff.call(this.event, target);
|
||||
}
|
||||
|
||||
/**打印日志 */
|
||||
protected get log(): Function {
|
||||
return Logger.create('log', '#4682b4', DEV ? `[${this['constructor'].name}] LOG` : `[${this['constructor'].name}] [LOG]`);
|
||||
}
|
||||
|
||||
/**打印警告 */
|
||||
protected get warn(): Function {
|
||||
return Logger.create('warn', '#ff7f50', DEV ? `[${this['constructor'].name}] WARN` : `[${this['constructor'].name}] [WARN]`);
|
||||
}
|
||||
|
||||
/**打印错误 */
|
||||
protected get error(): Function {
|
||||
return Logger.create('error', '#ff4757', DEV ? `[${this['constructor'].name}] ERROR` : `[${this['constructor'].name}] [ERROR]`);
|
||||
}
|
||||
}
|
||||
|
||||
export default function BaseController<C, T extends { [key in string]?: AnyFunc } = any>() {
|
||||
return class BaseController extends SuperBaseController<T> {
|
||||
/**
|
||||
* 控制器事件
|
||||
*/
|
||||
public static Event: { [key in keyof T]: key } = new Proxy({} as any, {
|
||||
get: function (target, key) {
|
||||
if (target[key]) return target[key];
|
||||
target[key] = key;
|
||||
return key;
|
||||
}
|
||||
});
|
||||
|
||||
private static _base_inst: IReadOnly<C> = null;
|
||||
/**
|
||||
* 控制器单例
|
||||
* - 尽量使用app.controller,可以避免因跨Bundle引用导致的问题,也可以避免Controller之间循环引用的问题
|
||||
*/
|
||||
public static get inst() {
|
||||
return this._base_inst;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
BaseController._base_inst = this as any;
|
||||
}
|
||||
};
|
||||
}
|
||||
9
extensions/app/assets/base/BaseController.ts.meta
Normal file
9
extensions/app/assets/base/BaseController.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "a7da7081-e604-487e-8e0c-7d458fdbb356",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
359
extensions/app/assets/base/BaseManager.ts
Normal file
359
extensions/app/assets/base/BaseManager.ts
Normal file
@@ -0,0 +1,359 @@
|
||||
import { AssetManager, Component, EventTarget, Prefab, Widget, _decorator, error, instantiate, js, path, warn } from 'cc';
|
||||
import { DEBUG, DEV, EDITOR } from 'cc/env';
|
||||
import Core from '../Core';
|
||||
import { Logger } from '../lib/logger/logger';
|
||||
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
const UserManagerPath = 'UserManager';
|
||||
const DontRewriteFuns = ['emit', 'on', 'once', 'off', 'targetOff'];
|
||||
|
||||
const uuid = new class UUID {
|
||||
private index = 0;
|
||||
|
||||
public create(): string {
|
||||
if (this.index++ > 10000000) {
|
||||
this.index = 0;
|
||||
}
|
||||
return `${Date.now()}-${this.index}`;
|
||||
}
|
||||
};
|
||||
|
||||
const loadBegin = Logger.create('log', '#32cd32', DEV ? '[BaseManager] 下载开始' : '[BaseManager] [下载开始]');
|
||||
|
||||
const loadFinish = Logger.create('log', '#00ae9d', DEV ? '[BaseManager] 下载完成' : '[BaseManager] [下载完成]');
|
||||
|
||||
const loadError = Logger.create('log', '#ff4757', DEV ? '[BaseManager] 下载失败' : '[BaseManager] [下载失败]');
|
||||
|
||||
const initBegin = Logger.create('log', '#3e4145', DEV ? '[BaseManager] 初始化开始' : '[BaseManager] [初始化开始]');
|
||||
|
||||
const initFinish = Logger.create('log', '#008080', DEV ? '[BaseManager] 初始化完成' : '[BaseManager] [初始化完成]');
|
||||
|
||||
@ccclass('BaseManager')
|
||||
export default class BaseManager extends Component {
|
||||
// 事件管理器
|
||||
private _base_event: EventTarget = new EventTarget();
|
||||
|
||||
// manager名字
|
||||
private _base_manager_name: string = js.getClassName(this);
|
||||
public get managerName() {
|
||||
return this._base_manager_name;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if (EDITOR) {
|
||||
DontRewriteFuns.forEach((funName) => {
|
||||
if (BaseManager.prototype[funName] !== this[funName]) {
|
||||
warn(`[${this._base_manager_name}] 不应该重写父类方法{${funName}}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this._base_manager_name !== 'Manager' && this._base_manager_name.slice(-7) === 'Manager') {
|
||||
const managerName = this._base_manager_name.slice(0, - 7);
|
||||
Core.inst.Manager[managerName] = this.constructor;
|
||||
Core.inst.manager[managerName.toLowerCase()] = this;
|
||||
} else if (EDITOR) {
|
||||
error(`[${this._base_manager_name}] manager命名错误(应为 xxxxManager 以Manager结尾)`);
|
||||
} else if (DEBUG) {
|
||||
error(`[${this._base_manager_name}] manager命名错误(应为 xxxxManager 以Manager结尾)`);
|
||||
}
|
||||
}
|
||||
|
||||
// 用来初始化组件或节点的一些属性,当该组件被第一次添加到节点上或用户点击了它的 Reset 菜单时调用。这个回调只会在编辑器下调用。
|
||||
resetInEditor() {
|
||||
const widget = this.node.getComponent(Widget) || this.node.addComponent(Widget);
|
||||
widget.isAlignBottom = true;
|
||||
widget.isAlignLeft = true;
|
||||
widget.isAlignRight = true;
|
||||
widget.isAlignTop = true;
|
||||
widget.top = 0;
|
||||
widget.left = 0;
|
||||
widget.right = 0;
|
||||
widget.bottom = 0;
|
||||
widget.alignMode = Widget.AlignMode.ON_WINDOW_RESIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* [无序] 自身初始化完成, init执行完毕后被调用
|
||||
*/
|
||||
protected onInited() {
|
||||
}
|
||||
|
||||
/**
|
||||
* [无序] 所有manager初始化完成
|
||||
*/
|
||||
protected onFinished() {
|
||||
}
|
||||
|
||||
/**
|
||||
* [无序] 初始化manager,在初始化完成后,调用finish方法
|
||||
* @param {Function} finish
|
||||
*/
|
||||
protected init(finish?: Function) {
|
||||
finish && finish();
|
||||
}
|
||||
|
||||
protected createUUID() {
|
||||
return uuid.create();
|
||||
}
|
||||
|
||||
/**打印日志 */
|
||||
protected get log() {
|
||||
if (DEV) {
|
||||
return window.console.log.bind(window.console,
|
||||
'%c %s %c %s ',
|
||||
'background:#4169e1; padding: 2px; border-radius: 5px 0 0 5px; border: 1px solid #4169e1; color: #fff; font-weight: normal;',
|
||||
`[${this._base_manager_name}] LOG ${new Date().toLocaleString()}`,
|
||||
'background:#ffffff ; padding: 2px; border-radius: 0 5px 5px 0; border: 1px solid #4169e1; color: #4169e1; font-weight: normal;'
|
||||
);
|
||||
}
|
||||
return window.console.log.bind(window.console,
|
||||
`[${this._base_manager_name}] [LOG] [${new Date().toLocaleString()}]`,
|
||||
);
|
||||
}
|
||||
|
||||
/**打印警告 */
|
||||
protected get warn() {
|
||||
if (DEV) {
|
||||
return window.console.warn.bind(window.console,
|
||||
'%c %s %c %s ',
|
||||
'background:#ff7f50; padding: 2px; border-radius: 5px 0 0 5px; border: 1px solid #ff7f50; color: #fff; font-weight: normal;',
|
||||
`[${this._base_manager_name}] WARN ${new Date().toLocaleString()}`,
|
||||
'background:#ffffff ; padding: 2px; border-radius: 0 5px 5px 0; border: 1px solid #ff7f50; color: #ff7f50; font-weight: normal;'
|
||||
);
|
||||
}
|
||||
return window.console.warn.bind(window.console,
|
||||
`[${this._base_manager_name}] [WARN] [${new Date().toLocaleString()}]`,
|
||||
);
|
||||
}
|
||||
|
||||
/**打印错误 */
|
||||
protected get error() {
|
||||
if (DEV) {
|
||||
return window.console.error.bind(window.console,
|
||||
'%c %s %c %s ',
|
||||
'background:#ff4757; padding: 2px; border-radius: 5px 0 0 5px; border: 1px solid #ff4757; color: #fff; font-weight: normal;',
|
||||
`[${this._base_manager_name}] ERROR ${new Date().toLocaleString()}`,
|
||||
'background:#ffffff ; padding: 2px; border-radius: 0 5px 5px 0; border: 1px solid #ff4757; color: #ff4757; font-weight: normal;'
|
||||
);
|
||||
}
|
||||
return window.console.error.bind(window.console,
|
||||
`[${this._base_manager_name}] [ERROR] [${new Date().toLocaleString()}]`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* [系统内置] 事件分发
|
||||
*/
|
||||
public emit(event: string | number, ...data: any[]) {
|
||||
this._base_event.emit(event as any, ...data);
|
||||
}
|
||||
|
||||
/**
|
||||
* [系统内置] 事件监听
|
||||
*/
|
||||
public on(event: string | number, cb: (...any: any[]) => void, target?: any) {
|
||||
this._base_event.on(event as any, cb, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* [系统内置] 事件监听
|
||||
*/
|
||||
public once(event: string | number, cb: (...any: any[]) => void, target?: any) {
|
||||
this._base_event.once(event as any, cb, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* [系统内置] 事件移除监听
|
||||
*/
|
||||
public off(event: string | number, cb?: (...any: any[]) => void, target?: any) {
|
||||
this._base_event.off(event as any, cb, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* [系统内置] 事件移除监听
|
||||
*/
|
||||
public targetOff(target: any) {
|
||||
this._base_event.targetOff(target);
|
||||
}
|
||||
|
||||
/***********************************静态***********************************/
|
||||
/**
|
||||
* 框架内置Manager的数量
|
||||
* @private
|
||||
*/
|
||||
public static get sysMgrCount() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得初始化资源的数量(包括sysMgrCount)
|
||||
* @private
|
||||
*/
|
||||
public static getTotalAssetNum(bundle: AssetManager.Bundle) {
|
||||
let count = this.sysMgrCount;
|
||||
|
||||
if (!bundle) return count;
|
||||
|
||||
const array = bundle.getDirWithPath('/', Prefab) as { uuid: string, path: string, ctor: Function }[];
|
||||
|
||||
array.forEach(function (item) {
|
||||
if (item.path.endsWith('Manager')) {
|
||||
count++;
|
||||
}
|
||||
});
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得初始化资源的数量
|
||||
* @private
|
||||
*/
|
||||
public static getUserAssetUrls(bundle: AssetManager.Bundle) {
|
||||
const pathArr: string[] = [];
|
||||
|
||||
if (!bundle) return pathArr;
|
||||
|
||||
const array = bundle.getDirWithPath('/', Prefab) as { uuid: string, path: string, ctor: Function }[];
|
||||
|
||||
array.forEach(function (item) {
|
||||
if (item.path.endsWith('Manager')) {
|
||||
pathArr.push(item.path);
|
||||
}
|
||||
});
|
||||
|
||||
return pathArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法,初始化manager,该方法必须在场景初始化完毕之后调用
|
||||
* @private
|
||||
*/
|
||||
public static init(
|
||||
bundle: AssetManager.Bundle,
|
||||
progress: (completeAsset: Number, totalAsset: Number) => any,
|
||||
complete: (totalAsset: Number) => any) {
|
||||
const urls = this.getUserAssetUrls(bundle);
|
||||
|
||||
const totalAsset = urls.length + this.sysMgrCount;
|
||||
let completeAsset = 0;
|
||||
|
||||
const onProgress = function (next: Function, manager: BaseManager) {
|
||||
if (DEBUG) {
|
||||
const startTime = window?.performance?.now ? performance.now() : Date.now();
|
||||
initBegin(manager.managerName);
|
||||
return function () {
|
||||
manager.onInited();
|
||||
if (DEBUG) {
|
||||
const endTime = window?.performance?.now ? performance.now() : Date.now();
|
||||
initFinish(`${manager.managerName} 耗时:${(endTime - startTime).toFixed(6)} ms`);
|
||||
}
|
||||
progress && progress(++completeAsset, totalAsset);
|
||||
next();
|
||||
};
|
||||
}
|
||||
return function () {
|
||||
manager.onInited();
|
||||
progress && progress(++completeAsset, totalAsset);
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
// 用户manager(动态添加)
|
||||
const userMgrList: BaseManager[] = [];
|
||||
// 系统manager(静态内置)
|
||||
const sysMgrList: BaseManager[] = [Core.inst.manager.event, Core.inst.manager.timer, Core.inst.manager.loader, Core.inst.manager.ui, Core.inst.manager.sound] as any as BaseManager[];
|
||||
|
||||
// 初始化系统manager
|
||||
const initSysMgrTask = Core.inst.lib.task.createASync();
|
||||
sysMgrList.forEach(function (manager: BaseManager) {
|
||||
initSysMgrTask.add(function (next) {
|
||||
manager.init(onProgress(next, manager));
|
||||
});
|
||||
});
|
||||
|
||||
// 加载用户manager
|
||||
const loadUserMgrTask = Core.inst.lib.task.createASync();
|
||||
const UserManagerRoot = Core.inst.manager.ui.root.getChildByPath(UserManagerPath);
|
||||
urls.forEach(function (url) {
|
||||
loadUserMgrTask.add(function (next, retry) {
|
||||
if (DEBUG) {
|
||||
const managerName = path.basename(url);
|
||||
const startTime = window?.performance?.now ? performance.now() : Date.now();
|
||||
loadBegin(managerName);
|
||||
bundle.load(url, Prefab, function (err, prefab: Prefab) {
|
||||
if (err || !prefab) {
|
||||
loadError(managerName, '重试...');
|
||||
retry(1);
|
||||
} else {
|
||||
const endTime = window?.performance?.now ? performance.now() : Date.now();
|
||||
loadFinish(`${managerName} 耗时:${(endTime - startTime).toFixed(6)} ms`);
|
||||
const node = instantiate(prefab);
|
||||
node.parent = UserManagerRoot;
|
||||
node.active = true;
|
||||
userMgrList.push(node.getComponent(BaseManager));
|
||||
next();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
bundle.load(url, Prefab, function (err, prefab: Prefab) {
|
||||
if (err || !prefab) {
|
||||
loadError(path.basename(url), '重试...');
|
||||
retry(1);
|
||||
} else {
|
||||
const node = instantiate(prefab);
|
||||
node.parent = UserManagerRoot;
|
||||
node.active = true;
|
||||
userMgrList.push(node.getComponent(BaseManager));
|
||||
next();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Core.inst.lib.task.createAny()
|
||||
.add([
|
||||
next => initSysMgrTask.start(next),
|
||||
next => loadUserMgrTask.start(next),
|
||||
])
|
||||
.add(function (next) {
|
||||
Core.emit(Core.EventType.EVENT_SYS_MANAGER_INITED);
|
||||
next();
|
||||
})
|
||||
.add(function (next) {
|
||||
// 初始化用户manager
|
||||
const initUserMgrTask = Core.inst.lib.task.createASync();
|
||||
userMgrList.forEach(manager => {
|
||||
initUserMgrTask.add(function (next) {
|
||||
manager.init(onProgress(next, manager));
|
||||
});
|
||||
});
|
||||
initUserMgrTask.start(next);
|
||||
})
|
||||
.add(function (next) {
|
||||
Core.emit(Core.EventType.EVENT_USER_MANAGER_INITED);
|
||||
Core.emit(Core.EventType.EVENT_MANAGER_INITED);
|
||||
next();
|
||||
})
|
||||
.add(function (next) {
|
||||
// 所有manager初始化完成后,触发回调
|
||||
sysMgrList.forEach(function (manager) {
|
||||
manager.onFinished();
|
||||
});
|
||||
userMgrList.forEach(function (manager) {
|
||||
manager.onFinished();
|
||||
});
|
||||
next();
|
||||
})
|
||||
.start(function () {
|
||||
Core.emit(Core.EventType.EVENT_MANAGER_FINISHED);
|
||||
complete && complete(totalAsset);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
9
extensions/app/assets/base/BaseManager.ts.meta
Normal file
9
extensions/app/assets/base/BaseManager.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "30205eac-9b1e-4f44-8081-7b70ff8d6c52",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
43
extensions/app/assets/base/BaseModel.ts
Normal file
43
extensions/app/assets/base/BaseModel.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
// export type IModel<T> = {
|
||||
// [P in keyof T]: T[P] extends Function
|
||||
// ? '❌此处不能定义任何方法'
|
||||
// : (
|
||||
// T[P] extends Array<infer R>
|
||||
// ? (
|
||||
// R extends Function
|
||||
// ? '❌此处不能定义任何方法'
|
||||
// : T[P]
|
||||
// )
|
||||
// : T[P] // IModel<T[P]> 性能消耗大
|
||||
// );
|
||||
// };
|
||||
|
||||
// export type IStore<T> = {
|
||||
// [P in keyof T]: T[P] extends Function
|
||||
// ? T[P]
|
||||
// : (
|
||||
// T[P] extends Array<infer R>
|
||||
// ? (
|
||||
// R extends Function
|
||||
// ? '❌此处不能定义任何方法'
|
||||
// : IModel<T[P]>
|
||||
// )
|
||||
// : IModel<T[P]>
|
||||
// );
|
||||
// };
|
||||
|
||||
export type IModel<T> = {
|
||||
[P in keyof T]: T[P] extends Function
|
||||
? '❌此处不能定义任何方法'
|
||||
: T[P];
|
||||
};
|
||||
|
||||
// export type IStore<T> = {
|
||||
// [P in keyof T]: T[P] extends Function
|
||||
// ? T[P]
|
||||
// : IModel<T[P]>;
|
||||
// };
|
||||
|
||||
export type IStore<T> = {
|
||||
[P in keyof T]: T[P];
|
||||
};
|
||||
9
extensions/app/assets/base/BaseModel.ts.meta
Normal file
9
extensions/app/assets/base/BaseModel.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "7a377ceb-e086-4e71-99bd-f44dab40d24f",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
1035
extensions/app/assets/base/BaseView.ts
Normal file
1035
extensions/app/assets/base/BaseView.ts
Normal file
@@ -0,0 +1,1035 @@
|
||||
import { Asset, Component, Enum, EventTouch, Font, Label, Node, Scene, Sprite, SpriteFrame, UITransform, Widget, _decorator, director, isValid, js, sp } from 'cc';
|
||||
import { DEV, EDITOR } from 'cc/env';
|
||||
import { IMiniViewName, IMiniViewNames, IViewName } from '../../../../assets/app-builtin/app-admin/executor';
|
||||
import Core from '../Core';
|
||||
import { Logger } from '../lib/logger/logger';
|
||||
import { IBaseControl } from './BaseControl';
|
||||
import { IBaseController } from './BaseController';
|
||||
|
||||
const { ccclass, property, disallowMultiple } = _decorator;
|
||||
|
||||
const BlockEvents = [
|
||||
Node.EventType.TOUCH_START, Node.EventType.TOUCH_MOVE, Node.EventType.TOUCH_END, Node.EventType.TOUCH_CANCEL,
|
||||
Node.EventType.MOUSE_DOWN, Node.EventType.MOUSE_MOVE, Node.EventType.MOUSE_UP,
|
||||
Node.EventType.MOUSE_ENTER, Node.EventType.MOUSE_LEAVE, Node.EventType.MOUSE_WHEEL
|
||||
];
|
||||
|
||||
const HideEvent = Enum({
|
||||
destroy: 1,
|
||||
active: 2
|
||||
});
|
||||
|
||||
export type IShade = {
|
||||
/**等待 默认0秒 */
|
||||
delay?: number,
|
||||
/**开始透明度 默认60 */
|
||||
begin?: number,
|
||||
/**结束透明度 默认180 */
|
||||
end?: number,
|
||||
/**透明变化速度 默认100 */
|
||||
speed?: number,
|
||||
/**
|
||||
* 毛玻璃效果 默认false
|
||||
* - 开启后其它参数将无效
|
||||
*/
|
||||
blur?: boolean,
|
||||
};
|
||||
|
||||
export interface IShowParamAttr {
|
||||
zIndex?: number,
|
||||
siblingIndex?: number,
|
||||
}
|
||||
|
||||
export interface IShowParamOnShow<T = any> {
|
||||
(result: T): any
|
||||
}
|
||||
|
||||
export interface IShowParamOnHide<T = any> {
|
||||
(result: T): any
|
||||
}
|
||||
|
||||
export interface IShowParamBeforeShow {
|
||||
(error: string): any
|
||||
}
|
||||
|
||||
export interface IShowParamInnerLoad {
|
||||
(name: string, path: string, type: { prototype: Asset }, callback: (result: Asset) => any): void
|
||||
}
|
||||
|
||||
export interface IHideParamOnHide<T = any> {
|
||||
(result: T): any
|
||||
}
|
||||
|
||||
export type IViewType = 'Page' | 'Paper' | 'Pop' | 'Top';
|
||||
|
||||
export enum ViewType {
|
||||
Page = 'Page',
|
||||
Paper = 'Paper',
|
||||
PaperAll = 'PaperAll',
|
||||
Pop = 'Pop',
|
||||
Top = 'Top'
|
||||
}
|
||||
|
||||
interface IMiniOnShow {
|
||||
(name: IMiniViewName, data?: any): any
|
||||
}
|
||||
interface IMiniOnHide {
|
||||
(name: IMiniViewName, data?: any): any
|
||||
}
|
||||
interface IMiniOnFinish {
|
||||
(): any
|
||||
}
|
||||
type IPick<T> = {
|
||||
-readonly [P in keyof T]: T[P] extends Function
|
||||
? T[P]
|
||||
: (T[P] extends Object
|
||||
? IPick<T[P]>
|
||||
: T[P]);
|
||||
};
|
||||
interface IBaseViewController<C, T extends { [key in string]: any }> {
|
||||
new(): BaseView & {
|
||||
readonly controller: IPick<C> & Readonly<{
|
||||
/**获取第一个事件回调的返回值 */
|
||||
emit<K extends keyof T>(key: K, ...args: Parameters<T[K]>): void;
|
||||
/**发射事件 */
|
||||
call<K extends keyof T & keyof T>(key: K, ...args: Parameters<T[K]>): ReturnType<T[K]>;
|
||||
/**注册事件回调 */
|
||||
on<K extends keyof T>(key: K, callback: (...args: Parameters<T[K]>) => ReturnType<T[K]>, target?: any): void;
|
||||
/**注册一次性事件回调 */
|
||||
once<K extends keyof T>(key: K, callback: (...args: Parameters<T[K]>) => ReturnType<T[K]>, target?: any): void;
|
||||
/**取消事件回调 */
|
||||
off(key: keyof T, callback: Function, target?: any): void;
|
||||
/**取消事件回调 */
|
||||
targetOff(target: any): void;
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
enum ViewState {
|
||||
BeforeShow,
|
||||
Showing,
|
||||
Showed,
|
||||
BeforeHide,
|
||||
Hiding,
|
||||
Hid,
|
||||
}
|
||||
|
||||
const Group = { id: 'BaseView', name: 'Settings', displayOrder: -Infinity, style: 'section' };
|
||||
|
||||
// 记录PaperAll的owner
|
||||
const PaperAllToOwner: Map<IMiniViewName, string> = new Map();
|
||||
|
||||
@ccclass('BaseView')
|
||||
@disallowMultiple()
|
||||
export default class BaseView extends Component {
|
||||
/**
|
||||
* @deprecated 废弃,请使用BindController代替BindControl
|
||||
*/
|
||||
static BindControl<C, E, T extends { [key in keyof E]?: any }>(Control: IBaseControl<C, E, T>) {
|
||||
return class BindControl extends BaseView {
|
||||
protected get control(): Pick<C, keyof C> & Readonly<{
|
||||
call<K extends keyof E>(key: E[K], ...args: Parameters<T[K]>): ReturnType<T[K]>;
|
||||
emit<K extends keyof E>(key: E[K], ...args: Parameters<T[K]>): void;
|
||||
on<K extends keyof E>(key: E[K], callback: (...args: Parameters<T[K]>) => ReturnType<T[K]>, target?: any): void;
|
||||
once<K extends keyof E>(key: E[K], callback: (...args: Parameters<T[K]>) => ReturnType<T[K]>, target?: any): void;
|
||||
off(key: E[keyof E], callback: Function, target?: any): void;
|
||||
targetOff(target: any): void;
|
||||
}> {
|
||||
return Control ? Control.inst as any : null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 给UI绑定一个控制器,绑定后可以通过this.controller访问,并能访问一些内部方法(emit、on、once、off、targetOff)
|
||||
*/
|
||||
static BindController<C, T extends { [key in string]: any }>(Controller: IBaseController<C, T>) {
|
||||
@disallowMultiple()
|
||||
class BindController extends BaseView {
|
||||
protected get controller() {
|
||||
return Controller ? Controller.inst as any : null;
|
||||
}
|
||||
}
|
||||
return BindController as any as IBaseViewController<C, T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有效,如果返回false的话,app.manager.ui.show会触发onError回调
|
||||
*/
|
||||
public static isViewValid(next: (valid: boolean) => void, data: any) {
|
||||
data;
|
||||
next && next(true);
|
||||
}
|
||||
|
||||
public static isPage(name: string) {
|
||||
return name.indexOf(ViewType.Page) === 0;
|
||||
}
|
||||
|
||||
public static isPaper(name: string) {
|
||||
return name.indexOf(ViewType.Paper) === 0;
|
||||
}
|
||||
|
||||
public static isPaperAll(name: string) {
|
||||
return name.indexOf(ViewType.PaperAll) === 0;
|
||||
}
|
||||
|
||||
public static isPop(name: string) {
|
||||
return name.indexOf(ViewType.Pop) === 0;
|
||||
}
|
||||
|
||||
public static isTop(name: string) {
|
||||
return name.indexOf(ViewType.Top) === 0;
|
||||
}
|
||||
|
||||
// 是否被调用过
|
||||
private _base_view_created = false;
|
||||
// view状态
|
||||
private _base_view_state = ViewState.Hid;
|
||||
// 当前view的名字
|
||||
private _base_view_name: IViewName | IMiniViewName = js.getClassName(this) as any;
|
||||
// 触摸是否有效
|
||||
private _base_touch_enable = true;
|
||||
// show/hide等待列表
|
||||
private _base_show_hide_delays: Function[] = [];
|
||||
// 子界面融合相关
|
||||
private _base_mini_show: Set<IMiniViewName> = new Set();
|
||||
|
||||
protected isPage() {
|
||||
return BaseView.isPage(this._base_view_name);
|
||||
}
|
||||
|
||||
protected isPaper() {
|
||||
return BaseView.isPaper(this._base_view_name);
|
||||
}
|
||||
|
||||
protected isPaperAll() {
|
||||
return BaseView.isPaperAll(this._base_view_name);
|
||||
}
|
||||
|
||||
protected isPop() {
|
||||
return BaseView.isPop(this._base_view_name);
|
||||
}
|
||||
|
||||
protected isTop() {
|
||||
return BaseView.isTop(this._base_view_name);
|
||||
}
|
||||
|
||||
protected is2D() {
|
||||
return !this.is3D();
|
||||
}
|
||||
|
||||
protected is3D() {
|
||||
if (this.node.parent instanceof Scene) {
|
||||
return this.node.parent.name === this.viewName;
|
||||
}
|
||||
const scene = director.getScene();
|
||||
return scene.name === this.viewName;
|
||||
}
|
||||
|
||||
@property
|
||||
private _hideEvent = HideEvent.destroy;
|
||||
@property({
|
||||
group: Group,
|
||||
type: HideEvent,
|
||||
tooltip: '何种模式隐藏节点\n1、destroy: 销毁UI并释放对应的所有资源\n2、active: 缓存UI并加速下次的打开速度',
|
||||
})
|
||||
public get hideEvent() {
|
||||
if (this.is3D()) return HideEvent.destroy;
|
||||
return this._hideEvent;
|
||||
}
|
||||
public set hideEvent(value) {
|
||||
if (this.is3D() && value !== HideEvent.destroy) {
|
||||
this.log('3D模式下只能是destroy模式');
|
||||
return;
|
||||
}
|
||||
this._hideEvent = value;
|
||||
}
|
||||
|
||||
@property
|
||||
private _singleton = true;
|
||||
private static _singleton = true;
|
||||
@property({
|
||||
group: Group,
|
||||
tooltip: '是否是单例模式\n1、单例模式: UI只会被创建一次(onShow会被重复触发)\n2、非单例模式: UI会被重复创建',
|
||||
})
|
||||
protected get singleton(): boolean {
|
||||
if (this.isPage()) return true;
|
||||
if (this.isPaperAll()) return true;
|
||||
if (this.isPaper()) return true;
|
||||
return this._singleton && (<typeof BaseView>this.constructor)._singleton;
|
||||
}
|
||||
protected set singleton(value) {
|
||||
if (!value) {
|
||||
if (this.isPage()) {
|
||||
this.log('Page只能是单例模式');
|
||||
return;
|
||||
}
|
||||
if (this.isPaper()) {
|
||||
this.log('Paper只能是单例模式');
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._singleton = (<typeof BaseView>this.constructor)._singleton = !!value;
|
||||
}
|
||||
|
||||
@property
|
||||
private _captureFocus = true;
|
||||
@property({
|
||||
group: Group,
|
||||
tooltip: '是否捕获焦点<响应onLostFocus和onFocus>\n1、当一个捕获焦点的UI处于最上层并展示时\n下层的UI永远不会响应focus事件',
|
||||
visible(this: BaseView) {
|
||||
if (this.is3D()) return false;
|
||||
return true;
|
||||
}
|
||||
})
|
||||
protected get captureFocus() {
|
||||
if (this.is3D()) return false;
|
||||
return this._captureFocus;
|
||||
}
|
||||
protected set captureFocus(value) {
|
||||
if (value && this.is3D()) {
|
||||
this.log('只有2D模式下才可以捕获焦点');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EDITOR && this._captureFocus !== value) {
|
||||
this._captureFocus = value;
|
||||
Core.inst?.manager?.ui?.refreshShade();
|
||||
} else {
|
||||
this._captureFocus = value;
|
||||
}
|
||||
}
|
||||
|
||||
@property
|
||||
private _shade = true;
|
||||
@property({
|
||||
group: Group,
|
||||
tooltip: '是否需要底层遮罩',
|
||||
visible(this: BaseView) {
|
||||
if (this.is3D()) return false;
|
||||
if (this.isPage()) return false;
|
||||
return true;
|
||||
}
|
||||
})
|
||||
protected get shade() {
|
||||
if (this.is3D()) return false;
|
||||
if (this.isPage()) return false;
|
||||
return this._shade;
|
||||
}
|
||||
protected set shade(value) {
|
||||
if (value) {
|
||||
if (this.is3D()) {
|
||||
this.log('只有2D模式下才可以设置底层遮罩');
|
||||
return;
|
||||
}
|
||||
if (this.isPage()) {
|
||||
this.log('Page不可以设置底层遮罩');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!EDITOR && this._shade !== value) {
|
||||
this._shade = value;
|
||||
Core.inst?.manager?.ui?.refreshShade();
|
||||
} else {
|
||||
this._shade = value;
|
||||
}
|
||||
}
|
||||
|
||||
@property
|
||||
private _blockInput = true;
|
||||
@property({
|
||||
group: Group,
|
||||
tooltip: '是否禁止点击事件向下层传递',
|
||||
visible(this: BaseView) {
|
||||
if (this.is3D()) return false;
|
||||
return true;
|
||||
}
|
||||
})
|
||||
protected get blockInput() {
|
||||
if (this.is3D()) return false;
|
||||
return this._blockInput;
|
||||
}
|
||||
protected set blockInput(value) {
|
||||
if (value && this.is3D()) {
|
||||
this.log('只有2D模式下才可以设置阻断点击事件');
|
||||
return;
|
||||
}
|
||||
this._blockInput = value;
|
||||
}
|
||||
|
||||
/** 修改框架、新增isAlwaysExist选项,如果勾选,则不会自动隐藏旧页面 */
|
||||
@property
|
||||
private _alwaysExist = false;
|
||||
@property({
|
||||
group: Group,
|
||||
tooltip: '界面是否始终存在\- 打开另外界面后不会被关闭',
|
||||
visible(this: BaseView) {
|
||||
if (this.is3D()) return false;
|
||||
return true;
|
||||
}
|
||||
})
|
||||
protected get alwaysExist() {
|
||||
if (this.is3D()) return false;
|
||||
return this._alwaysExist;
|
||||
}
|
||||
protected set alwaysExist(value) {
|
||||
if (value && this.is3D()) {
|
||||
this.log('只有2D模式下才可以设置界面是否始终存在');
|
||||
return;
|
||||
}
|
||||
this._alwaysExist = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 子界面(只能用于Page)
|
||||
*/
|
||||
protected miniViews: IMiniViewNames = [];
|
||||
|
||||
/**
|
||||
* 当前view名字
|
||||
*/
|
||||
public get viewName() {
|
||||
return this._base_view_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础名字, 如PageHome => Home
|
||||
*/
|
||||
public get baseName() {
|
||||
return this._base_view_name.slice(this.typeName.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型名字, 如PageHome => Page
|
||||
*/
|
||||
public get typeName() {
|
||||
if (this._base_view_name.indexOf(ViewType.Paper) === 0) return ViewType.Paper;
|
||||
if (this._base_view_name.indexOf(ViewType.Pop) === 0) return ViewType.Pop;
|
||||
if (this._base_view_name.indexOf(ViewType.Top) === 0) return ViewType.Top;
|
||||
return ViewType.Page;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是单例模式
|
||||
*/
|
||||
public get isSingleton(): boolean {
|
||||
return this.singleton;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否捕获焦点
|
||||
*/
|
||||
public get isCaptureFocus(): boolean {
|
||||
return this.captureFocus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否需要遮罩
|
||||
*/
|
||||
public get isNeedShade(): boolean {
|
||||
return this.shade;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否展示了(不为Hid状态)
|
||||
*/
|
||||
public get isShow(): boolean {
|
||||
return this._base_view_state != ViewState.Hid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开其他界面时,此界面不关闭
|
||||
*/
|
||||
public get isAlwaysExist(): boolean {
|
||||
return this._alwaysExist;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否show了某个子界面
|
||||
*/
|
||||
protected isMiniViewShow(name: IMiniViewName) {
|
||||
return this._base_mini_show.has(name);
|
||||
}
|
||||
|
||||
// 用来初始化组件或节点的一些属性,当该组件被第一次添加到节点上或用户点击了它的 Reset 菜单时调用。这个回调只会在编辑器下调用。
|
||||
resetInEditor(): any {
|
||||
if (EDITOR) {
|
||||
const is3D = this.is3D();
|
||||
if (this.viewName.indexOf(ViewType.Page) >= 0) {
|
||||
this.shade = false;
|
||||
this.blockInput = is3D ? false : true;
|
||||
this.captureFocus = is3D ? false : true;
|
||||
} else if (this.viewName.indexOf(ViewType.Paper) >= 0) {
|
||||
this.shade = false;
|
||||
this.captureFocus = false;
|
||||
this.blockInput = false;
|
||||
}
|
||||
|
||||
if (is3D) return;
|
||||
this.node.getComponent(UITransform) || this.node.addComponent(UITransform);
|
||||
|
||||
const widget = this.node.getComponent(Widget) || this.node.addComponent(Widget);
|
||||
widget.isAlignBottom = true;
|
||||
widget.isAlignLeft = true;
|
||||
widget.isAlignRight = true;
|
||||
widget.isAlignTop = true;
|
||||
widget.top = 0;
|
||||
widget.left = 0;
|
||||
widget.right = 0;
|
||||
widget.bottom = 0;
|
||||
widget.alignMode = Widget.AlignMode.ON_WINDOW_RESIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否可点击
|
||||
*/
|
||||
protected setTouchEnabled(enabled: boolean = true): any {
|
||||
this._base_touch_enable = !!enabled;
|
||||
}
|
||||
|
||||
private blockPropagation(event: EventTouch) {
|
||||
if (this.blockInput) {
|
||||
event.propagationStopped = true;
|
||||
if (event.type === Node.EventType.TOUCH_START) {
|
||||
this.log('阻断触摸向下层传递');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private stopPropagation(event: EventTouch) {
|
||||
if (!this._base_touch_enable) {
|
||||
event.propagationStopped = true;
|
||||
event.propagationImmediateStopped = true;
|
||||
if (event.type === Node.EventType.TOUCH_START) {
|
||||
this.log('屏蔽触摸');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onBaseViewCreate(): any {
|
||||
if (this.is3D()) return;
|
||||
const uiTransform = this.getComponent(UITransform);
|
||||
if (uiTransform) uiTransform.hitTest = (...args: any[]): boolean => {
|
||||
if (this.blockInput) {
|
||||
return UITransform.prototype.hitTest.apply(uiTransform, args);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
for (let i = 0; i < BlockEvents.length; i++) {
|
||||
this.node.on(BlockEvents[i], this.blockPropagation, this);
|
||||
this.node.on(BlockEvents[i], this.stopPropagation, this, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭所有子界面
|
||||
*/
|
||||
protected hideAllMiniViews(data?: any) {
|
||||
this._base_mini_show.forEach((name) => {
|
||||
Core.inst.manager.ui.hide({ name, data });
|
||||
});
|
||||
this._base_mini_show.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭子界面
|
||||
*/
|
||||
protected hideMiniViews({ data, views }: { data?: any, views: IMiniViewNames }) {
|
||||
if (this.miniViews.length === 0) return;
|
||||
if (views.length === 0) return;
|
||||
|
||||
views.forEach(name => {
|
||||
if (this.miniViews.indexOf(name) === -1) {
|
||||
this.warn('hideMiniViews', `${name}不在miniViews中, 已跳过`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证
|
||||
if (!this._base_mini_show.has(name)) return;
|
||||
// 关闭
|
||||
Core.inst.manager.ui.hide({ name, data });
|
||||
});
|
||||
// TODO 手动刷新一下Paper下的UI顺序(原因是原生环境,显示层级正确但触摸层级可能会不正确)
|
||||
Core.inst.manager.ui.sortUserInterface('Paper');
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示子界面
|
||||
*/
|
||||
protected showMiniViews({ data, views, onShow, onHide, onFinish }: {
|
||||
/**传递给子界面的数据 */
|
||||
data?: any,
|
||||
/**子界面名字列表 */
|
||||
views: Array<IMiniViewName | IMiniViewNames>,
|
||||
/**子界面展示回调 */
|
||||
onShow?: IMiniOnShow,
|
||||
/**子界面关闭回调 */
|
||||
onHide?: IMiniOnHide,
|
||||
/**子界面融合完成回调 */
|
||||
onFinish?: IMiniOnFinish
|
||||
}) {
|
||||
if (views.length === 0) return false;
|
||||
if (this.typeName !== ViewType.Page) {
|
||||
this.warn('showMiniViews', '仅支持Page类型');
|
||||
return false;
|
||||
}
|
||||
|
||||
const task = Core.inst.lib.task.createSync();
|
||||
|
||||
for (let index = 0; index < views.length; index++) {
|
||||
const names = views[index];
|
||||
if (names instanceof Array) {
|
||||
task.add(next => {
|
||||
this.createMixMiniViewsTask(names, data, onShow, onHide).start(next);
|
||||
});
|
||||
} else {
|
||||
task.add(next => {
|
||||
this.createMixMiniViewsTask([names], data, onShow, onHide).start(next);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
task.start(onFinish && function () {
|
||||
onFinish();
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建自定义加载任务
|
||||
*/
|
||||
private createMixMiniViewsTask(views: IMiniViewNames = [], data?: any, onShow?: IMiniOnShow, onHide?: IMiniOnHide) {
|
||||
const task = Core.inst.lib.task.createSync();
|
||||
|
||||
if (this.typeName !== ViewType.Page) {
|
||||
this.warn('showMiniViews', '仅支持Page类型');
|
||||
return task;
|
||||
}
|
||||
|
||||
views = views.filter(name => {
|
||||
if (!name) {
|
||||
this.warn('showMiniViews', 'name不能为空');
|
||||
return false;
|
||||
}
|
||||
if (this._base_mini_show.has(name)) {
|
||||
this.warn('showMiniViews', `重复融合${name}, 已跳过`);
|
||||
return false;
|
||||
}
|
||||
if (this.miniViews.indexOf(name) === -1) {
|
||||
this.warn('showMiniViews', `${name}不在miniViews中, 已跳过`);
|
||||
return false;
|
||||
}
|
||||
if (name.indexOf(this.baseName) !== ViewType.Paper.length && name.indexOf(ViewType.PaperAll) !== 0) {
|
||||
this.warn('showMiniViews', `${name}不属于当前Page, 已跳过`);
|
||||
return false;
|
||||
}
|
||||
|
||||
this._base_mini_show.add(name);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (views.length === 0) return task;
|
||||
|
||||
// 先load全部
|
||||
task.add((next) => {
|
||||
const aSync = Core.inst.lib.task.createASync();
|
||||
views.forEach(name => {
|
||||
aSync.add((next, retry) => {
|
||||
this.log(`下载子页面: ${name}`);
|
||||
Core.inst.manager.ui.load(name as any, result => {
|
||||
result ? next() : this.scheduleOnce(retry, 0.1);
|
||||
});
|
||||
});
|
||||
});
|
||||
aSync.start(next);
|
||||
});
|
||||
|
||||
// 再show全部
|
||||
task.add((next) => {
|
||||
const aSync = Core.inst.lib.task.createASync();
|
||||
views.forEach(name => {
|
||||
aSync.add((next) => {
|
||||
if (!this._base_mini_show?.has(name)) return next();
|
||||
|
||||
this.log(`展示子页面: ${name}`);
|
||||
// 是PaperAll,设置owner
|
||||
if (BaseView.isPaperAll(name)) {
|
||||
PaperAllToOwner.set(name, this.uuid);
|
||||
}
|
||||
Core.inst.manager.ui.show({
|
||||
name, data,
|
||||
silent: true,
|
||||
attr: { zIndex: this.miniViews.indexOf(name) },
|
||||
onShow: (result) => {
|
||||
if (onShow) onShow(name, result);
|
||||
next();
|
||||
},
|
||||
onHide: (result) => {
|
||||
if (BaseView.isPaperAll(name)) {
|
||||
// 验证PaperAll是否属于当前Page
|
||||
const owner = PaperAllToOwner.get(name);
|
||||
if (owner && owner === this.uuid) {
|
||||
PaperAllToOwner.delete(name);
|
||||
}
|
||||
}
|
||||
this._base_mini_show?.delete(name);
|
||||
if (onHide) onHide(name, result);
|
||||
},
|
||||
onError: (result, code) => {
|
||||
if (code === Core.inst.Manager.UI.ErrorCode.LoadError) return true;
|
||||
if (BaseView.isPaperAll(name)) {
|
||||
// 验证PaperAll是否属于当前Page
|
||||
const owner = PaperAllToOwner.get(name);
|
||||
if (owner && owner === this.uuid) {
|
||||
PaperAllToOwner.delete(name);
|
||||
Core.inst.manager.ui.hide({ name });
|
||||
}
|
||||
}
|
||||
this._base_mini_show?.delete(name);
|
||||
this.warn('忽略子页面', name, result);
|
||||
next();
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
aSync.start(() => {
|
||||
// TODO 手动刷新一下Paper下的UI顺序(原因是原生环境,显示层级正确但触摸层级可能会不正确)
|
||||
Core.inst.manager.ui.sortUserInterface('Paper');
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置节点属性
|
||||
*/
|
||||
private setNodeAttr(attr: IShowParamAttr) {
|
||||
if (!attr) return;
|
||||
if (typeof attr.zIndex === 'number') {
|
||||
// 以z坐标来代替2.x时代的zIndex
|
||||
this.node.setPosition(this.node.position.x, this.node.position.y, attr.zIndex);
|
||||
}
|
||||
|
||||
if (typeof attr.siblingIndex === 'number') {
|
||||
this.node.setSiblingIndex(attr.siblingIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private show(data?: any, attr?: IShowParamAttr, onShow?: IShowParamOnShow, onHide?: IShowParamOnHide, beforeShow?: IShowParamBeforeShow) {
|
||||
// 当前show操作需要等待其它流程
|
||||
if (this._base_view_state !== ViewState.Showed &&
|
||||
this._base_view_state !== ViewState.Hid) {
|
||||
this._base_show_hide_delays.push(
|
||||
this.show.bind(this, data, attr, onShow, onHide, beforeShow)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// show流程
|
||||
const changeState = this._base_view_state === ViewState.Hid;
|
||||
if (changeState) this._base_view_state = ViewState.BeforeShow;
|
||||
const next = (error: string) => {
|
||||
if (!error) {
|
||||
// 所有Paper只会是单例,而且所有Paper都不允许被当前Page重复show
|
||||
// 但PaprAll比较特殊,会被不同的Page使用,在PaperAll被不同的Page重复show时,清除之前的onHide
|
||||
if (this.isPaperAll()) this.node.emit('onHide');
|
||||
}
|
||||
beforeShow && beforeShow(error);
|
||||
if (!error) {
|
||||
// 设置展示中
|
||||
if (changeState) this._base_view_state = ViewState.Showing;
|
||||
onHide && this.node.once('onHide', onHide);
|
||||
|
||||
// 触发onCreate
|
||||
if (this._base_view_created === false) {
|
||||
this._base_view_created = true;
|
||||
this.onBaseViewCreate();
|
||||
}
|
||||
|
||||
// 设置属性
|
||||
this.setNodeAttr(attr);
|
||||
|
||||
// 触发onLoad、onEnable
|
||||
if (this.node.active !== true) { this.node.active = true; }
|
||||
|
||||
this.log('onShow');
|
||||
let result = null;
|
||||
try {
|
||||
result = this.onShow(data);
|
||||
} catch (err) {
|
||||
this.onError();
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
// 设置遮罩,触发focus逻辑
|
||||
Core.inst.manager.ui.refreshShade();
|
||||
|
||||
try {
|
||||
onShow && onShow(result);
|
||||
this.node.emit('onShow', result);
|
||||
Core.inst.manager.ui.emit(this._base_view_name, { event: 'onShow', result: result });
|
||||
Core.inst.manager.ui.emit('onShow', { name: this._base_view_name, result: result });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
if (changeState) this._base_view_state = ViewState.Showed;
|
||||
} else {
|
||||
if (changeState) this._base_view_state = ViewState.Hid;
|
||||
}
|
||||
|
||||
if (this._base_show_hide_delays.length > 0) {
|
||||
this._base_show_hide_delays.shift()();
|
||||
}
|
||||
};
|
||||
|
||||
this.log('beforeShow');
|
||||
let isNextCalled = false;
|
||||
this.beforeShow((error) => {
|
||||
if (isNextCalled) return this.error('beforeShow', 'next被重复调用');
|
||||
isNextCalled = true;
|
||||
next(error || null);
|
||||
}, data);
|
||||
}
|
||||
|
||||
protected hide(
|
||||
//@ts-ignore
|
||||
data?: Parameters<this['onHide']>[0],
|
||||
onHide?: IHideParamOnHide) {
|
||||
|
||||
// 当前hide操作需要等待其它流程
|
||||
if (this._base_view_state !== ViewState.Hid &&
|
||||
this._base_view_state !== ViewState.Showed) {
|
||||
this._base_show_hide_delays.push(
|
||||
this.hide.bind(this, data, onHide)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// hide流程
|
||||
const changeState = this._base_view_state === ViewState.Showed;
|
||||
if (changeState) this._base_view_state = ViewState.BeforeHide;
|
||||
this.log('beforeHide');
|
||||
const error = this.beforeHide(data);
|
||||
if (!error) {
|
||||
this.log('onHide');
|
||||
if (changeState) this._base_view_state = ViewState.Hiding;
|
||||
this.hideAllMiniViews(data);
|
||||
|
||||
let result = null;
|
||||
try {
|
||||
result = this.onHide(data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
try {
|
||||
onHide && onHide(result);
|
||||
this.node.emit('onHide', result);
|
||||
Core.inst.manager.ui.emit(this._base_view_name, { event: 'onHide', result: result });
|
||||
Core.inst.manager.ui.emit('onHide', { name: this._base_view_name, result: result });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
if (changeState) this._base_view_state = ViewState.Hid;
|
||||
|
||||
if (this.hideEvent === HideEvent.active) { this.node.active = false; }
|
||||
else if (this.hideEvent === HideEvent.destroy) { Core.inst.manager.ui.release(this); }
|
||||
Core.inst.manager.ui.refreshShade();
|
||||
} else {
|
||||
if (changeState) this._base_view_state = ViewState.Showed;
|
||||
}
|
||||
|
||||
if (this._base_show_hide_delays.length > 0) {
|
||||
this._base_show_hide_delays.shift()();
|
||||
}
|
||||
}
|
||||
|
||||
private focus(boo: boolean): any {
|
||||
let result = null;
|
||||
let event = '';
|
||||
if (boo) {
|
||||
result = this.onFocus();
|
||||
event = 'onFocus';
|
||||
} else {
|
||||
result = this.onLostFocus();
|
||||
event = 'onLostFocus';
|
||||
}
|
||||
|
||||
this.node.emit(event, result);
|
||||
Core.inst.manager.ui.emit(this._base_view_name, { event: event, result: result });
|
||||
Core.inst.manager.ui.emit(event, { name: this._base_view_name, result: result });
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载UI目录下resources里面的资源
|
||||
* @param path 相对于resources的路径
|
||||
* @param callback 回调
|
||||
* this.loadRes('Bag', Prefab, function(asset){})
|
||||
*/
|
||||
protected loadRes<T extends typeof Asset>(path: string, type: T, callback?: (result: InstanceType<T> | null) => any) {
|
||||
Core.inst.manager.ui.loadRes(this, path, type, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载UI目录下resources里面的资源
|
||||
* @param path 相对于resources的路径
|
||||
* this.preloadRes('Bag', Prefab)
|
||||
*/
|
||||
protected preloadRes<T extends typeof Asset>(path: string, type: T) {
|
||||
Core.inst.manager.ui.preloadRes(this, path, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载UI目录下resources里面的资源
|
||||
* @param path 相对于resources的路径
|
||||
* @param callback 回调
|
||||
* this.loadResDir('Bag', Prefab, function(asset){})
|
||||
*/
|
||||
protected loadResDir<T extends typeof Asset>(path: string, type: T, callback?: (result: InstanceType<T>[] | null) => any) {
|
||||
Core.inst.manager.ui.loadResDir(this, path, type, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载UI目录下resources里面的资源
|
||||
* @param path 相对于resources的路径
|
||||
* this.preloadResDir('Bag', Prefab)
|
||||
*/
|
||||
protected preloadResDir<T extends typeof Asset>(path: string, type: T) {
|
||||
Core.inst.manager.ui.preloadResDir(this, path, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置字体资源
|
||||
* @param path UI的resources目录下的相对路径
|
||||
*/
|
||||
protected setFont(target: Label, path: string, onComplete?: (success: boolean) => any) {
|
||||
this.loadRes(path, Font, (font) => {
|
||||
if (!font || !isValid(target)) {
|
||||
return onComplete && onComplete(false);
|
||||
}
|
||||
target.font = font;
|
||||
onComplete && onComplete(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Spine资源
|
||||
* @param path UI的resources目录下的相对路径
|
||||
*/
|
||||
protected setSpine(target: sp.Skeleton, path: string, onComplete?: (success: boolean) => any) {
|
||||
this.loadRes(path, sp.SkeletonData, (skeletonData) => {
|
||||
if (!skeletonData || !isValid(target)) {
|
||||
return onComplete && onComplete(false);
|
||||
}
|
||||
target.skeletonData = skeletonData;
|
||||
onComplete && onComplete(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置图片资源
|
||||
* @param path UI的resources目录下的相对路径(必须以/spriteFrame结尾)
|
||||
*
|
||||
* @example
|
||||
* setSprite(sprite, 'img/a/spriteFrame', onComplete:(succ)=>{})
|
||||
*/
|
||||
protected setSprite(target: Sprite, path: string, onComplete?: (success: boolean) => any) {
|
||||
this.loadRes(path, SpriteFrame, (spriteFrame) => {
|
||||
if (!spriteFrame || !isValid(target)) {
|
||||
return onComplete && onComplete(false);
|
||||
}
|
||||
target.spriteFrame = spriteFrame;
|
||||
onComplete && onComplete(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**打印日志 */
|
||||
protected get log() {
|
||||
return Logger.create('log', '#1e90ff', DEV ? `[${this._base_view_name}] LOG` : `[${this._base_view_name}] [LOG]`);
|
||||
}
|
||||
|
||||
/**打印警告 */
|
||||
protected get warn() {
|
||||
return Logger.create('warn', '#ff7f50', DEV ? `[${this._base_view_name}] WARN` : `[${this._base_view_name}] [WARN]`);
|
||||
}
|
||||
|
||||
/**打印错误 */
|
||||
protected get error() {
|
||||
return Logger.create('error', '#ff4757', DEV ? `[${this._base_view_name}] ERROR` : `[${this._base_view_name}] [ERROR]`);
|
||||
}
|
||||
|
||||
//////////////以下为可重写//////////////
|
||||
/**
|
||||
* 展示
|
||||
* @param data 传递给onShow的参数
|
||||
* @returns
|
||||
*/
|
||||
protected onShow(data?: any): any {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏
|
||||
* @param data 传递给onHide的参数
|
||||
* @returns
|
||||
*/
|
||||
protected onHide(data?: any): any {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 失去焦点
|
||||
* @returns
|
||||
*/
|
||||
protected onLostFocus(): any {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得焦点
|
||||
* @returns
|
||||
*/
|
||||
protected onFocus(): any {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* onShow前调用
|
||||
* @param next 回调,传递的error不为空时,表示错误,onShow不会执行
|
||||
* @param data 传递给onShow的参数
|
||||
*/
|
||||
protected beforeShow(next: (error?: string) => void, data?: any): any {
|
||||
next(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* hide前调用
|
||||
* @param data 传递给onHide的参数
|
||||
* @returns 如果返回字符串,则表示错误信息
|
||||
*/
|
||||
protected beforeHide(data?: any): string | void {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* onShow报错会执行
|
||||
*/
|
||||
protected onError(): any {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 背景遮照的参数
|
||||
*/
|
||||
protected onShade(): IShade {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
9
extensions/app/assets/base/BaseView.ts.meta
Normal file
9
extensions/app/assets/base/BaseView.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "eddc0411-4239-423d-8710-77fdf92affc2",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
12
extensions/app/assets/lib.meta
Normal file
12
extensions/app/assets/lib.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "6ff04fb4-44cd-4fc4-a4b8-28ac28d83a0b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
12
extensions/app/assets/lib/debug.meta
Normal file
12
extensions/app/assets/lib/debug.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "e939f63c-c853-403c-aed7-b8f0925106f0",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
40
extensions/app/assets/lib/debug/debug.ts
Normal file
40
extensions/app/assets/lib/debug/debug.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { DEBUG, DEV } from 'cc/env';
|
||||
|
||||
function jsGetSet(obj: unknown, prop: string, getter: Function, setter?: Function) {
|
||||
Object.defineProperty(obj, prop, {
|
||||
get: getter as any,
|
||||
set: setter as any,
|
||||
enumerable: false,
|
||||
configurable: false
|
||||
});
|
||||
}
|
||||
function clear(object: Record<string | number, any>) {
|
||||
if (!object) return;
|
||||
for (const key of Object.keys(object)) {
|
||||
delete object[key];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 将某个变量设置为不可观测(不可在浏览器中打印)
|
||||
* @param owner object | string | number | boolean | Array | Function | ...
|
||||
* @param callback 被观测时触发回调
|
||||
* @returns
|
||||
*/
|
||||
export function unobservable(owner: unknown, callback?: Function) {
|
||||
if (DEV || DEBUG) return;
|
||||
if (!owner) return;
|
||||
function define() {
|
||||
function accessor() {
|
||||
if (callback) {
|
||||
callback();
|
||||
} else {
|
||||
clear(owner);
|
||||
}
|
||||
}
|
||||
try {
|
||||
jsGetSet(owner, 'unobservable', accessor.bind(null, 'de' + 'bu' + 'gg' + 'er'));
|
||||
}
|
||||
catch (e) { !0; }
|
||||
}
|
||||
define();
|
||||
}
|
||||
9
extensions/app/assets/lib/debug/debug.ts.meta
Normal file
9
extensions/app/assets/lib/debug/debug.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "c0d5da3b-23f0-400e-85bb-6c754b9c08eb",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
9
extensions/app/assets/lib/logger.meta
Normal file
9
extensions/app/assets/lib/logger.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "140d98c1-6885-4e37-bf16-2ee67ffe087e",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
77
extensions/app/assets/lib/logger/logger.ts
Normal file
77
extensions/app/assets/lib/logger/logger.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { DEV } from 'cc/env';
|
||||
|
||||
interface ILog {
|
||||
(title: string, ...args: any[]): void
|
||||
}
|
||||
|
||||
function empty() { }
|
||||
|
||||
/**
|
||||
* 日志管理类,用于统一日志输出格式
|
||||
*/
|
||||
export class Logger {
|
||||
static setting: {
|
||||
filter: Array<'log' | 'warn' | 'error'>
|
||||
} = {
|
||||
filter: ['error', 'log', 'warn']
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建日志输出函数
|
||||
*/
|
||||
static create(level: 'log' | 'warn' | 'error', styleColor: string, title: string, titleColor = '#fff') {
|
||||
if (this.setting.filter.indexOf(level) == -1) {
|
||||
return empty;
|
||||
}
|
||||
|
||||
if (DEV) {
|
||||
return window.console[level].bind(window.console,
|
||||
'%c %s %c %s ',
|
||||
`background:${styleColor}; padding: 2px; border-radius: 5px 0 0 5px; border: 1px solid ${styleColor}; color: ${titleColor}; font-weight: normal;`,
|
||||
`${title} ${new Date().toLocaleString()}`,
|
||||
`background:#ffffff ; padding: 2px; border-radius: 0 5px 5px 0; border: 1px solid ${styleColor}; color: ${styleColor}; font-weight: normal;`
|
||||
);
|
||||
}
|
||||
return window.console[level].bind(window.console,
|
||||
`${title} [${new Date().toLocaleString()}]`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于输出一般信息
|
||||
*/
|
||||
get log() {
|
||||
return Logger.create('log', '#6495ed', '[LOG]', '#000') as ILog;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于输出警告信息
|
||||
*/
|
||||
|
||||
get warn() {
|
||||
return Logger.create('warn', '#ff7f50', '[WARN]', '#000') as ILog;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于输出错误信息
|
||||
*/
|
||||
get error() {
|
||||
return Logger.create('error', '#ff4757', '[ERROR]', '#000') as ILog;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于输出调试信息
|
||||
*/
|
||||
get debug() {
|
||||
return Logger.create('log', '#ff6347', '[DEBUG]', '#000') as ILog;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于输出成功信息
|
||||
*/
|
||||
get success() {
|
||||
return Logger.create('log', '#00ae9d', '[SUCC]', '#000') as ILog;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Logger();
|
||||
9
extensions/app/assets/lib/logger/logger.ts.meta
Normal file
9
extensions/app/assets/lib/logger/logger.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "0b5812e8-b67d-4ba9-83d8-12ca1e46cfd0",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
12
extensions/app/assets/lib/storage.meta
Normal file
12
extensions/app/assets/lib/storage.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "0c157f59-fd16-449c-b42c-d0efa2dc1401",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
236
extensions/app/assets/lib/storage/storage.ts
Normal file
236
extensions/app/assets/lib/storage/storage.ts
Normal file
@@ -0,0 +1,236 @@
|
||||
import { error, js, log, sys } from 'cc';
|
||||
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
|
||||
function encode(text: string, key: string) {
|
||||
key = key || chars;
|
||||
let encrypted = '';
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const charCode = text.charCodeAt(i) ^ key.charCodeAt(i % key.length);
|
||||
encrypted += String.fromCharCode(charCode);
|
||||
}
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
function decode(encryptedText: string, key: string) {
|
||||
key = key || chars;
|
||||
let decrypted = '';
|
||||
for (let i = 0; i < encryptedText.length; i++) {
|
||||
const charCode = encryptedText.charCodeAt(i) ^ key.charCodeAt(i % key.length);
|
||||
decrypted += String.fromCharCode(charCode);
|
||||
}
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
const weekOfYear = function (curDate?: Date) {
|
||||
/*
|
||||
date1是当前日期
|
||||
date2是当年第一天
|
||||
d是当前日期是今年第多少天
|
||||
用d + 当前年的第一天的周差距的和在除以7就是本年第几周
|
||||
*/
|
||||
curDate = curDate || new Date();
|
||||
let a = curDate.getFullYear();
|
||||
let b = curDate.getMonth() + 1;
|
||||
let c = curDate.getDate();
|
||||
|
||||
let date1 = new Date(a, b - 1, c), date2 = new Date(a, 0, 1),
|
||||
d = Math.round((date1.valueOf() - date2.valueOf()) / 86400000);
|
||||
return Math.ceil(
|
||||
(d + ((date2.getDay() + 1) - 1)) / 7
|
||||
);
|
||||
};
|
||||
|
||||
const getWeekUpdateTime = function () {
|
||||
const date = new Date();
|
||||
const year = date.getFullYear();
|
||||
const week = weekOfYear(date);
|
||||
return year + '' + week;
|
||||
};
|
||||
|
||||
const getDayUpdateTime = function (curDate?: Date) {
|
||||
curDate = curDate || new Date();
|
||||
return curDate.toLocaleDateString();
|
||||
};
|
||||
|
||||
export class Storage {
|
||||
static setting: {
|
||||
/**
|
||||
* 加密密钥
|
||||
* - 如果需要加密内容,请设置密钥的值
|
||||
*/
|
||||
secretKey: string
|
||||
} = {
|
||||
secretKey: ''
|
||||
};
|
||||
|
||||
private _cache = {};
|
||||
|
||||
/**
|
||||
* 返回值为false代表调用失败
|
||||
*/
|
||||
set(key: string, value: unknown) {
|
||||
if (typeof key === 'string' && typeof value !== 'undefined') {
|
||||
try {
|
||||
const data = JSON.stringify(value);
|
||||
if (Storage.setting.secretKey) {
|
||||
sys.localStorage.setItem(key, encode(data, Storage.setting.secretKey));
|
||||
} else {
|
||||
sys.localStorage.setItem(key, data);
|
||||
}
|
||||
// 设置缓存
|
||||
this._cache[key] = data;
|
||||
return true;
|
||||
} catch (err) { log(err); }
|
||||
} else {
|
||||
error('storage set error');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回值为undefined代表调用失败
|
||||
*/
|
||||
get(key: string) {
|
||||
// 先读取缓存
|
||||
if (typeof this._cache[key] !== 'undefined') {
|
||||
return JSON.parse(this._cache[key]);
|
||||
}
|
||||
|
||||
let result = null;
|
||||
try {
|
||||
let data = sys.localStorage.getItem(key);
|
||||
if (data && typeof data === 'string') {
|
||||
if (Storage.setting.secretKey) data = decode(data, Storage.setting.secretKey);
|
||||
// 设置缓存
|
||||
this._cache[key] = data;
|
||||
result = JSON.parse(data);
|
||||
} else if (data !== '' && data !== null) {
|
||||
result = undefined;
|
||||
}
|
||||
} catch (e) {
|
||||
result = undefined;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回值为false代表调用失败
|
||||
*/
|
||||
add(key: string, value: number = 1) {
|
||||
let result = this.get(key);
|
||||
if (result !== undefined) {
|
||||
result = result || 0;
|
||||
result += value;
|
||||
if (this.set(key, result)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回值为false代表调用失败
|
||||
*/
|
||||
remove(key: string) {
|
||||
try {
|
||||
sys.localStorage.removeItem(key);
|
||||
delete this._cache[key];
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回值为false代表调用失败
|
||||
*/
|
||||
clear() {
|
||||
try {
|
||||
sys.localStorage.clear();
|
||||
js.clear(this._cache);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置本周数据 [返回值为false代表调用失败]
|
||||
* @param {Function} cb 当已存在本周的数据时,会根据cb的返回决定是否存储,true代表存储
|
||||
*/
|
||||
setWeek(key: string, value: unknown, cb?: (oldValue: unknown, newValue: unknown) => boolean) {
|
||||
const updateTime = getWeekUpdateTime();
|
||||
|
||||
if (cb) {
|
||||
const data = this.getWeek(key);
|
||||
if (data !== undefined) {
|
||||
if (data === null || cb(data, value)) {
|
||||
return this.set(key, {
|
||||
data: value,
|
||||
updateTime: updateTime
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return this.set(key, {
|
||||
data: value,
|
||||
updateTime: updateTime
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本周数据 [返回值为undefined代表调用失败]
|
||||
*/
|
||||
getWeek(key: string) {
|
||||
const data = this.get(key);
|
||||
if (data && data.updateTime == getWeekUpdateTime()) {
|
||||
return data.data;
|
||||
}
|
||||
return data && null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置本天数据 [返回值为false代表调用失败]
|
||||
* @param {Function} cb 当已存在本天的数据时,会根据cb的返回决定是否存储,true代表存储
|
||||
*/
|
||||
setDay(key: string, value: unknown, cb?: (oldValue: unknown, newValue: unknown) => boolean) {
|
||||
const updateTime = getDayUpdateTime();
|
||||
|
||||
if (cb) {
|
||||
const data = this.getDay(key);
|
||||
if (data !== undefined) {
|
||||
if (data === null || cb(data, value)) {
|
||||
return this.set(key, {
|
||||
data: value,
|
||||
updateTime: updateTime
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return this.set(key, {
|
||||
data: value,
|
||||
updateTime: updateTime
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本天数据 [返回值为undefined代表调用失败]
|
||||
* @param {*} key
|
||||
*/
|
||||
getDay(key: string) {
|
||||
const data = this.get(key);
|
||||
if (data && data.updateTime == getDayUpdateTime()) {
|
||||
return data.data;
|
||||
}
|
||||
return data && null;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Storage();
|
||||
9
extensions/app/assets/lib/storage/storage.ts.meta
Normal file
9
extensions/app/assets/lib/storage/storage.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "be3da7ca-1bb5-4a41-894b-f82751c78ef2",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
12
extensions/app/assets/lib/task.meta
Normal file
12
extensions/app/assets/lib/task.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "54e30e53-aef2-4e16-8969-e38bea0ea336",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
440
extensions/app/assets/lib/task/task.ts
Normal file
440
extensions/app/assets/lib/task/task.ts
Normal file
@@ -0,0 +1,440 @@
|
||||
interface IHandle {
|
||||
(next: (data?: any) => boolean, retry: (timeout?: number) => Promise<boolean>, end: (data?: any) => boolean): void
|
||||
}
|
||||
|
||||
interface IFinish<T> {
|
||||
(results?: T, success?: boolean): any
|
||||
}
|
||||
|
||||
export interface ITask<T extends Array<any> = any[]> {
|
||||
readonly results: Readonly<T>;
|
||||
size(): number;
|
||||
add(handle: IHandle): this;
|
||||
start(finish?: IFinish<T> | Function): this;
|
||||
stop(): boolean;
|
||||
isRunning(): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 顺序执行
|
||||
*/
|
||||
export class Sync<T extends Array<any>> implements ITask<T> {
|
||||
private running = false;
|
||||
private index: number = -1;
|
||||
private list: IHandle[] = [];
|
||||
private finish: IFinish<T> | Function = null;
|
||||
|
||||
/**
|
||||
* 每个handle的返回值,通过next或end存储
|
||||
*/
|
||||
public results: T = [] as T;
|
||||
|
||||
/**
|
||||
* 任务数量
|
||||
* @returns
|
||||
*/
|
||||
public size(): number {
|
||||
return this.list.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个任务
|
||||
* @param handle
|
||||
* @returns
|
||||
*/
|
||||
public add(handle: IHandle) {
|
||||
this.list.push(handle);
|
||||
this.results.push(undefined);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始执行所有任务
|
||||
* @param finish 执行完毕回调
|
||||
* @returns
|
||||
*/
|
||||
public start(finish?: IFinish<T> | Function) {
|
||||
if (this.running) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.running = true;
|
||||
this.index = -1;
|
||||
this.finish = finish;
|
||||
|
||||
this.next(this.index);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止所有任务
|
||||
* @returns
|
||||
*/
|
||||
public stop(): boolean {
|
||||
if (!this.running) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.running = false;
|
||||
if (this.finish) {
|
||||
this.finish(this.results, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否正在执行
|
||||
* @returns
|
||||
*/
|
||||
public isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @returns
|
||||
*/
|
||||
public isStop() {
|
||||
return !this.running;
|
||||
}
|
||||
|
||||
private end(data?: any): boolean {
|
||||
if (!this.running) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof data !== 'undefined') {
|
||||
this.results[this.index] = data;
|
||||
}
|
||||
|
||||
this.running = false;
|
||||
if (this.finish) {
|
||||
this.finish(this.results, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private next(index: number, data?: any): boolean {
|
||||
if (!this.running) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index !== this.index) return false;
|
||||
|
||||
if (typeof data !== 'undefined') {
|
||||
this.results[this.index] = data;
|
||||
}
|
||||
|
||||
if (++this.index < this.list.length) {
|
||||
this.retry(this.index);
|
||||
} else {
|
||||
this.end();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private retry(index: number): boolean {
|
||||
if (!this.running) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index !== this.index) return false;
|
||||
|
||||
const handle = this.list[index];
|
||||
handle && handle(
|
||||
(data?: any) => this.next(index, data),
|
||||
(timeout = 0) => {
|
||||
return new Promise(resolve => {
|
||||
if (timeout > 0) {
|
||||
setTimeout(() => {
|
||||
resolve(this.retry(index));
|
||||
}, timeout * 1000);
|
||||
} else {
|
||||
resolve(this.retry(index));
|
||||
}
|
||||
});
|
||||
},
|
||||
(data?: any) => this.end(data)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 同时执行
|
||||
*/
|
||||
export class ASync<T extends Array<any>> implements ITask<T> {
|
||||
private running = false;
|
||||
private count: number = 0;
|
||||
private list: IHandle[] = [];
|
||||
private finish: IFinish<T> | Function = null;
|
||||
|
||||
/**
|
||||
* 每个handle的返回值,通过next或end存储
|
||||
*/
|
||||
public results: T = [] as T;
|
||||
|
||||
/**
|
||||
* 任务数量
|
||||
* @returns
|
||||
*/
|
||||
public size(): number {
|
||||
return this.list.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个任务
|
||||
* @param handle
|
||||
* @returns
|
||||
*/
|
||||
public add(handle: IHandle) {
|
||||
this.list.push(handle);
|
||||
this.results.push(undefined);
|
||||
|
||||
if (this.running) {
|
||||
this.retry(this.list.length - 1);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始执行所有任务
|
||||
* @param finish 执行完毕回调
|
||||
* @returns
|
||||
*/
|
||||
public start(finish?: IFinish<T> | Function) {
|
||||
if (this.running) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.running = true;
|
||||
this.count = 0;
|
||||
this.finish = finish;
|
||||
|
||||
if (this.list.length) {
|
||||
for (let index = 0; index < this.list.length; index++) {
|
||||
this.retry(index);
|
||||
}
|
||||
} else {
|
||||
this.end && this.end(this.count);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止所有任务
|
||||
* @returns
|
||||
*/
|
||||
public stop(): boolean {
|
||||
if (!this.running) {
|
||||
return false;
|
||||
}
|
||||
this.running = false;
|
||||
if (this.finish) {
|
||||
this.finish(this.results, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否正在执行
|
||||
* @returns
|
||||
*/
|
||||
public isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @returns
|
||||
*/
|
||||
public isStop() {
|
||||
return !this.running;
|
||||
}
|
||||
|
||||
private end(index: number, data?: any): boolean {
|
||||
if (!this.running) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index >= 0 && index < this.results.length) {
|
||||
if (this.results[index] || this.results[index] === null) return false;
|
||||
this.results[index] = typeof data !== 'undefined' ? data : null;
|
||||
}
|
||||
|
||||
this.running = false;
|
||||
if (this.finish) {
|
||||
this.finish(this.results, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private next(index: number, data?: any): boolean {
|
||||
if (!this.running) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index >= 0 && index < this.results.length) {
|
||||
if (this.results[index] || this.results[index] === null) return false;
|
||||
this.results[index] = typeof data !== 'undefined' ? data : null;
|
||||
}
|
||||
|
||||
if (++this.count === this.list.length) {
|
||||
this.end && this.end(this.count);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private retry(index: number): boolean {
|
||||
if (!this.running) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const handle = this.list[index];
|
||||
handle && handle(
|
||||
(data?: any) => this.next(index, data),
|
||||
(timeout = 0) => {
|
||||
return new Promise(resolve => {
|
||||
if (timeout > 0) {
|
||||
setTimeout(() => {
|
||||
resolve(this.retry(index));
|
||||
}, timeout * 1000);
|
||||
} else {
|
||||
resolve(this.retry(index));
|
||||
}
|
||||
});
|
||||
},
|
||||
(data?: any) => this.end(index, data)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class Any<T extends Array<any>> implements ITask<T> {
|
||||
private task = new Sync();
|
||||
|
||||
/**
|
||||
* 每个handle的返回值,通过next或end存储
|
||||
*/
|
||||
public get results(): T {
|
||||
return this.task.results as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务数量
|
||||
* @returns
|
||||
*/
|
||||
public size() {
|
||||
return this.task.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个任务
|
||||
* @param handle
|
||||
* @returns
|
||||
*/
|
||||
public add(handles: IHandle | IHandle[]) {
|
||||
if (handles instanceof Array) {
|
||||
const async = new ASync();
|
||||
handles.forEach(handle => async.add(handle));
|
||||
this.task.add(async.start.bind(async));
|
||||
} else {
|
||||
this.task.add(handles);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始执行所有任务
|
||||
* @param finish 执行完毕回调
|
||||
* @returns
|
||||
*/
|
||||
public start(finish?: IFinish<T> | Function) {
|
||||
this.task.start(finish);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止所有任务
|
||||
* @returns
|
||||
*/
|
||||
public stop() {
|
||||
return this.task.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否正在执行
|
||||
* @returns
|
||||
*/
|
||||
public isRunning() {
|
||||
return this.task.isRunning();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @returns
|
||||
*/
|
||||
public isStop() {
|
||||
return this.task.isStop();
|
||||
}
|
||||
}
|
||||
|
||||
interface IExecuteCallBack {
|
||||
(retry: (timeout?: number) => void): void
|
||||
}
|
||||
|
||||
const task = {
|
||||
/**
|
||||
* 任务顺序执行
|
||||
*/
|
||||
createSync<T extends Array<any>>(): Sync<T> {
|
||||
return new Sync<T>();
|
||||
},
|
||||
|
||||
/**
|
||||
* 任务同时执行
|
||||
*/
|
||||
createASync<T extends Array<any>>(): ASync<T> {
|
||||
return new ASync<T>();
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据参数指定执行顺序
|
||||
* @example
|
||||
* createAny()
|
||||
* .add(1).add(2).add(3).add(4)
|
||||
* .add([5,6,7])
|
||||
* .add(8)
|
||||
* 执行顺序,1,2,3,4依次执行,然后同时执行5,6,7,最后执行8
|
||||
*/
|
||||
createAny<T extends Array<any>>() {
|
||||
return new Any<T>();
|
||||
},
|
||||
|
||||
/**
|
||||
* 执行单个任务
|
||||
*/
|
||||
execute(fun: IExecuteCallBack, retryMax = -1, retryFinish?: Function) {
|
||||
fun(function retry(timeout = 0) {
|
||||
if (retryMax === 0) return retryFinish && retryFinish();
|
||||
retryMax = retryMax > 0 ? retryMax - 1 : retryMax;
|
||||
if (timeout > 0) {
|
||||
setTimeout(() => task.execute(fun, retryMax, retryFinish), timeout * 1000);
|
||||
} else {
|
||||
task.execute(fun, retryMax, retryFinish);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default task;
|
||||
9
extensions/app/assets/lib/task/task.ts.meta
Normal file
9
extensions/app/assets/lib/task/task.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "721e6dcc-aab3-48d6-aa4e-e1baf821263b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
12
extensions/app/assets/manager.meta
Normal file
12
extensions/app/assets/manager.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "b50df186-2646-40b3-83c5-5dca8bf01803",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
626
extensions/app/assets/manager/MainManager.prefab
Normal file
626
extensions/app/assets/manager/MainManager.prefab
Normal file
@@ -0,0 +1,626 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "MainManager",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "MainManager",
|
||||
"_objFlags": 512,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
},
|
||||
{
|
||||
"__id__": 6
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 18
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 26
|
||||
},
|
||||
{
|
||||
"__id__": 28
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 30
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 33554432,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "EventManager",
|
||||
"_objFlags": 512,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 5
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 33554432,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "b4ea6NEN3hCPZiqp3hRVbvU",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "38T7Dsd1BPcZKW0ht+pktn"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "d6NoKoYv9Gk4UlM7XwQPMZ",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "TimerManager",
|
||||
"_objFlags": 512,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 7
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 33554432,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "b5636+NNRZFEKq6dPkgK4qf",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 8
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "1dkZGh7HlMao6WFTkih0ky"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "48d9+lep1ML4jLQU06pY4T",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "LoaderManager",
|
||||
"_objFlags": 512,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 11
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 33554432,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "b3bf5M3DHNHcYe1nnNZYr6B",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 10
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 12
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "8eHWii+rhPFp6edhCSGQYK"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "f2JbolDiVMXbduKISlIjVg",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "SoundManager",
|
||||
"_objFlags": 512,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 15
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 33554432,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "58002Ha2adOWbt2LDr8rmBT",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 14
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 16
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "8bj+DC16xOWa/0aihp7pcS"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "54fbQjsl1HwJD2+SejsBvm",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "UIManager",
|
||||
"_objFlags": 512,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 19
|
||||
},
|
||||
{
|
||||
"__id__": 21
|
||||
},
|
||||
{
|
||||
"__id__": 23
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 25
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 33554432,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "234f6Lx69NNFJ9vC2nHCWRJ",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 18
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 20
|
||||
},
|
||||
"loadingPre": {
|
||||
"__uuid__": "fe542035-b018-493e-bea8-084fe4e01905",
|
||||
"__expectedType__": "cc.Prefab"
|
||||
},
|
||||
"shadePre": {
|
||||
"__uuid__": "000cee21-922c-4fcd-bd39-6f80ac2436a4",
|
||||
"__expectedType__": "cc.Prefab"
|
||||
},
|
||||
"toastPre": {
|
||||
"__uuid__": "b2a00c44-d199-4031-8fa7-ea681618b9d4",
|
||||
"__expectedType__": "cc.Prefab"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "67aJm34PdM/ItCntR8+zcy"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 18
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 22
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 750,
|
||||
"height": 1334
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "c3fJug795H8Zpvt/9PivC1"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Widget",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 18
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 24
|
||||
},
|
||||
"_alignFlags": 45,
|
||||
"_target": null,
|
||||
"_left": 0,
|
||||
"_right": 0,
|
||||
"_top": 0,
|
||||
"_bottom": 0,
|
||||
"_horizontalCenter": 0,
|
||||
"_verticalCenter": 0,
|
||||
"_isAbsLeft": true,
|
||||
"_isAbsRight": true,
|
||||
"_isAbsTop": true,
|
||||
"_isAbsBottom": true,
|
||||
"_isAbsHorizontalCenter": true,
|
||||
"_isAbsVerticalCenter": true,
|
||||
"_originalWidth": 750,
|
||||
"_originalHeight": 1334,
|
||||
"_alignMode": 2,
|
||||
"_lockFlags": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "152XoOPG1D3KEcrikGio7Z"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "4d13DruoVK/5VWhsBYQS/E",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 27
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 750,
|
||||
"height": 1334
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "bab8wNsgZHRICm9NXgLsKC"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Widget",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 29
|
||||
},
|
||||
"_alignFlags": 45,
|
||||
"_target": null,
|
||||
"_left": 0,
|
||||
"_right": 0,
|
||||
"_top": 0,
|
||||
"_bottom": 0,
|
||||
"_horizontalCenter": 0,
|
||||
"_verticalCenter": 0,
|
||||
"_isAbsLeft": true,
|
||||
"_isAbsRight": true,
|
||||
"_isAbsTop": true,
|
||||
"_isAbsBottom": true,
|
||||
"_isAbsHorizontalCenter": true,
|
||||
"_isAbsVerticalCenter": true,
|
||||
"_originalWidth": 100,
|
||||
"_originalHeight": 100,
|
||||
"_alignMode": 2,
|
||||
"_lockFlags": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "12csQGnuVJRr1L+07HdNSc"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "09f6AV8NhG8amoujtBeGc3",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
13
extensions/app/assets/manager/MainManager.prefab.meta
Normal file
13
extensions/app/assets/manager/MainManager.prefab.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "5e43bb09-848f-434a-b3a5-a6b6602e00af",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "MainManager"
|
||||
}
|
||||
}
|
||||
12
extensions/app/assets/manager/event.meta
Normal file
12
extensions/app/assets/manager/event.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "4f12d0c8-895c-48a5-8805-79653aadb7e4",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
89
extensions/app/assets/manager/event/EventManager.ts
Normal file
89
extensions/app/assets/manager/event/EventManager.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { EventTarget, _decorator } from 'cc';
|
||||
import BaseManager from '../../base/BaseManager';
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
class Event {
|
||||
static destroy(event: Event) {
|
||||
if (!event) return;
|
||||
event._event = null;
|
||||
}
|
||||
|
||||
// 事件管理器
|
||||
private _event: EventTarget = new EventTarget();
|
||||
|
||||
/**
|
||||
* 事件分发
|
||||
*/
|
||||
public emit(event: string | number, ...data: any[]) {
|
||||
if (!this._event) {
|
||||
throw Error('当前event已销毁,无法继续调用');
|
||||
}
|
||||
this._event.emit(event as any, ...data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件监听
|
||||
*/
|
||||
public on(event: string | number, cb: (...any: any[]) => void, target?: any) {
|
||||
if (!this._event) {
|
||||
throw Error('当前event已销毁,无法继续调用');
|
||||
}
|
||||
this._event.on(event as any, cb, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件监听
|
||||
*/
|
||||
public once(event: string | number, cb: (...any: any[]) => void, target?: any) {
|
||||
if (!this._event) {
|
||||
throw Error('当前event已销毁,无法继续调用');
|
||||
}
|
||||
this._event.once(event as any, cb, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件移除监听
|
||||
*/
|
||||
public off(event: string | number, cb?: (...any: any[]) => void, target?: any) {
|
||||
if (!this._event) {
|
||||
throw Error('当前event已销毁,无法继续调用');
|
||||
}
|
||||
this._event.off(event as any, cb, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件移除监听
|
||||
*/
|
||||
public targetOff(target: any) {
|
||||
if (!this._event) {
|
||||
throw Error('当前event已销毁,无法继续调用');
|
||||
}
|
||||
this._event.targetOff(target);
|
||||
}
|
||||
}
|
||||
|
||||
@ccclass('EventManager')
|
||||
export default class EventManager extends BaseManager {
|
||||
private events: Map<string | number | Symbol, Event> = new Map();
|
||||
|
||||
clear() {
|
||||
this.events.forEach(event => Event.destroy(event));
|
||||
return this.events.clear();
|
||||
}
|
||||
|
||||
delete(key: string | number | Symbol) {
|
||||
Event.destroy(this.events.get(key));
|
||||
return this.events.delete(key);
|
||||
}
|
||||
|
||||
get(key: string | number | Symbol): Event {
|
||||
if (this.events.has(key)) {
|
||||
return this.events.get(key);
|
||||
}
|
||||
|
||||
const event = new Event();
|
||||
this.events.set(key, event);
|
||||
|
||||
return event;
|
||||
}
|
||||
}
|
||||
9
extensions/app/assets/manager/event/EventManager.ts.meta
Normal file
9
extensions/app/assets/manager/event/EventManager.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "b4ea6344-3778-423d-98aa-a7785155bbd4",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
12
extensions/app/assets/manager/loader.meta
Normal file
12
extensions/app/assets/manager/loader.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "d8954580-884f-4927-b59a-dfb9553d4ce6",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
869
extensions/app/assets/manager/loader/LoaderManager.ts
Normal file
869
extensions/app/assets/manager/loader/LoaderManager.ts
Normal file
@@ -0,0 +1,869 @@
|
||||
import { Asset, AssetManager, Font, ImageAsset, JsonAsset, Label, SceneAsset, Sprite, SpriteFrame, Texture2D, TextureCube, _decorator, assetManager, isValid, path, sp } from 'cc';
|
||||
import { MINIGAME } from 'cc/env';
|
||||
import BaseManager from '../../base/BaseManager';
|
||||
import Core from '../../Core';
|
||||
const { ccclass } = _decorator;
|
||||
const REGEX = /^https?:\/\/.*/;
|
||||
|
||||
class Command {
|
||||
private static cache: Command[] = [];
|
||||
|
||||
static create(onComplete: (items: unknown) => void, onProgress: (finish: number, total: number, item: AssetManager.RequestItem) => void = null) {
|
||||
const command = Command.cache.pop() || new Command();
|
||||
onProgress && command.onProgress.push(onProgress);
|
||||
onComplete && command.onComplete.push(onComplete);
|
||||
return command;
|
||||
}
|
||||
|
||||
static put(command: Command) {
|
||||
command.onProgress.length = 0;
|
||||
command.onComplete.length = 0;
|
||||
Command.cache.push(command);
|
||||
}
|
||||
|
||||
onProgress: Array<(finish: number, total: number, item: AssetManager.RequestItem) => void> = [];
|
||||
onComplete: Array<(items: unknown) => void> = [];
|
||||
|
||||
private constructor() { }
|
||||
}
|
||||
|
||||
class Loader {
|
||||
private assetMap = new Map<string, Asset>();
|
||||
private loadingMap = new Map<string, Command>();
|
||||
|
||||
/**
|
||||
* 预加载
|
||||
* @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle
|
||||
* @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC
|
||||
*/
|
||||
public preload(params: { path: string, bundle?: string, version?: string, type?: typeof Asset, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (item: AssetManager.RequestItem[] | null) => void }) {
|
||||
return Core.inst.manager.loader.preload(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载
|
||||
* @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle
|
||||
* @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC
|
||||
*/
|
||||
public preloadDir(params: { path: string, bundle?: string, version?: string, type?: typeof Asset, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (items: AssetManager.RequestItem[] | null) => void }) {
|
||||
return Core.inst.manager.loader.preloadDir(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载bundle下的资源
|
||||
* @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundel名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle
|
||||
* @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC
|
||||
* @param params.path bundle下的相对路径
|
||||
* @param params.type 资源类型
|
||||
*/
|
||||
public load<T extends typeof Asset>(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (item: InstanceType<T> | null) => void }) {
|
||||
const key = `${params.bundle || 'resources'}-${params.type.name}-${params.path}-${params.version || ''}`;
|
||||
|
||||
if (this.loadingMap.has(key)) {
|
||||
const command = this.loadingMap.get(key);
|
||||
params.onProgress && command.onProgress.push(params.onProgress);
|
||||
params.onComplete && command.onComplete.push(params.onComplete);
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载中
|
||||
const command = Command.create(params.onComplete, params.onProgress);
|
||||
this.loadingMap.set(key, command);
|
||||
|
||||
// 有缓存
|
||||
if (this.assetMap.has(key)) {
|
||||
const asset = this.assetMap.get(key);
|
||||
// 有缓存的情况下不触发onProgress回调
|
||||
setTimeout(() => {
|
||||
// 加载无效
|
||||
if (!this.loadingMap.has(key)) return;
|
||||
this.loadingMap.delete(key);
|
||||
command.onComplete.forEach(cb => cb(asset));
|
||||
Command.put(command);
|
||||
}, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
Core.inst.manager.loader.load({
|
||||
...params,
|
||||
onProgress: (finish, total, item) => {
|
||||
if (!this.loadingMap.has(key)) return;
|
||||
command.onProgress.forEach(cb => cb(finish, total, item));
|
||||
},
|
||||
onComplete: (asset) => {
|
||||
// 加载无效
|
||||
if (!this.loadingMap.has(key)) {
|
||||
asset.addRef();
|
||||
asset.decRef();
|
||||
return;
|
||||
}
|
||||
this.loadingMap.delete(key);
|
||||
if (asset) {
|
||||
asset.addRef();
|
||||
this.assetMap.set(key, asset);
|
||||
}
|
||||
command.onComplete.forEach(cb => cb(asset));
|
||||
Command.put(command);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载bundle下的资源
|
||||
* @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle
|
||||
* @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC
|
||||
* @param params.path bundle下的相对路径
|
||||
* @param params.type 资源类型
|
||||
*/
|
||||
public loadAsync<T extends typeof Asset>(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void }): Promise<InstanceType<T> | null> {
|
||||
return new Promise((resolve) => {
|
||||
this.load({
|
||||
...params,
|
||||
onComplete: resolve
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载bundle下的资源
|
||||
* @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundel名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle
|
||||
* @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC
|
||||
* @param params.path bundle下的相对路径
|
||||
* @param params.type 资源类型
|
||||
*/
|
||||
public loadDir<T extends typeof Asset>(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (items: InstanceType<T>[] | null) => void }) {
|
||||
const key = `${params.bundle || 'resources'}-${params.type.name}-${params.path}-${params.version || ''}:`;
|
||||
|
||||
if (this.loadingMap.has(key)) {
|
||||
const command = this.loadingMap.get(key);
|
||||
params.onProgress && command.onProgress.push(params.onProgress);
|
||||
params.onComplete && command.onComplete.push(params.onComplete);
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载中
|
||||
const command = Command.create(params.onComplete, params.onProgress);
|
||||
this.loadingMap.set(key, command);
|
||||
|
||||
const results = [] as InstanceType<T>[];
|
||||
this.assetMap.forEach((asset, path) => {
|
||||
if (path.indexOf(key) === 0) {
|
||||
results.push(asset as InstanceType<T>);
|
||||
}
|
||||
});
|
||||
|
||||
// 有缓存
|
||||
if (results.length) {
|
||||
// 有缓存的情况下不触发onProgress回调
|
||||
setTimeout(() => {
|
||||
// 加载无效
|
||||
if (!this.loadingMap.has(key)) return;
|
||||
this.loadingMap.delete(key);
|
||||
command.onComplete.forEach(cb => cb(results));
|
||||
Command.put(command);
|
||||
}, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
Core.inst.manager.loader.loadDir({
|
||||
...params,
|
||||
onProgress: (finish, total, item) => {
|
||||
if (!this.loadingMap.has(key)) return;
|
||||
command.onProgress.forEach(cb => cb(finish, total, item));
|
||||
},
|
||||
onComplete: (assets) => {
|
||||
// 加载无效
|
||||
if (!this.loadingMap.has(key)) {
|
||||
assets?.forEach((asset) => {
|
||||
asset.addRef();
|
||||
asset.decRef();
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.loadingMap.delete(key);
|
||||
assets?.forEach((asset) => {
|
||||
asset.addRef();
|
||||
this.assetMap.set(key + asset.uuid, asset);
|
||||
});
|
||||
command.onComplete.forEach(cb => cb(assets));
|
||||
Command.put(command);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载bundle下的资源
|
||||
* @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle
|
||||
* @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC
|
||||
* @param params.path bundle下的相对路径
|
||||
* @param params.type 资源类型
|
||||
*/
|
||||
public loadDirAsync<T extends typeof Asset>(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void }): Promise<InstanceType<T>[] | null> {
|
||||
return new Promise((resolve) => {
|
||||
this.loadDir({
|
||||
...params,
|
||||
onComplete: resolve
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载远程资源
|
||||
* @example
|
||||
* loadRemote({url:'', ext:'.png', onComplete:(result){ }})
|
||||
*/
|
||||
public loadRemote({ url, ext, onComplete }: { url: string, ext?: string, onComplete?: (result: Asset | null) => void }) {
|
||||
if (this.loadingMap.has(url)) {
|
||||
const command = this.loadingMap.get(url);
|
||||
onComplete && command.onComplete.push(onComplete);
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载中
|
||||
const command = Command.create(onComplete);
|
||||
this.loadingMap.set(url, command);
|
||||
|
||||
// 有缓存
|
||||
if (this.assetMap.has(url)) {
|
||||
const asset = this.assetMap.get(url);
|
||||
// 有缓存的情况下不触发onProgress回调
|
||||
setTimeout(() => {
|
||||
// 加载无效
|
||||
if (!this.loadingMap.has(url)) return;
|
||||
this.loadingMap.delete(url);
|
||||
command.onComplete.forEach(cb => cb(asset));
|
||||
Command.put(command);
|
||||
}, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
Core.inst.manager.loader.loadRemote({
|
||||
url, ext,
|
||||
onComplete: (asset) => {
|
||||
// 加载无效
|
||||
if (!this.loadingMap.has(url)) {
|
||||
asset.addRef();
|
||||
asset.decRef();
|
||||
return;
|
||||
}
|
||||
this.loadingMap.delete(url);
|
||||
if (asset) {
|
||||
asset.addRef();
|
||||
this.assetMap.set(url, asset);
|
||||
}
|
||||
command.onComplete.forEach(cb => cb(asset));
|
||||
Command.put(command);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载远程资源
|
||||
* @example
|
||||
* await loadRemoteAsync({url:'', ext:'.png'})
|
||||
*/
|
||||
public loadRemoteAsync(params: { url: string, ext?: string }): Promise<Asset | null> {
|
||||
return new Promise((resolve) => {
|
||||
this.loadRemote({
|
||||
...params,
|
||||
onComplete: resolve
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置字体资源
|
||||
* @param params.bundle 默认为resources
|
||||
* @param params.path bundle下的相对路径
|
||||
*
|
||||
* @example
|
||||
* setFont({target:label, path:'font/num', bundle:'resources', onComplete:(succ)=>{}})
|
||||
* setFont({target:label, url:'http://img/a/font',ext:'.ttf', onComplete:(succ)=>{}})
|
||||
*/
|
||||
public setFont(params: { target: Label, url: string, ext?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }): void;
|
||||
public setFont(params: { target: Label, path: string, bundle?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }): void;
|
||||
public setFont(params: { target: Label, path?: string, bundle?: string, url?: string, ext?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }) {
|
||||
if (params.url) {
|
||||
this.loadRemote({
|
||||
url: params.url,
|
||||
ext: params.ext,
|
||||
onComplete: (font: Font) => {
|
||||
if (!font || !isValid(params.target)) {
|
||||
params.onFail && params.onFail();
|
||||
params.onComplete && params.onComplete(false);
|
||||
return;
|
||||
}
|
||||
params.target.font = font;
|
||||
params.onSuccess && params.onSuccess();
|
||||
params.onComplete && params.onComplete(true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.load({
|
||||
path: params.path,
|
||||
bundle: params.bundle,
|
||||
type: Font,
|
||||
onComplete: (font) => {
|
||||
if (!font || !isValid(params.target)) {
|
||||
params.onFail && params.onFail();
|
||||
params.onComplete && params.onComplete(false);
|
||||
return;
|
||||
}
|
||||
params.target.font = font;
|
||||
params.onSuccess && params.onSuccess();
|
||||
params.onComplete && params.onComplete(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Spine资源
|
||||
* @param params.bundle 默认为resources
|
||||
* @param params.path bundle下的相对路径
|
||||
*
|
||||
* @example
|
||||
* setSpine({target:spine, path:'spine/role', bundle:'resources', onComplete:(succ)=>{}})
|
||||
*/
|
||||
public setSpine(params: { target: sp.Skeleton, path: string, bundle?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }) {
|
||||
this.load({
|
||||
path: params.path,
|
||||
bundle: params.bundle,
|
||||
type: sp.SkeletonData,
|
||||
onComplete: (skeletonData) => {
|
||||
if (!skeletonData || !isValid(params.target)) {
|
||||
params.onFail && params.onFail();
|
||||
params.onComplete && params.onComplete(false);
|
||||
return;
|
||||
}
|
||||
params.target.skeletonData = skeletonData;
|
||||
params.onSuccess && params.onSuccess();
|
||||
params.onComplete && params.onComplete(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置图片资源
|
||||
* @param params.bundle 默认为resources
|
||||
* @param params.path bundle下的相对路径
|
||||
*
|
||||
* @example
|
||||
* setSprite({target:sprite, path:'img/a/spriteFrame', bundle:'resources', onComplete:(succ)=>{}})
|
||||
* setSprite({target:sprite, url:'http://img/a/avatar',ext:'.png', onComplete:(succ)=>{}})
|
||||
*/
|
||||
public setSprite(params: { target: Sprite, url: string, ext?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }): void;
|
||||
public setSprite(params: { target: Sprite, path: string, bundle?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }): void;
|
||||
public setSprite(params: { target: Sprite, path?: string, bundle?: string, url?: string, ext?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }) {
|
||||
if (params.url) {
|
||||
this.loadRemote({
|
||||
url: params.url,
|
||||
ext: params.ext,
|
||||
onComplete: (imageAsset: ImageAsset) => {
|
||||
if (!imageAsset || !isValid(params.target)) {
|
||||
params.onFail && params.onFail();
|
||||
params.onComplete && params.onComplete(false);
|
||||
return;
|
||||
}
|
||||
const spriteFrame = SpriteFrame.createWithImage(imageAsset);
|
||||
params.target.spriteFrame = spriteFrame;
|
||||
params.onSuccess && params.onSuccess();
|
||||
params.onComplete && params.onComplete(true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.load({
|
||||
path: params.path,
|
||||
bundle: params.bundle,
|
||||
type: SpriteFrame,
|
||||
onComplete: (spriteFrame) => {
|
||||
if (!spriteFrame || !isValid(params.target)) {
|
||||
params.onFail && params.onFail();
|
||||
params.onComplete && params.onComplete(false);
|
||||
return;
|
||||
}
|
||||
params.target.spriteFrame = spriteFrame;
|
||||
params.onSuccess && params.onSuccess();
|
||||
params.onComplete && params.onComplete(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放所有资源
|
||||
*/
|
||||
public releaseAll() {
|
||||
const assetList: Asset[] = [];
|
||||
this.assetMap.forEach(asset => assetList.push(asset));
|
||||
this.assetMap.clear();
|
||||
this.loadingMap.clear();
|
||||
// 延迟一秒释放资源
|
||||
setTimeout(() => {
|
||||
assetList.forEach(asset => asset.decRef());
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@ccclass('LoaderManager')
|
||||
export default class LoaderManager extends BaseManager {
|
||||
/**
|
||||
* `Loader`的目的是对资源加载进行分组引用计数管理。比如两个`Loader`实例都加载了同一个资源,当某个实例执行releaseAll后,并不会让引擎资源释放资源,只有两个实例都执行了释放资源后,才会让引擎资源释放资源。
|
||||
* @example
|
||||
* // 创建Loader实例
|
||||
* const loader = new LoaderManager.Loader();
|
||||
* // 加载资源
|
||||
* loader.load({path:'img/a/spriteFrame', bundle:'resources', type:SpriteFrame, onComplete:(spriteFrame)=>{}})
|
||||
* // 加载远程图片资源
|
||||
* loader.loadRemote({url:'http://img/a/avatar',ext:'.png', onComplete:(imageAsset)=>{}})
|
||||
* // 释放所有资源
|
||||
* loader.releaseAll();
|
||||
*/
|
||||
static Loader = Loader;
|
||||
|
||||
private handle(handle: string, { bundle, version, path, type, onProgress, onComplete }: { bundle?: string, version?: string, path: string, type?: typeof Asset, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (result: unknown | null) => void }) {
|
||||
if (!handle) {
|
||||
this.error('handle is empty');
|
||||
return onComplete && onComplete(null);
|
||||
}
|
||||
if (!path) {
|
||||
this.error(`${handle} fail. path is empty`);
|
||||
return onComplete && onComplete(null);
|
||||
}
|
||||
if (!bundle) bundle = 'resources';
|
||||
|
||||
const args: any[] = [path];
|
||||
if (type) args.push(type);
|
||||
if (onProgress) args.push(onProgress);
|
||||
args.push((err: string, res: any) => {
|
||||
if (err) {
|
||||
this.error(`${handle} "${path}" fail`, err);
|
||||
if (type === SpriteFrame && path.slice(-12) !== '/spriteFrame') {
|
||||
this.warn(`加载SpriteFrame类型的资源, 路径可能需要以/spriteFrame结尾, 如: 「${path}」 -> 「${path}/spriteFrame」`);
|
||||
} else if (type === Texture2D && path.slice(-8) !== '/texture') {
|
||||
this.warn(`加载Texture2D类型的资源, 路径可能需要以/texture结尾, 如: 「${path}」 -> 「${path}/texture」`);
|
||||
} else if (type === TextureCube && path.slice(-12) !== '/textureCube') {
|
||||
this.warn(`加载TextureCube类型的资源, 路径可能需要以/textureCube结尾, 如: 「${path}」 -> 「${path}/textureCube」`);
|
||||
}
|
||||
onComplete && onComplete(null);
|
||||
} else {
|
||||
onComplete && onComplete(res);
|
||||
}
|
||||
});
|
||||
|
||||
this.loadBundle({
|
||||
bundle, version,
|
||||
onComplete(bundle) {
|
||||
if (!bundle) return onComplete && onComplete(null);
|
||||
bundle[handle](args[0], args[1], args[2], args[3]);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载
|
||||
* @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle
|
||||
* @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC
|
||||
*/
|
||||
public preload(params: { path: string, bundle?: string, version?: string, type?: typeof Asset, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (item: AssetManager.RequestItem[] | null) => void }) {
|
||||
if (SceneAsset === params.type as typeof Asset) {
|
||||
this.handle('preloadScene', { path: params.path, bundle: params.bundle, version: params.version, onProgress: params.onProgress, onComplete: params.onComplete });
|
||||
} else {
|
||||
this.handle('preload', params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载
|
||||
* @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle
|
||||
* @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC
|
||||
*/
|
||||
public preloadDir(params: { path: string, bundle?: string, version?: string, type?: typeof Asset, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (items: AssetManager.RequestItem[] | null) => void }) {
|
||||
this.handle('preloadDir', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载bundle下的资源
|
||||
* @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle
|
||||
* @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC
|
||||
* @param params.path bundle下的相对路径
|
||||
* @param params.type 资源类型
|
||||
*/
|
||||
public load<T extends typeof Asset>(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (item: InstanceType<T> | null) => void }) {
|
||||
if (SceneAsset === params.type as typeof Asset) {
|
||||
this.handle('loadScene', { path: params.path, bundle: params.bundle, version: params.version, onProgress: params.onProgress, onComplete: params.onComplete });
|
||||
} else {
|
||||
this.handle('load', params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载bundle下的资源
|
||||
* @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle
|
||||
* @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC
|
||||
* @param params.path bundle下的相对路径
|
||||
* @param params.type 资源类型
|
||||
*/
|
||||
public loadAsync<T extends typeof Asset>(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void }): Promise<InstanceType<T> | null> {
|
||||
return new Promise((resolve) => {
|
||||
this.load({
|
||||
...params,
|
||||
onComplete: resolve
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载bundle下的资源
|
||||
* @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle
|
||||
* @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC
|
||||
* @param params.path bundle下的相对路径
|
||||
* @param params.type 资源类型
|
||||
*/
|
||||
public loadDir<T extends typeof Asset>(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (items: InstanceType<T>[] | null) => void }) {
|
||||
this.handle('loadDir', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载bundle下的资源
|
||||
* @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle
|
||||
* @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC
|
||||
* @param params.path bundle下的相对路径
|
||||
* @param params.type 资源类型
|
||||
*/
|
||||
public loadDirAsync<T extends typeof Asset>(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void }): Promise<InstanceType<T>[] | null> {
|
||||
return new Promise((resolve) => {
|
||||
this.loadDir({
|
||||
...params,
|
||||
onComplete: resolve
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁一个bundle中对应path和type的资源
|
||||
* @param params.bundle 默认为resources,如果是远程bundle,则使用url末位作为bundle名
|
||||
* @param params.path bundle下的相对路径
|
||||
* @param params.type 资源类型
|
||||
*/
|
||||
public release({ path, bundle, type }: { path: string, bundle?: string, type?: typeof Asset }) {
|
||||
if (!bundle) bundle = 'resources';
|
||||
assetManager.getBundle(bundle)?.release(path, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁一个bundle中所有的资源
|
||||
* @param bundle 默认为resources,如果是远程bundle,则使用url末位作为bundle名
|
||||
*/
|
||||
public releaseAll(bundle?: string) {
|
||||
if (!bundle) bundle = 'resources';
|
||||
const _bundle = assetManager.getBundle(bundle);
|
||||
if (!_bundle) return;
|
||||
// 只释放自己内部的资源,依赖的资源只减少引用计数
|
||||
_bundle.getDirWithPath('/', Asset).forEach((asset) => {
|
||||
_bundle.release(asset.path, asset.ctor);
|
||||
});
|
||||
// cocos提供的方法会将依赖的资源也卸载(这个设计很奇怪)
|
||||
// _bundle?.releaseAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁一个bundle中未使用的资源
|
||||
* @param bundle 默认为resources,如果是远程bundle,则使用url末位作为bundle名
|
||||
*/
|
||||
public releaseUnused(bundle?: string) {
|
||||
if (!bundle) bundle = 'resources';
|
||||
//@ts-ignore
|
||||
assetManager.getBundle(bundle)?.releaseUnusedAssets();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载一个bundle
|
||||
* @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle
|
||||
* @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC
|
||||
*/
|
||||
public loadBundle({ bundle, version, onComplete }: { bundle?: string, version?: string, onComplete?: (bundle: AssetManager.Bundle | null) => any }) {
|
||||
if (!bundle) bundle = 'resources';
|
||||
|
||||
if (MINIGAME) {
|
||||
if (REGEX.test(bundle)) {
|
||||
this.warn('小游戏环境下只支持加载远程Bundle的资源数据, 不会加载脚本');
|
||||
this.reloadBundle({ bundle, version, onComplete });
|
||||
return;
|
||||
}
|
||||
if (version && assetManager.downloader.bundleVers[bundle] !== version) {
|
||||
this.warn('小游戏环境下只支持更新Bundle的远程资源数据, 不会更新脚本');
|
||||
// 先加载本地bundle运行脚本
|
||||
assetManager.loadBundle(bundle, (err: Error, b: AssetManager.Bundle) => {
|
||||
if (err || !b) return onComplete?.(null);
|
||||
// 然后再走重载逻辑更新资源
|
||||
this.reloadBundle({ bundle, version, onComplete });
|
||||
});
|
||||
} else {
|
||||
assetManager.loadBundle(bundle, (err: Error, bundle: AssetManager.Bundle) => {
|
||||
onComplete && onComplete(err ? null : bundle);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (version) {
|
||||
assetManager.loadBundle(bundle, { version }, (err: Error, bundle: AssetManager.Bundle) => {
|
||||
onComplete && onComplete(err ? null : bundle);
|
||||
});
|
||||
} else {
|
||||
assetManager.loadBundle(bundle, (err: Error, bundle: AssetManager.Bundle) => {
|
||||
onComplete && onComplete(err ? null : bundle);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载一个bundle
|
||||
* @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle
|
||||
* @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC
|
||||
*/
|
||||
public loadBundleAsync(params: { bundle?: string, version?: string }): Promise<AssetManager.Bundle | null> {
|
||||
return new Promise((resolve) => {
|
||||
this.loadBundle({
|
||||
...params,
|
||||
onComplete: resolve
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个已经加载的bundle
|
||||
* @param bundle 默认为resources,如果是远程bundle,则使用url末位作为bundle名
|
||||
*/
|
||||
public getBundle(bundle?: string) {
|
||||
if (!bundle) bundle = 'resources';
|
||||
return assetManager.getBundle(bundle);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除一个已经加载的bundle
|
||||
* @param bundle 默认为resources,如果是远程bundle,则使用url末位作为bundle名
|
||||
*/
|
||||
public removeBundle(bundle?: string) {
|
||||
if (!bundle) bundle = 'resources';
|
||||
const b = assetManager.getBundle(bundle);
|
||||
if (b) assetManager.removeBundle(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载一个bundle(只重载资源列表)
|
||||
* - 只有远程bundle支持重载
|
||||
* @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle
|
||||
* @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC
|
||||
*/
|
||||
public reloadBundle({ bundle, version, onComplete }: { bundle?: string, version?: string, onComplete?: (bundle: AssetManager.Bundle | null) => any }) {
|
||||
if (!bundle) bundle = 'resources';
|
||||
|
||||
let baseUrl = '';
|
||||
let configUrl = '';
|
||||
|
||||
if (REGEX.test(bundle)) {
|
||||
baseUrl = bundle;
|
||||
const suffix = version ? `${version}.` : '';
|
||||
configUrl = `${baseUrl}config.${suffix}json`;
|
||||
}
|
||||
else {
|
||||
baseUrl = `${assetManager.downloader.remoteServerAddress}remote/${bundle}/`;
|
||||
const suffix = version ? `${version}.` : '';
|
||||
configUrl = `${baseUrl}config.${suffix}json`;
|
||||
}
|
||||
|
||||
// 清除可能存在的config缓存
|
||||
assetManager.cacheManager?.removeCache(configUrl);
|
||||
assetManager.loadRemote(configUrl, (err: Error, data: JsonAsset) => {
|
||||
if (err) {
|
||||
this.error(`下载Bundle配置失败: ${configUrl}`);
|
||||
onComplete?.(null);
|
||||
return;
|
||||
}
|
||||
|
||||
this.releaseAll(path.basename(bundle));
|
||||
this.removeBundle(path.basename(bundle));
|
||||
|
||||
const ab = new AssetManager.Bundle();
|
||||
const config = data.json as any;
|
||||
config.base = baseUrl;
|
||||
ab.init(config);
|
||||
onComplete?.(ab);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载一个bundle(只重载资源列表)
|
||||
* - 只有远程bundle支持重载
|
||||
* @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle
|
||||
* @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC
|
||||
*/
|
||||
public reloadBundleAsync(params: { bundle?: string, version?: string }): Promise<AssetManager.Bundle | null> {
|
||||
return new Promise((resolve) => {
|
||||
this.reloadBundle({
|
||||
...params,
|
||||
onComplete: resolve
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载远程资源
|
||||
* @example
|
||||
* loadRemote({url:'', ext:'.png', onComplete:(result){ }})
|
||||
*/
|
||||
public loadRemote({ url, ext, onComplete }: { url: string, ext?: string, onComplete?: (result: Asset | null) => void }) {
|
||||
if (ext) {
|
||||
assetManager.loadRemote(url, { ext }, (error, res) => {
|
||||
if (error) {
|
||||
this.error(`loadRemote ${url} fail`);
|
||||
return onComplete && onComplete(null);
|
||||
}
|
||||
onComplete && onComplete(res);
|
||||
});
|
||||
} else {
|
||||
assetManager.loadRemote(url, (error, res) => {
|
||||
if (error) {
|
||||
this.error(`loadRemote ${url} fail`);
|
||||
return onComplete && onComplete(null);
|
||||
}
|
||||
onComplete && onComplete(res);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载远程资源
|
||||
* @example
|
||||
* await loadRemoteAsync({url:'', ext:'.png'})
|
||||
*/
|
||||
public loadRemoteAsync(params: { url: string, ext?: string }): Promise<Asset | null> {
|
||||
return new Promise((resolve) => {
|
||||
this.loadRemote({
|
||||
...params,
|
||||
onComplete: resolve
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置字体资源
|
||||
* @param params.bundle 默认为resources
|
||||
* @param params.path bundle下的相对路径
|
||||
*
|
||||
* @example
|
||||
* setFont({target:label, path:'font/num', bundle:'resources', onComplete:(succ)=>{}})
|
||||
* setFont({target:label, url:'http://img/a/font',ext:'.ttf', onComplete:(succ)=>{}})
|
||||
*/
|
||||
public setFont(params: { target: Label, url: string, ext?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }): void;
|
||||
public setFont(params: { target: Label, path: string, bundle?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }): void;
|
||||
public setFont(params: { target: Label, path?: string, bundle?: string, url?: string, ext?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }) {
|
||||
if (params.url) {
|
||||
this.loadRemote({
|
||||
url: params.url,
|
||||
ext: params.ext,
|
||||
onComplete: (font: Font) => {
|
||||
if (!font || !isValid(params.target)) {
|
||||
params.onFail && params.onFail();
|
||||
params.onComplete && params.onComplete(false);
|
||||
return;
|
||||
}
|
||||
params.target.font = font;
|
||||
params.onSuccess && params.onSuccess();
|
||||
params.onComplete && params.onComplete(true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.load({
|
||||
path: params.path,
|
||||
bundle: params.bundle,
|
||||
type: Font,
|
||||
onComplete: (font) => {
|
||||
if (!font || !isValid(params.target)) {
|
||||
params.onFail && params.onFail();
|
||||
params.onComplete && params.onComplete(false);
|
||||
return;
|
||||
}
|
||||
params.target.font = font;
|
||||
params.onSuccess && params.onSuccess();
|
||||
params.onComplete && params.onComplete(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Spine资源
|
||||
* @param params.bundle 默认为resources
|
||||
* @param params.path bundle下的相对路径
|
||||
*
|
||||
* @example
|
||||
* setSpine({target:spine, path:'spine/role', bundle:'resources', onComplete:(succ)=>{}})
|
||||
*/
|
||||
public setSpine(params: { target: sp.Skeleton, path: string, bundle?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }) {
|
||||
this.load({
|
||||
path: params.path,
|
||||
bundle: params.bundle,
|
||||
type: sp.SkeletonData,
|
||||
onComplete: (skeletonData) => {
|
||||
if (!skeletonData || !isValid(params.target)) {
|
||||
params.onFail && params.onFail();
|
||||
params.onComplete && params.onComplete(false);
|
||||
return;
|
||||
}
|
||||
params.target.skeletonData = skeletonData;
|
||||
params.onSuccess && params.onSuccess();
|
||||
params.onComplete && params.onComplete(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置图片资源
|
||||
* @param params.bundle 默认为resources
|
||||
* @param params.path bundle下的相对路径
|
||||
*
|
||||
* @example
|
||||
* setSprite({target:sprite, path:'img/a/spriteFrame', bundle:'resources', onComplete:(succ)=>{}})
|
||||
* setSprite({target:sprite, url:'http://img/a/avatar',ext:'.png', onComplete:(succ)=>{}})
|
||||
*/
|
||||
public setSprite(params: { target: Sprite, url: string, ext?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }): void;
|
||||
public setSprite(params: { target: Sprite, path: string, bundle?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }): void;
|
||||
public setSprite(params: { target: Sprite, path?: string, bundle?: string, url?: string, ext?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }) {
|
||||
if (params.url) {
|
||||
this.loadRemote({
|
||||
url: params.url,
|
||||
ext: params.ext,
|
||||
onComplete: (imageAsset: ImageAsset) => {
|
||||
if (!imageAsset || !isValid(params.target)) {
|
||||
params.onFail && params.onFail();
|
||||
params.onComplete && params.onComplete(false);
|
||||
return;
|
||||
}
|
||||
const spriteFrame = SpriteFrame.createWithImage(imageAsset);
|
||||
params.target.spriteFrame = spriteFrame;
|
||||
params.onSuccess && params.onSuccess();
|
||||
params.onComplete && params.onComplete(true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.load({
|
||||
path: params.path,
|
||||
bundle: params.bundle,
|
||||
type: SpriteFrame,
|
||||
onComplete: (spriteFrame) => {
|
||||
if (!spriteFrame || !isValid(params.target)) {
|
||||
params.onFail && params.onFail();
|
||||
params.onComplete && params.onComplete(false);
|
||||
return;
|
||||
}
|
||||
params.target.spriteFrame = spriteFrame;
|
||||
params.onSuccess && params.onSuccess();
|
||||
params.onComplete && params.onComplete(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "b3bf5337-0c73-4771-87b5-9e735962be81",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
12
extensions/app/assets/manager/sound.meta
Normal file
12
extensions/app/assets/manager/sound.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "a99e9a5e-e037-428c-b8f4-e5ad266db8ee",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
152
extensions/app/assets/manager/sound/Audio.ts
Normal file
152
extensions/app/assets/manager/sound/Audio.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import { AudioClip, AudioSource, Node } from 'cc';
|
||||
|
||||
export default class Audio {
|
||||
private volume = 1;
|
||||
private volumeScale = 1;
|
||||
private mute = false;
|
||||
private endedCallback: Function = null;
|
||||
private startedCallback: Function = null;
|
||||
|
||||
private _playing = false;
|
||||
public get playing() {
|
||||
return this._playing;
|
||||
}
|
||||
private set playing(value) {
|
||||
this._playing = value;
|
||||
}
|
||||
|
||||
private _paused = false;
|
||||
public get paused() {
|
||||
return this._paused;
|
||||
}
|
||||
private set paused(value) {
|
||||
this._paused = value;
|
||||
}
|
||||
|
||||
private audioSource: AudioSource = null;
|
||||
constructor() {
|
||||
const node = new Node('audio');
|
||||
this.audioSource = node.addComponent(AudioSource);
|
||||
node.on(AudioSource.EventType.ENDED, this.onAudioEnded, this);
|
||||
node.on(AudioSource.EventType.STARTED, this.onAudioStarted, this);
|
||||
}
|
||||
|
||||
private onAudioEnded() {
|
||||
if (this.endedCallback) {
|
||||
const endedCallback = this.endedCallback;
|
||||
this.endedCallback = null;
|
||||
endedCallback();
|
||||
}
|
||||
}
|
||||
|
||||
private onAudioStarted() {
|
||||
if (this.startedCallback) {
|
||||
const startedCallback = this.startedCallback;
|
||||
this.startedCallback = null;
|
||||
startedCallback();
|
||||
}
|
||||
}
|
||||
|
||||
play(clip: AudioClip, onEnded: Function = null, onStarted: Function = null) {
|
||||
this.audioSource.clip = clip;
|
||||
this.endedCallback = onEnded;
|
||||
this.startedCallback = onStarted;
|
||||
this.audioSource.play();
|
||||
this.playing = true;
|
||||
this.paused = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.playing = false;
|
||||
this.paused = false;
|
||||
this.audioSource.stop();
|
||||
this.audioSource.node.emit(AudioSource.EventType.ENDED);
|
||||
return this;
|
||||
}
|
||||
|
||||
pause() {
|
||||
if (!this.playing) return this;
|
||||
|
||||
this.paused = true;
|
||||
this.audioSource.pause();
|
||||
return this;
|
||||
}
|
||||
|
||||
resume() {
|
||||
if (!this.playing) return this;
|
||||
if (!this.paused) return this;
|
||||
|
||||
this.paused = false;
|
||||
this.audioSource.play();
|
||||
return this;
|
||||
}
|
||||
|
||||
setVolume(volume = 1, scale?: number) {
|
||||
this.volume = volume;
|
||||
if (typeof scale === 'number') this.volumeScale = scale;
|
||||
this.audioSource.volume = volume * this.volumeScale * (this.mute ? 0 : 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
getVolume() {
|
||||
return this.volume;
|
||||
}
|
||||
|
||||
setVolumeScale(scale = 1) {
|
||||
this.volumeScale = scale;
|
||||
this.audioSource.volume = this.volume * scale * (this.mute ? 0 : 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
getVolumeScale() {
|
||||
return this.volumeScale;
|
||||
}
|
||||
|
||||
setLoop(loop: boolean) {
|
||||
this.audioSource.loop = loop;
|
||||
return this;
|
||||
}
|
||||
|
||||
getLoop() {
|
||||
return this.audioSource.loop;
|
||||
}
|
||||
|
||||
setMute(mute = true) {
|
||||
this.mute = mute;
|
||||
this.setVolume(this.volume);
|
||||
return this;
|
||||
}
|
||||
|
||||
getMute() {
|
||||
return this.mute;
|
||||
}
|
||||
|
||||
onEnded(endedCallback: Function) {
|
||||
this.endedCallback = endedCallback;
|
||||
return this;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.volume = 1;
|
||||
this.volumeScale = 1;
|
||||
this.mute = false;
|
||||
this.paused = false;
|
||||
this.endedCallback = null;
|
||||
this.startedCallback = null;
|
||||
if (this.audioSource) {
|
||||
this.audioSource.stop();
|
||||
this.audioSource.volume = 1;
|
||||
this.audioSource.clip = null;
|
||||
this.audioSource.loop = false;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.clear();
|
||||
this.audioSource.destroy();
|
||||
this.audioSource.node.destroy();
|
||||
this.audioSource = null;
|
||||
}
|
||||
}
|
||||
9
extensions/app/assets/manager/sound/Audio.ts.meta
Normal file
9
extensions/app/assets/manager/sound/Audio.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "a40e77a6-cdac-445c-a50c-a8ced2411dbc",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
287
extensions/app/assets/manager/sound/AudioEngine.ts
Normal file
287
extensions/app/assets/manager/sound/AudioEngine.ts
Normal file
@@ -0,0 +1,287 @@
|
||||
import { AudioClip } from 'cc';
|
||||
import Audio from './Audio';
|
||||
import AudioManager from './AudioManager';
|
||||
|
||||
export default class AudioEngine {
|
||||
private static _inst: AudioEngine = null;
|
||||
static get inst() {
|
||||
if (!this._inst) this._inst = new AudioEngine();
|
||||
return this._inst;
|
||||
}
|
||||
private constructor() { }
|
||||
|
||||
/**effect的id从1开始,music的id始终为0 */
|
||||
private audioID = 1;
|
||||
private endedCallbackMap: Map<number, Function> = new Map();
|
||||
private effectMap: Map<number, Audio> = new Map();
|
||||
private music: Audio = null;
|
||||
|
||||
private musicMute = false;
|
||||
private musicVolumeScale = 1;
|
||||
|
||||
private effectMute = false;
|
||||
private effectVolumeScale = 1;
|
||||
|
||||
////////////////////////////////
|
||||
// 音效 //
|
||||
////////////////////////////////
|
||||
playEffect(audioClip: AudioClip, volume = 1, loop = false, onStarted: (audioID: number) => any = null, onEnded: Function = null) {
|
||||
if (this.audioID > 100000) this.audioID = 1;
|
||||
|
||||
const audioID = this.audioID++;
|
||||
const audio = AudioManager.inst.getAudio();
|
||||
this.effectMap.set(audioID, audio);
|
||||
if (onEnded) this.endedCallbackMap.set(audioID, onEnded);
|
||||
|
||||
audio.setLoop(loop)
|
||||
.setMute(this.effectMute)
|
||||
.setVolume(volume, this.effectVolumeScale)
|
||||
.play(audioClip, () => {
|
||||
AudioManager.inst.putAudio(audio);
|
||||
this.effectMap.delete(audioID);
|
||||
const callback = this.endedCallbackMap.get(audioID);
|
||||
if (callback) {
|
||||
this.endedCallbackMap.delete(audioID);
|
||||
callback();
|
||||
}
|
||||
}, () => {
|
||||
onStarted && onStarted(audioID);
|
||||
});
|
||||
|
||||
return audioID;
|
||||
}
|
||||
|
||||
stopEffect(id: number) {
|
||||
return !!this.effectMap.get(id)?.stop();
|
||||
}
|
||||
|
||||
stopAllEffects() {
|
||||
this.effectMap.forEach((audio) => {
|
||||
audio.stop();
|
||||
});
|
||||
}
|
||||
|
||||
pauseEffect(id: number) {
|
||||
return !!this.effectMap.get(id)?.pause();
|
||||
}
|
||||
|
||||
pauseAllEffects() {
|
||||
this.effectMap.forEach((audio) => {
|
||||
audio.pause();
|
||||
});
|
||||
}
|
||||
|
||||
resumeEffect(id: number) {
|
||||
return !!this.effectMap.get(id)?.resume();
|
||||
}
|
||||
|
||||
resumeAllEffects() {
|
||||
this.effectMap.forEach((audio) => {
|
||||
audio.resume();
|
||||
});
|
||||
}
|
||||
|
||||
setEffectMute(id: number, mute: boolean) {
|
||||
return !!this.effectMap.get(id)?.setMute(mute);
|
||||
}
|
||||
|
||||
getEffectMute(id: number) {
|
||||
return !!this.effectMap.get(id)?.getMute();
|
||||
}
|
||||
|
||||
setEffectVolume(id: number, volume: number) {
|
||||
return !!this.effectMap.get(id)?.setVolume(volume);
|
||||
}
|
||||
|
||||
getEffectVolume(id: number) {
|
||||
return this.effectMap.get(id)?.getVolume() || 0;
|
||||
}
|
||||
|
||||
setAllEffectsVolume(volume: number) {
|
||||
this.effectMap.forEach((audio) => {
|
||||
audio.setVolume(volume);
|
||||
});
|
||||
}
|
||||
|
||||
setEffectVolumeScale(id: number, volume: number) {
|
||||
return !!this.effectMap.get(id)?.setVolumeScale(volume);
|
||||
}
|
||||
|
||||
getEffectVolumeScale(id: number) {
|
||||
return this.effectMap.get(id)?.getVolumeScale() || 0;
|
||||
}
|
||||
|
||||
setGlobalEffectsVolumeScale(scale: number) {
|
||||
this.effectVolumeScale = scale;
|
||||
this.effectMap.forEach((audio) => {
|
||||
audio.setVolumeScale(scale);
|
||||
});
|
||||
}
|
||||
|
||||
getGlobalEffectsVolumeScale() {
|
||||
return this.effectVolumeScale;
|
||||
}
|
||||
|
||||
setGlobalEffectsMute(mute: boolean) {
|
||||
this.effectMute = mute;
|
||||
this.effectMap.forEach((audio) => {
|
||||
audio.setMute(mute);
|
||||
});
|
||||
}
|
||||
|
||||
getGlobalEffectsMute() {
|
||||
return this.effectMute;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// 音乐 //
|
||||
////////////////////////////////
|
||||
playMusic(audioClip: AudioClip, volume = 1, onStarted: Function = null) {
|
||||
this.stopMusic();
|
||||
|
||||
this.music = AudioManager.inst.getAudio();
|
||||
this.music
|
||||
.setLoop(true)
|
||||
.setMute(this.musicMute)
|
||||
.setVolume(volume, this.musicVolumeScale)
|
||||
.play(audioClip, null, onStarted);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
stopMusic() {
|
||||
if (!this.music) return false;
|
||||
this.music.destroy();
|
||||
this.music = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
pauseMusic() {
|
||||
if (!this.music) return false;
|
||||
this.music.pause();
|
||||
return true;
|
||||
}
|
||||
|
||||
resumeMusic() {
|
||||
if (!this.music) return false;
|
||||
this.music.resume();
|
||||
return true;
|
||||
}
|
||||
|
||||
setMusicVolume(volume: number) {
|
||||
if (!this.music) return false;
|
||||
this.music.setVolume(volume);
|
||||
return true;
|
||||
}
|
||||
|
||||
getMusicVolume() {
|
||||
if (!this.music) return -1;
|
||||
return this.music.getVolume();
|
||||
}
|
||||
|
||||
setMusicVolumeScale(scale: number) {
|
||||
this.musicVolumeScale = scale;
|
||||
this.music?.setVolumeScale(scale);
|
||||
return true;
|
||||
}
|
||||
|
||||
getMusicVolumeScale() {
|
||||
return this.musicVolumeScale;
|
||||
}
|
||||
|
||||
setMusicMute(mute: boolean) {
|
||||
this.musicMute = mute;
|
||||
this.music?.setMute(mute);
|
||||
return true;
|
||||
}
|
||||
|
||||
getMusicMute() {
|
||||
return this.musicMute;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// 通用 //
|
||||
////////////////////////////////
|
||||
setEndedCallback(audioID: number, callback: Function) {
|
||||
if (audioID === 0) {
|
||||
return !!this.music?.onEnded(callback);
|
||||
} else {
|
||||
if (this.effectMap.has(audioID)) {
|
||||
this.endedCallbackMap.set(audioID, callback);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
stop(audioID: number) {
|
||||
if (audioID === 0) {
|
||||
return this.stopMusic();
|
||||
} else {
|
||||
return this.stopEffect(audioID);
|
||||
}
|
||||
}
|
||||
|
||||
pause(audioID: number) {
|
||||
if (audioID === 0) {
|
||||
return this.pauseMusic();
|
||||
} else {
|
||||
return this.pauseEffect(audioID);
|
||||
}
|
||||
}
|
||||
|
||||
resume(audioID: number) {
|
||||
if (audioID === 0) {
|
||||
return this.resumeMusic();
|
||||
} else {
|
||||
return this.resumeEffect(audioID);
|
||||
}
|
||||
}
|
||||
|
||||
pauseAll() {
|
||||
this.pauseMusic();
|
||||
this.pauseAllEffects();
|
||||
}
|
||||
|
||||
resumeAll() {
|
||||
this.resumeMusic();
|
||||
this.resumeAllEffects();
|
||||
}
|
||||
|
||||
stopAll() {
|
||||
this.stopMusic();
|
||||
this.stopAllEffects();
|
||||
}
|
||||
|
||||
setVolume(audioID: number, volume: number) {
|
||||
if (audioID === 0) {
|
||||
return this.setMusicVolume(volume);
|
||||
} else {
|
||||
return this.setEffectVolume(audioID, volume);
|
||||
}
|
||||
}
|
||||
|
||||
getVolume(audioID: number) {
|
||||
if (audioID === 0) {
|
||||
return this.getMusicVolume();
|
||||
} else {
|
||||
return this.getEffectVolume(audioID);
|
||||
}
|
||||
}
|
||||
|
||||
setVolumeScale(audioID: number, scale: number) {
|
||||
if (audioID === 0) {
|
||||
return this.setMusicVolumeScale(scale);
|
||||
} else {
|
||||
return this.setEffectVolumeScale(audioID, scale);
|
||||
}
|
||||
}
|
||||
|
||||
getVolumeScale(audioID: number) {
|
||||
if (audioID === 0) {
|
||||
return this.getMusicVolumeScale();
|
||||
} else {
|
||||
return this.getEffectVolumeScale(audioID);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
extensions/app/assets/manager/sound/AudioEngine.ts.meta
Normal file
9
extensions/app/assets/manager/sound/AudioEngine.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "7a638f5c-801f-4e69-88cc-3fc72e1be985",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
24
extensions/app/assets/manager/sound/AudioManager.ts
Normal file
24
extensions/app/assets/manager/sound/AudioManager.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import Audio from './Audio';
|
||||
|
||||
export default class AudioManager {
|
||||
private static _inst: AudioManager = null;
|
||||
static get inst() {
|
||||
if (!this._inst) this._inst = new AudioManager();
|
||||
return this._inst;
|
||||
}
|
||||
private constructor() { }
|
||||
|
||||
private audioArray: Audio[] = [];
|
||||
|
||||
getAudio() {
|
||||
if (this.audioArray.length) {
|
||||
return this.audioArray.pop();
|
||||
}
|
||||
return new Audio();
|
||||
}
|
||||
|
||||
putAudio(audio: Audio) {
|
||||
audio.clear();
|
||||
this.audioArray.push(audio);
|
||||
}
|
||||
}
|
||||
9
extensions/app/assets/manager/sound/AudioManager.ts.meta
Normal file
9
extensions/app/assets/manager/sound/AudioManager.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "5902cecf-4966-4675-95a9-ab0f0f271857",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
717
extensions/app/assets/manager/sound/SoundManager.ts
Normal file
717
extensions/app/assets/manager/sound/SoundManager.ts
Normal file
@@ -0,0 +1,717 @@
|
||||
import { AssetManager, AudioClip, Button, Game, _decorator, game, isValid, sys } from 'cc';
|
||||
import { IEffectName, IMusicName } from '../../../../../assets/app-builtin/app-admin/executor';
|
||||
import Core from '../../Core';
|
||||
import BaseManager from '../../base/BaseManager';
|
||||
import AudioEngine from './AudioEngine';
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
interface playMusic<T> { name: T, volume?: number, force?: boolean, onPlay?: Function, onError?: Function }
|
||||
interface playEffect<T> { name: T, volume?: number, loop?: boolean, interval?: number, onPlay?: (audioID: number) => any, onError?: Function, onEnded?: Function }
|
||||
interface playMusicAsync<T> { name: T, volume?: number, force?: boolean }
|
||||
interface playEffectAsync<T> { name: T, volume?: number, loop?: boolean, interval?: number, onEnded?: Function }
|
||||
|
||||
interface playMusicWidthBundle { name: string, bundle: string, volume?: number, force?: boolean, onPlay?: Function, onError?: Function }
|
||||
interface playEffectWidthBundle { name: string, bundle: string, volume?: number, loop?: boolean, interval?: number, onPlay?: (audioID: number) => any, onError?: Function, onEnded?: Function }
|
||||
interface playMusicWidthBundleAsync { name: string, bundle: string, volume?: number, force?: boolean }
|
||||
interface playEffectWidthBundleAsync { name: string, bundle: string, volume?: number, loop?: boolean, interval?: number, onEnded?: Function }
|
||||
|
||||
const storage = {
|
||||
set(key: string, value: any) {
|
||||
sys.localStorage.setItem(key, JSON.stringify(value));
|
||||
},
|
||||
get(key: string) {
|
||||
const data = sys.localStorage.getItem(key);
|
||||
if (data && typeof data === 'string') {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 音乐名字枚举
|
||||
*/
|
||||
const MusicName: { [key in IMusicName]: key } = new Proxy({} as any, {
|
||||
get: function (target, key) {
|
||||
if (target[key]) return target[key];
|
||||
target[key] = key;
|
||||
return key;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 音效名字枚举
|
||||
*/
|
||||
const EffectName: { [key in IEffectName]: key } = new Proxy({} as any, {
|
||||
get: function (target, key) {
|
||||
if (target[key]) return target[key];
|
||||
target[key] = key;
|
||||
return key;
|
||||
}
|
||||
});
|
||||
|
||||
const BundleName = 'app-sound';
|
||||
@ccclass('SoundManager')
|
||||
export default class SoundManager<E extends string, M extends string> extends BaseManager {
|
||||
/**静态设置 */
|
||||
static setting: {
|
||||
/**预加载 */
|
||||
preload?: (IMusicName | IEffectName)[],
|
||||
|
||||
/**默认播放的音乐名 */
|
||||
defaultMusicName?: IMusicName | '',
|
||||
/**默认音乐的音量 */
|
||||
defaultMusicVolume?: number,
|
||||
|
||||
/**默认按钮的音效名 */
|
||||
defaultEffectName?: IEffectName | '',
|
||||
/**默认按钮音效的音量 */
|
||||
defaultEffectVolume?: number
|
||||
} = {};
|
||||
|
||||
/**音乐名字枚举 */
|
||||
static MusicName = MusicName;
|
||||
/**音效名字枚举 */
|
||||
static EffectName = EffectName;
|
||||
|
||||
private musicMuteCacheKey = 'SoundManager:MusicMute';
|
||||
private effectMuteCacheKey = 'SoundManager:EffectMute';
|
||||
private musicVolumeScaleCacheKey = 'SoundManager:MusicVolumeScale';
|
||||
private effectVolumeScaleCacheKey = 'SoundManager:EffectVolumeScale';
|
||||
|
||||
private defaultMusicName = '';
|
||||
private defaultMusicVolume = 0.2;
|
||||
private defaultEffectName = '';
|
||||
private defaultEffectVolume = 1;
|
||||
|
||||
private audioCache = {};
|
||||
private effectInterval: { [key in string]: number } = {};
|
||||
private playingMusic = { uuid: '', id: -1, name: '', volume: 1, playing: false, paused: false };
|
||||
|
||||
protected init(finish: Function) {
|
||||
const setting = SoundManager.setting;
|
||||
|
||||
// 默认音乐
|
||||
if (setting.defaultMusicName) this.defaultMusicName = setting.defaultMusicName;
|
||||
if (typeof setting.defaultMusicVolume === 'number') this.defaultMusicVolume = setting.defaultMusicVolume;
|
||||
|
||||
// 默认按钮音效
|
||||
if (setting.defaultEffectName) this.defaultEffectName = setting.defaultEffectName;
|
||||
if (typeof setting.defaultEffectVolume === 'number') this.defaultEffectVolume = setting.defaultEffectVolume;
|
||||
|
||||
if (this.musicMuteCacheKey) {
|
||||
const musicMute = storage.get(this.musicMuteCacheKey) === true;
|
||||
AudioEngine.inst.setMusicMute(musicMute);
|
||||
} else {
|
||||
this.warn('musicMuteCacheKey不能为空');
|
||||
}
|
||||
if (this.effectMuteCacheKey) {
|
||||
const effectMute = storage.get(this.effectMuteCacheKey) === true;
|
||||
AudioEngine.inst.setGlobalEffectsMute(effectMute);
|
||||
} else {
|
||||
this.warn('effectMuteCacheKey不能为空');
|
||||
}
|
||||
if (this.musicVolumeScaleCacheKey) {
|
||||
const musicVolumeScale = storage.get(this.musicVolumeScaleCacheKey);
|
||||
if (typeof musicVolumeScale === 'number') AudioEngine.inst.setMusicVolumeScale(musicVolumeScale);
|
||||
} else {
|
||||
this.warn('musicVolumeScaleCacheKey不能为空');
|
||||
}
|
||||
if (this.effectVolumeScaleCacheKey) {
|
||||
const effectVolumeScale = storage.get(this.effectVolumeScaleCacheKey);
|
||||
if (typeof effectVolumeScale === 'number') AudioEngine.inst.setGlobalEffectsVolumeScale(effectVolumeScale);
|
||||
} else {
|
||||
this.warn('effectVolumeScaleCacheKey不能为空');
|
||||
}
|
||||
|
||||
super.init(finish);
|
||||
|
||||
// 预加载
|
||||
setting.preload?.forEach((path: string) => {
|
||||
Core.inst.manager.loader.preload({
|
||||
bundle: BundleName,
|
||||
type: AudioClip,
|
||||
path: path
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected onLoad() {
|
||||
game.on(Game.EVENT_HIDE, function () {
|
||||
AudioEngine.inst.pauseAll();
|
||||
});
|
||||
game.on(Game.EVENT_SHOW, function () {
|
||||
AudioEngine.inst.resumeAll();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载声音资源
|
||||
* @param name sound路径
|
||||
* @param bundle Bundle名,默认为app-sound
|
||||
*/
|
||||
public preload(name: string, bundle: string, complete?: (item: AssetManager.RequestItem[] | null) => any): void;
|
||||
public preload(name: (E | M), complete?: (item: AssetManager.RequestItem[] | null) => any): void;
|
||||
public preload(name: string, ...args: any[]) {
|
||||
const bundleName = (args.length >= 1
|
||||
&& (typeof args[0] === 'string')
|
||||
? (args[0] || BundleName)
|
||||
: BundleName
|
||||
) as string;
|
||||
const complete = (args.length >= 1
|
||||
&& (args[args.length - 1] instanceof Function)
|
||||
? args[args.length - 1]
|
||||
: null
|
||||
) as (item: AssetManager.RequestItem[] | null) => any;
|
||||
|
||||
if (!name) {
|
||||
this.error('preload', 'fail');
|
||||
complete && setTimeout(() => {
|
||||
if (!isValid(this)) return;
|
||||
complete(null);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (name.indexOf('effect') !== 0 && name.indexOf('music') !== 0) {
|
||||
this.error('preload', 'fail', name);
|
||||
complete && setTimeout(() => {
|
||||
if (!isValid(this)) return;
|
||||
complete(null);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 远程加载
|
||||
Core.inst.manager.loader.preload({
|
||||
bundle: bundleName,
|
||||
path: name,
|
||||
type: AudioClip,
|
||||
onComplete: complete
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载声音资源
|
||||
* @param name sound路径
|
||||
* @param bundle Bundle名,默认为app-sound
|
||||
* @param progress 加载进度回调
|
||||
* @param complete 加载完成回调
|
||||
*/
|
||||
public load(name: string, bundle: string): void;
|
||||
public load(name: string, bundle: string, complete: (result: AudioClip | null) => any): void;
|
||||
public load(name: string, bundle: string, progress: (finish: number, total: number, item: AssetManager.RequestItem) => void, complete: (result: AudioClip | null) => any): void;
|
||||
public load(name: (E | M)): void;
|
||||
public load(name: (E | M), complete: (result: AudioClip | null) => any): void;
|
||||
public load(name: (E | M), progress: (finish: number, total: number, item: AssetManager.RequestItem) => void, complete: (result: AudioClip | null) => any): void;
|
||||
public load(name: string, ...args: any[]): void {
|
||||
const bundleName = (args.length >= 1
|
||||
&& (typeof args[0] === 'string')
|
||||
? (args[0] || BundleName)
|
||||
: BundleName
|
||||
) as string;
|
||||
const progress = (args.length >= 2
|
||||
&& (args[args.length - 1] instanceof Function)
|
||||
&& (args[args.length - 2] instanceof Function)
|
||||
? args[args.length - 2]
|
||||
: null
|
||||
) as (finish: number, total: number, item: AssetManager.RequestItem) => void;
|
||||
const complete = (args.length >= 1
|
||||
&& (args[args.length - 1] instanceof Function)
|
||||
? args[args.length - 1]
|
||||
: null
|
||||
) as (result: AudioClip | null) => any;
|
||||
|
||||
if (!name) {
|
||||
this.error('load', 'fail');
|
||||
complete && setTimeout(() => {
|
||||
if (!isValid(this)) return;
|
||||
complete(null);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const soundName = `${bundleName}://${name}`;
|
||||
|
||||
// 判断有无缓存
|
||||
const audio = this.audioCache[soundName];
|
||||
if (audio) {
|
||||
complete && setTimeout(() => {
|
||||
if (!isValid(this)) return;
|
||||
complete(audio);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 远程加载
|
||||
Core.inst.manager.loader.load({
|
||||
path: name,
|
||||
bundle: bundleName,
|
||||
type: AudioClip,
|
||||
onProgress: progress,
|
||||
onComplete: (audioClip) => {
|
||||
if (!isValid(this)) return;
|
||||
if (audioClip) {
|
||||
this.audioCache[soundName] = audioClip;
|
||||
complete && complete(audioClip);
|
||||
} else {
|
||||
complete && complete(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放声音资源
|
||||
* @param name 声音路径
|
||||
* @param bundle Bundle名,默认为app-sound
|
||||
*/
|
||||
public release(name: string, bundle: string): void;
|
||||
public release(name: E | M): void;
|
||||
public release(name: string, bundle?: string) {
|
||||
const bundleName = bundle || BundleName;
|
||||
const soundName = `${bundleName}://${name}`;
|
||||
|
||||
delete this.audioCache[soundName];
|
||||
Core.inst.manager.loader.release({ bundle: bundleName, path: name, type: AudioClip });
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放默认音乐
|
||||
*/
|
||||
public playDefaultMusic(onPlay?: Function) {
|
||||
if (this.defaultMusicName) {
|
||||
this.playMusic({ name: <M>this.defaultMusicName, volume: this.defaultMusicVolume, onPlay });
|
||||
} else {
|
||||
this.warn('defaultMusicName 不存在');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放默认音效
|
||||
*/
|
||||
public playDefaultEffect(onPlay?: (audioID: number) => void) {
|
||||
if (this.defaultEffectName) {
|
||||
this.playEffect({ name: <E>this.defaultEffectName, volume: this.defaultEffectVolume, onPlay });
|
||||
} else {
|
||||
this.warn('defaultEffectName 不存在');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置按钮点击播放的音效,优先级高于默认音效
|
||||
* @param name 音效(如果为空,则使用默认音效)
|
||||
* @param opts.volume 音量
|
||||
* @param opts.interval 多少秒内不会重复播放
|
||||
*/
|
||||
public setButtonEffect(target: Button, name?: E, opts?: {
|
||||
volume: number,
|
||||
interval: number
|
||||
}) {
|
||||
if (name) {
|
||||
const { volume = 1, interval = 0 } = opts || {};
|
||||
//@ts-ignore
|
||||
target.node['useDefaultEffect'] = false;
|
||||
target.node.targetOff(this);
|
||||
target.node.on(Button.EventType.CLICK, function (this: SoundManager<E, M>) {
|
||||
this.playEffect({ name, volume, interval });
|
||||
}, this);
|
||||
} else {
|
||||
//@ts-ignore
|
||||
target.node['useDefaultEffect'] = true;
|
||||
target.node.targetOff(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放音效
|
||||
* @param name 音效
|
||||
* @param bundle Bundle名,默认为app-sound
|
||||
* @param loop 循环播放
|
||||
* @param volume 音量
|
||||
* @param interval 多少秒内不会重复播放
|
||||
*/
|
||||
public playEffect({ name, volume, loop, interval, onEnded, onPlay, onError }: playEffect<E>): void;
|
||||
public playEffect({ name, bundle, volume, loop, interval, onEnded, onPlay, onError }: playEffectWidthBundle): void;
|
||||
public playEffect({ name, bundle, volume = 1, loop = false, interval = 0, onEnded, onPlay, onError }) {
|
||||
if (!name) {
|
||||
onError && onError();
|
||||
return;
|
||||
}
|
||||
|
||||
const bundleName = bundle || BundleName;
|
||||
const soundName = `${bundleName}://${name}`;
|
||||
|
||||
// 静音不允许播放
|
||||
if (this.isEffectMute) {
|
||||
onError && onError();
|
||||
return;
|
||||
}
|
||||
// 正在播放中,不允许重复播放
|
||||
if (this.effectInterval[soundName] && Date.now() < this.effectInterval[soundName]) {
|
||||
onError && onError();
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载音乐
|
||||
this.load(name, bundleName, (audioClip) => {
|
||||
if (!isValid(this)) {
|
||||
onError && onError();
|
||||
return;
|
||||
}
|
||||
// 静音不允许播放
|
||||
if (this.isEffectMute) {
|
||||
onError && onError();
|
||||
return;
|
||||
}
|
||||
// 正在播放中,不允许重复播放
|
||||
if (this.effectInterval[soundName] && Date.now() < this.effectInterval[soundName]) {
|
||||
onError && onError();
|
||||
return;
|
||||
}
|
||||
if (!audioClip) {
|
||||
this.error(`playEffect ${name} 不存在或加载失败`);
|
||||
onError && onError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (interval > 0) {
|
||||
this.effectInterval[soundName] = Date.now() + interval * 1000;
|
||||
}
|
||||
|
||||
AudioEngine.inst.playEffect(audioClip, volume, loop, onPlay, onEnded);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放音效
|
||||
* @param name 音效
|
||||
* @param bundle Bundle名,默认为app-sound
|
||||
* @param loop 循环播放
|
||||
* @param volume 音量
|
||||
* @param interval 多少秒内不会重复播放
|
||||
* @returns 如果Promise返回值是null(非真),则播放失败
|
||||
*/
|
||||
public async playEffectAsync(params: playEffectAsync<E>): Promise<number>;
|
||||
public async playEffectAsync(params: playEffectWidthBundleAsync): Promise<number>;
|
||||
public async playEffectAsync(params: any): Promise<number> {
|
||||
return new Promise((resolve) => {
|
||||
this.playEffect({
|
||||
...params,
|
||||
onPlay: (audioID) => {
|
||||
resolve(audioID);
|
||||
},
|
||||
onError: () => {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停音效
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
public pauseEffect(id: number) {
|
||||
return AudioEngine.inst.pauseEffect(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停所有音效
|
||||
* @returns
|
||||
*/
|
||||
public pauseAllEffects() {
|
||||
return AudioEngine.inst.pauseAllEffects();
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复音效
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
public resumeEffect(id: number) {
|
||||
return AudioEngine.inst.resumeEffect(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复所有音效
|
||||
* @returns
|
||||
*/
|
||||
public resumeAllEffects() {
|
||||
return AudioEngine.inst.resumeAllEffects();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止音效
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
public stopEffect(id: number) {
|
||||
return AudioEngine.inst.stopEffect(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止所有音效
|
||||
* @returns
|
||||
*/
|
||||
public stopAllEffects() {
|
||||
return AudioEngine.inst.stopAllEffects();
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放音乐
|
||||
* @param volume 音量
|
||||
* @param bundle Bundle名,默认为app-sound
|
||||
* @param force 是否强制重新播放
|
||||
*/
|
||||
public playMusic(params: playMusic<M>): void;
|
||||
public playMusic(params: playMusicWidthBundle): void;
|
||||
public playMusic({ name, bundle, volume = 1, force = false, onPlay, onError }): void {
|
||||
if (!name) {
|
||||
onError && onError();
|
||||
return;
|
||||
}
|
||||
|
||||
const bundleName = bundle || BundleName;
|
||||
const soundName = `${bundleName}://${name}`;
|
||||
|
||||
// 该音乐正在播放中
|
||||
if (!force && this.playingMusic.id !== -1 && this.playingMusic.name === soundName) {
|
||||
AudioEngine.inst.setMusicVolume(volume);
|
||||
onPlay && onPlay();
|
||||
return;
|
||||
}
|
||||
|
||||
// 先停止当前音乐
|
||||
this.stopMusic();
|
||||
|
||||
// 播放操作uuid
|
||||
const uuid = this.createUUID();
|
||||
this.playingMusic.uuid = uuid;
|
||||
// 记录要播放音乐的名字
|
||||
this.playingMusic.name = soundName;
|
||||
// 记录要播放音乐的音量
|
||||
this.playingMusic.volume = volume;
|
||||
// 记录音乐状态
|
||||
this.playingMusic.playing = true;
|
||||
this.playingMusic.paused = false;
|
||||
|
||||
// 静音
|
||||
if (this.isMusicMute) {
|
||||
onPlay && onPlay();
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载音乐
|
||||
this.load(name, bundleName, (audioClip) => {
|
||||
if (!isValid(this)) {
|
||||
onError && onError();
|
||||
return;
|
||||
}
|
||||
// 不合法
|
||||
if (this.playingMusic.id !== -1) {
|
||||
onError && onError();
|
||||
return;
|
||||
}
|
||||
if (this.playingMusic.name !== soundName) {
|
||||
onError && onError();
|
||||
return;
|
||||
}
|
||||
if (this.playingMusic.uuid !== this.playingMusic.uuid) {
|
||||
onError && onError();
|
||||
return;
|
||||
}
|
||||
// 不存在
|
||||
if (!audioClip) {
|
||||
this.error(`playMusic ${name} 不存在或加载失败`);
|
||||
onError && onError();
|
||||
return;
|
||||
}
|
||||
// 静音
|
||||
if (this.isMusicMute) {
|
||||
onPlay && onPlay();
|
||||
return;
|
||||
}
|
||||
|
||||
this.playingMusic.id = AudioEngine.inst.playMusic(audioClip, volume, onPlay);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放音乐
|
||||
* @param volume 音量
|
||||
* @param bundle Bundle名,默认为app-sound
|
||||
* @param force 是否强制重新播放
|
||||
* @returns 如果Promise返回值是false,则播放失败
|
||||
*/
|
||||
public playMusicAsync(params: playMusicAsync<M>): Promise<boolean>;
|
||||
public playMusicAsync(params: playMusicWidthBundleAsync): Promise<boolean>;
|
||||
public playMusicAsync(params: any): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
this.playMusic({
|
||||
...params,
|
||||
onPlay: () => {
|
||||
resolve(true);
|
||||
},
|
||||
onError: () => {
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新播放音乐
|
||||
*/
|
||||
public replayMusic(onPlay?: Function) {
|
||||
if (!this.playingMusic.playing) return;
|
||||
if (!this.playingMusic.name) return;
|
||||
if (this.playingMusic.name.indexOf('://') > 0) {
|
||||
const [bundle, name] = this.playingMusic.name.split('://');
|
||||
this.playMusic({
|
||||
name,
|
||||
bundle,
|
||||
volume: this.playingMusic.volume,
|
||||
force: true,
|
||||
onPlay
|
||||
});
|
||||
} else {
|
||||
this.playMusic({
|
||||
name: this.playingMusic.name as any,
|
||||
volume: this.playingMusic.volume,
|
||||
force: true,
|
||||
onPlay
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停音乐
|
||||
*/
|
||||
public pauseMusic() {
|
||||
if (!this.playingMusic.playing) return false;
|
||||
this.playingMusic.paused = true;
|
||||
return AudioEngine.inst.pauseMusic();
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复音乐
|
||||
*/
|
||||
public resumeMusic() {
|
||||
if (!this.playingMusic.playing) return false;
|
||||
this.playingMusic.paused = false;
|
||||
return AudioEngine.inst.resumeMusic();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止音乐
|
||||
*/
|
||||
public stopMusic() {
|
||||
this.playingMusic.playing = false;
|
||||
this.playingMusic.paused = false;
|
||||
this.playingMusic.volume = 1;
|
||||
this.playingMusic.name = '';
|
||||
this.playingMusic.uuid = '';
|
||||
this.playingMusic.id = -1;
|
||||
return AudioEngine.inst.stopMusic();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置音乐静音
|
||||
* @param mute 是否静音
|
||||
* @param isCache 静音状态是否写入缓存(通过localstorage)
|
||||
*/
|
||||
public setMusicMute(mute: boolean, isCache = false) {
|
||||
isCache && storage.set(this.musicMuteCacheKey, mute);
|
||||
AudioEngine.inst.setMusicMute(mute);
|
||||
if (!mute && this.playingMusic.name) {
|
||||
if (this.playingMusic.name.indexOf('://') > 0) {
|
||||
const [bundle, name] = this.playingMusic.name.split('://');
|
||||
this.playMusic({
|
||||
name,
|
||||
bundle,
|
||||
volume: this.playingMusic.volume,
|
||||
|
||||
});
|
||||
} else {
|
||||
this.playMusic({
|
||||
name: this.playingMusic.name as any,
|
||||
volume: this.playingMusic.volume
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 音乐是否正在播放
|
||||
*/
|
||||
get isMusicPlaying() {
|
||||
return this.playingMusic.playing;
|
||||
}
|
||||
|
||||
/**
|
||||
* 音乐是否暂停
|
||||
*/
|
||||
get isMusicPaused() {
|
||||
return this.playingMusic.paused;
|
||||
}
|
||||
|
||||
/**
|
||||
* 音乐是否静音
|
||||
*/
|
||||
public get isMusicMute() {
|
||||
return AudioEngine.inst.getMusicMute();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置音效静音
|
||||
* @param mute 是否静音
|
||||
* @param isCache 静音状态是否写入缓存(通过localstorage)
|
||||
*/
|
||||
public setEffectMute(mute: boolean, isCache = false) {
|
||||
AudioEngine.inst.setGlobalEffectsMute(mute);
|
||||
isCache && storage.set(this.effectMuteCacheKey, mute);
|
||||
}
|
||||
|
||||
/**
|
||||
* 音效是否静音
|
||||
*/
|
||||
public get isEffectMute() {
|
||||
return AudioEngine.inst.getGlobalEffectsMute();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置音乐音量倍率
|
||||
* @param scale
|
||||
* @param isCache 音量倍率是否写入缓存(通过localstorage)
|
||||
*/
|
||||
public setMusicVolumeScale(scale: number, isCache = false) {
|
||||
AudioEngine.inst.setMusicVolumeScale(scale);
|
||||
isCache && storage.set(this.musicVolumeScaleCacheKey, scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* 音乐音量倍率
|
||||
*/
|
||||
public get musicVolumeScale() {
|
||||
return AudioEngine.inst.getMusicVolumeScale();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置音效音量倍率
|
||||
* @param scale
|
||||
* @param isCache 音量倍率是否写入缓存(通过localstorage)
|
||||
*/
|
||||
public setEffectVolumeScale(scale: number, isCache = false) {
|
||||
AudioEngine.inst.setGlobalEffectsVolumeScale(scale);
|
||||
isCache && storage.set(this.effectVolumeScaleCacheKey, scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* 音效音量倍率
|
||||
*/
|
||||
public get effectVolumeScale() {
|
||||
return AudioEngine.inst.getGlobalEffectsVolumeScale();
|
||||
}
|
||||
}
|
||||
9
extensions/app/assets/manager/sound/SoundManager.ts.meta
Normal file
9
extensions/app/assets/manager/sound/SoundManager.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "580021da-d9a7-4e59-bb76-2c3afcae6053",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
12
extensions/app/assets/manager/timer.meta
Normal file
12
extensions/app/assets/manager/timer.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "ea45b442-c58f-49d1-9b11-edc7bc719a73",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
276
extensions/app/assets/manager/timer/TimerManager.ts
Normal file
276
extensions/app/assets/manager/timer/TimerManager.ts
Normal file
@@ -0,0 +1,276 @@
|
||||
import { _decorator } from 'cc';
|
||||
import BaseManager from '../../base/BaseManager';
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
type ICallback = (...args: any[]) => any;
|
||||
|
||||
class DailyTimer {
|
||||
// 获取当前时间相对于当日零点的毫秒数
|
||||
private static getDayTimeMs(date: Date): number {
|
||||
return (
|
||||
date.getHours() * 3600 +
|
||||
date.getMinutes() * 60 +
|
||||
date.getSeconds()
|
||||
) * 1000 + date.getMilliseconds();
|
||||
}
|
||||
|
||||
// 静态方法解析时间为毫秒数
|
||||
private static parseTimeToMs(h: number, m: number, s: number): number {
|
||||
return ((h * 3600 + m * 60 + s) * 1000) % 86400000;
|
||||
}
|
||||
|
||||
private readonly startMs: number; // 起始时间毫秒数(相对于当日零点)
|
||||
private readonly endMs: number; // 结束时间毫秒数
|
||||
|
||||
// 用于检查当前时间是否可触发回调
|
||||
private checkDay = 0;
|
||||
|
||||
constructor(
|
||||
time: string,
|
||||
public readonly callback: ICallback,
|
||||
public readonly target?: unknown,
|
||||
public readonly once: boolean = false
|
||||
) {
|
||||
// 使用解构赋值提高可读性
|
||||
const [startSegment, endSegment = startSegment] = time.split('-');
|
||||
|
||||
// 开始时间
|
||||
const [startH = 0, startM = 0, startS = 0] = startSegment.split(':').map(
|
||||
part => Math.max(0, parseInt(part, 10) || 0)
|
||||
);
|
||||
this.startMs = DailyTimer.parseTimeToMs(startH, startM, startS);
|
||||
|
||||
// 结束时间
|
||||
const [endH = 0, endM = 0, endS = 0] = endSegment.split(':').map(
|
||||
part => Math.max(0, parseInt(part, 10) || 0)
|
||||
);
|
||||
this.endMs = DailyTimer.parseTimeToMs(endH, endM, endS);
|
||||
// 结束时间与开始时间不能相同
|
||||
if (this.endMs === this.startMs) {
|
||||
if (startM === 0 && startS === 0) {
|
||||
this.endMs = DailyTimer.parseTimeToMs(startH + 1, startM, startS);
|
||||
} else if (startS === 0) {
|
||||
this.endMs = DailyTimer.parseTimeToMs(startH, startM + 1, startS);
|
||||
} else {
|
||||
this.endMs = DailyTimer.parseTimeToMs(startH, startM, startS + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前时间是否在时间范围内
|
||||
private isInRange(now: Date): boolean {
|
||||
const currentMs = DailyTimer.getDayTimeMs(now);
|
||||
|
||||
// 处理跨天时间段(如 23:00-01:00)
|
||||
return this.startMs <= this.endMs
|
||||
? currentMs >= this.startMs && currentMs < this.endMs
|
||||
: currentMs >= this.startMs || currentMs < this.endMs;
|
||||
}
|
||||
|
||||
update(now: Date): boolean {
|
||||
const dateDay = now.getDay();
|
||||
|
||||
if (this.checkDay === dateDay) return false;
|
||||
if (!this.isInRange(now)) return false;
|
||||
|
||||
this.checkDay = dateDay;
|
||||
this.callback.call(this.target);
|
||||
|
||||
return this.once;
|
||||
}
|
||||
}
|
||||
|
||||
class IntervalTimer {
|
||||
private elapsed: number = 0;
|
||||
|
||||
constructor(
|
||||
public readonly interval: number,
|
||||
public readonly callback: ICallback,
|
||||
public readonly target?: unknown,
|
||||
public readonly once: boolean = false
|
||||
) { }
|
||||
|
||||
update(dt: number): boolean {
|
||||
this.elapsed += dt;
|
||||
let completed = false;
|
||||
|
||||
// 处理可能多次触发的情况(当dt > interval时)
|
||||
while (this.elapsed >= this.interval) {
|
||||
this.callback.call(this.target);
|
||||
this.elapsed -= this.interval;
|
||||
|
||||
if (this.once) {
|
||||
completed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return completed;
|
||||
}
|
||||
}
|
||||
|
||||
class Timer {
|
||||
static update(timer: Timer, dt: number) {
|
||||
return timer.update(dt);
|
||||
}
|
||||
|
||||
private intervalTimer: IntervalTimer[] = [];
|
||||
|
||||
/**
|
||||
* 注册定时器
|
||||
* @param interval
|
||||
* @param callback
|
||||
* @param target
|
||||
* @param once
|
||||
*/
|
||||
register(
|
||||
interval: number,
|
||||
callback: ICallback,
|
||||
target?: unknown,
|
||||
once?: boolean
|
||||
): void {
|
||||
const timer = new IntervalTimer(interval, callback, target, once || false);
|
||||
this.intervalTimer.push(timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消定时器
|
||||
* @param callback
|
||||
* @param target
|
||||
*/
|
||||
unregister(callback: ICallback, target?: unknown): void {
|
||||
if (typeof target === 'undefined') {
|
||||
this.intervalTimer = this.intervalTimer.filter(
|
||||
timer => timer.callback !== callback
|
||||
);
|
||||
} else {
|
||||
this.intervalTimer = this.intervalTimer.filter(
|
||||
timer => !(timer.callback === callback && timer.target === target)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消所有定时器
|
||||
*/
|
||||
unregisterAll() {
|
||||
this.intervalTimer = [];
|
||||
}
|
||||
|
||||
private dailyTimers: DailyTimer[] = [];
|
||||
|
||||
/**
|
||||
* 注册每日触发器
|
||||
* @param time 24小时制,精确到秒
|
||||
*
|
||||
* @example
|
||||
* registerDailyTrigger('16', ...) 等同于 registerDailyTrigger('16-17', ...)
|
||||
* registerDailyTrigger('8-9:00', ...) 等同于 registerDailyTrigger('8', ...)
|
||||
* registerDailyTrigger('8:00:01-24', ...)
|
||||
*/
|
||||
registerDailyTrigger(
|
||||
time: string,
|
||||
callback: ICallback,
|
||||
target?: unknown,
|
||||
once?: boolean
|
||||
) {
|
||||
const timer = new DailyTimer(time, callback, target, once || false);
|
||||
this.dailyTimers.push(timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消每日触发器
|
||||
*/
|
||||
unregisterDailyTrigger(callback: ICallback, target?: unknown) {
|
||||
if (typeof target === 'undefined') {
|
||||
this.dailyTimers = this.dailyTimers.filter(
|
||||
timer => timer.callback !== callback
|
||||
);
|
||||
} else {
|
||||
this.dailyTimers = this.dailyTimers.filter(
|
||||
timer => !(timer.callback === callback && timer.target === target)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消所有每日触发器
|
||||
*/
|
||||
unregisterAllDailyTrigger() {
|
||||
this.dailyTimers = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有定时器和触发器
|
||||
*/
|
||||
clear() {
|
||||
this.intervalTimer = [];
|
||||
this.dailyTimers = [];
|
||||
}
|
||||
|
||||
protected update(dt: number): void {
|
||||
for (let index = 0; index < this.intervalTimer.length; index++) {
|
||||
const timer = this.intervalTimer[index];
|
||||
if (timer.update(dt)) {
|
||||
this.intervalTimer.splice(index, 1);
|
||||
index--;
|
||||
}
|
||||
}
|
||||
|
||||
const date = new Date();
|
||||
for (let index = 0; index < this.dailyTimers.length; index++) {
|
||||
const timer = this.dailyTimers[index];
|
||||
if (timer.update(date)) {
|
||||
this.dailyTimers.splice(index, 1);
|
||||
index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ccclass('TimerManager')
|
||||
export default class TimerManager extends BaseManager {
|
||||
private timers: Map<string | number | Symbol, Timer> = new Map();
|
||||
|
||||
/**
|
||||
* 清除所有定时器
|
||||
*/
|
||||
clear() {
|
||||
this.timers.forEach((timer) => {
|
||||
timer.clear();
|
||||
});
|
||||
this.timers.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除定时器
|
||||
* @param key 定时器key
|
||||
*/
|
||||
delete(key: string | number | Symbol) {
|
||||
const timer = this.timers.get(key);
|
||||
if (!timer) return;
|
||||
|
||||
this.timers.delete(key);
|
||||
timer.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取定时器
|
||||
* @param key 定时器key
|
||||
*/
|
||||
get(key: string | number | Symbol): Timer {
|
||||
if (this.timers.has(key)) {
|
||||
return this.timers.get(key);
|
||||
}
|
||||
|
||||
const timer = new Timer();
|
||||
this.timers.set(key, timer);
|
||||
return timer;
|
||||
}
|
||||
|
||||
protected update(dt: number): void {
|
||||
this.timers.forEach((timer) => {
|
||||
Timer.update(timer, dt);
|
||||
});
|
||||
}
|
||||
}
|
||||
9
extensions/app/assets/manager/timer/TimerManager.ts.meta
Normal file
9
extensions/app/assets/manager/timer/TimerManager.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "b5636f8d-3516-4510-aaba-74f9202b8a9f",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
12
extensions/app/assets/manager/ui.meta
Normal file
12
extensions/app/assets/manager/ui.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "714739c4-f4c0-4313-bc98-2d350df6b208",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
BIN
extensions/app/assets/manager/ui/.DS_Store
vendored
Normal file
BIN
extensions/app/assets/manager/ui/.DS_Store
vendored
Normal file
Binary file not shown.
1617
extensions/app/assets/manager/ui/UIManager.ts
Normal file
1617
extensions/app/assets/manager/ui/UIManager.ts
Normal file
@@ -0,0 +1,1617 @@
|
||||
import { Asset, AssetManager, Camera, Canvas, Component, Event, Layers, Node, Prefab, RenderTexture, ResolutionPolicy, Scene, SceneAsset, Settings, UITransform, Widget, _decorator, director, instantiate, isValid, js, screen, settings, size, view } from 'cc';
|
||||
import { DEV } from 'cc/env';
|
||||
import { IMiniViewName, IViewName } from '../../../../../assets/app-builtin/app-admin/executor';
|
||||
import Core from '../../Core';
|
||||
import BaseManager from '../../base/BaseManager';
|
||||
import BaseView, { IHideParamOnHide, IShade, IShowParamAttr, IShowParamOnHide, IShowParamOnShow, IViewType, ViewType } from '../../base/BaseView';
|
||||
import UIMgrLoading from './comp/UIMgrLoading';
|
||||
import UIMgrShade from './comp/UIMgrShade';
|
||||
import UIMgrToast from './comp/UIMgrToast';
|
||||
import UIMgrZOrder from './comp/UIMgrZOrder';
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
interface IShowParams<T, IShow = any, IShowReturn = any, IHideReturn = any> {
|
||||
/**UI名 */
|
||||
name: T,
|
||||
/**
|
||||
* 数据
|
||||
* - 被onShow接收
|
||||
*/
|
||||
data?: IShow,
|
||||
/**
|
||||
* 是否将UI显示在最上
|
||||
* - 默认true
|
||||
*/
|
||||
top?: boolean,
|
||||
/**
|
||||
* 队列模式,一个UI关闭后,是否展示下一个UI
|
||||
* - join: 排队
|
||||
* - jump: 插队(到首位)
|
||||
*/
|
||||
queue?: 'join' | 'jump',
|
||||
/**静默 默认false(不显示加载loading,也不屏蔽触摸) */
|
||||
silent?: boolean,
|
||||
/**UI触发onShow后 */
|
||||
onShow?: IShowParamOnShow<IShowReturn>,
|
||||
/**UI触发onHide后 */
|
||||
onHide?: IShowParamOnHide<IHideReturn>,
|
||||
/**当code的值为ErrorCode.LogicError时,如果返回true,则自动重试 */
|
||||
onError?: (result: string, code: ErrorCode) => true | void,
|
||||
/**
|
||||
* @private
|
||||
* @deprecated
|
||||
*/
|
||||
attr?: IShowParamAttr,
|
||||
}
|
||||
|
||||
interface IShowAsyncParams<T, IShow = any, IShowReturn = any> {
|
||||
/**UI名 */
|
||||
name: T,
|
||||
/**
|
||||
* 数据
|
||||
* - 被onShow接收
|
||||
*/
|
||||
data?: IShow,
|
||||
/**
|
||||
* 是否将UI显示在最上
|
||||
* - 默认true
|
||||
*/
|
||||
top?: boolean,
|
||||
/**
|
||||
* 队列模式,一个UI关闭后,是否展示下一个UI
|
||||
* - join: 排队
|
||||
* - jump: 插队(到首位)
|
||||
*/
|
||||
queue?: 'join' | 'jump',
|
||||
/**静默 默认false(不显示加载loading,也不屏蔽触摸) */
|
||||
silent?: boolean,
|
||||
/**UI触发onShow后 */
|
||||
onShow?: IShowParamOnShow<IShowReturn>,
|
||||
/**当code的值为ErrorCode.LogicError时,如果返回true,则自动重试 */
|
||||
onError?: (result: string, code: ErrorCode) => true | void,
|
||||
/**
|
||||
* @private
|
||||
* @deprecated
|
||||
*/
|
||||
attr?: IShowParamAttr,
|
||||
}
|
||||
|
||||
interface IHideParams<T, IHide = any, IHideReturn = any> {
|
||||
name: T,
|
||||
data?: IHide,
|
||||
onHide?: IHideParamOnHide<IHideReturn>
|
||||
}
|
||||
|
||||
const UIScene = 'UIScene';
|
||||
const UserInterfacePath = 'UserInterface';
|
||||
const ViewTypes = [ViewType.Page, ViewType.Paper, ViewType.Pop, ViewType.Top];
|
||||
|
||||
const BlockEvents = [
|
||||
Node.EventType.TOUCH_START, Node.EventType.TOUCH_MOVE, Node.EventType.TOUCH_END, Node.EventType.TOUCH_CANCEL,
|
||||
Node.EventType.MOUSE_DOWN, Node.EventType.MOUSE_MOVE, Node.EventType.MOUSE_UP,
|
||||
Node.EventType.MOUSE_ENTER, Node.EventType.MOUSE_LEAVE, Node.EventType.MOUSE_WHEEL
|
||||
];
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
enum ErrorCode {
|
||||
/**加载失败 */
|
||||
LoadError,
|
||||
/**beforeShow返回错误 */
|
||||
LogicError,
|
||||
/**UI无效(UI的isViewValid返回false) */
|
||||
InvalidError,
|
||||
}
|
||||
|
||||
/**
|
||||
* 界面名字枚举
|
||||
*/
|
||||
const ViewName: { [key in IViewName]: key } = new Proxy({} as any, {
|
||||
get: function (target, key) {
|
||||
if (target[key]) return target[key];
|
||||
target[key] = key;
|
||||
return key;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 子界面名字枚举
|
||||
*/
|
||||
const MiniViewName: { [key in IMiniViewName]: key } = new Proxy({} as any, {
|
||||
get: function (target, key) {
|
||||
if (target[key]) return target[key];
|
||||
target[key] = key;
|
||||
return key;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 将驼峰命名转成串式命名
|
||||
* @param str 驼峰字符串
|
||||
* @returns
|
||||
*/
|
||||
function stringCaseNegate(str: string) {
|
||||
return str.replace(/[A-Z]/g, (searchStr, startIndex) => {
|
||||
if (startIndex === 0) {
|
||||
return searchStr.toLowerCase();
|
||||
} else {
|
||||
return '-' + searchStr.toLowerCase();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ccclass('UIManager')
|
||||
export default class UIManager<UIName extends string, MiniName extends string> extends BaseManager {
|
||||
/**静态设置 */
|
||||
static setting: {
|
||||
/**预加载列表,会在UI初始化阶段进行 */
|
||||
preload?: (IViewName | IMiniViewName | Array<IViewName | IMiniViewName>)[],
|
||||
/**默认UI,框架初始化完毕后会自动加载 */
|
||||
defaultUI?: IViewName,
|
||||
/**给默认UI传递的数据 */
|
||||
defaultData?: any,
|
||||
/**弹窗背景遮罩的参数 */
|
||||
shade?: IShade,
|
||||
/**
|
||||
* 是否自动适配分辨率策略
|
||||
* - 开启后会弃用当前的适配策略,并根据实际设备分辨率与设计分辨率的比值,计算出新的适配策略(宽适配或高适配),保证游戏区域不会被裁减只会扩边
|
||||
* - 当实际设备分辨率「高/宽」>= 设计分辨率「高/宽」时,为宽适配
|
||||
* - 当实际设备分辨率「高/宽」< 设计分辨率「高/宽」时,为高适配
|
||||
*/
|
||||
autoFit?: boolean,
|
||||
} = {};
|
||||
|
||||
/**错误码 */
|
||||
static ErrorCode = ErrorCode;
|
||||
|
||||
/**界面名字枚举 */
|
||||
static ViewName = ViewName;
|
||||
|
||||
/**子界面名字枚举 */
|
||||
static MiniViewName = MiniViewName;
|
||||
|
||||
@property({
|
||||
type: Prefab,
|
||||
tooltip: '位置: app://manager/ui/prefab/UIMgrLoading'
|
||||
})
|
||||
private loadingPre: Prefab = null;
|
||||
|
||||
@property({
|
||||
type: Prefab,
|
||||
tooltip: '位置: app://manager/ui/prefab/UIMgrShade'
|
||||
})
|
||||
private shadePre: Prefab = null;
|
||||
|
||||
@property({
|
||||
type: Prefab,
|
||||
tooltip: '位置: app://manager/ui/prefab/UIMgrToast'
|
||||
})
|
||||
private toastPre: Prefab = null;
|
||||
|
||||
// UI根节点
|
||||
private UserInterface: Node = null;
|
||||
|
||||
// 加载和遮罩节点
|
||||
private loading: Node = null;
|
||||
private shade: Node = null;
|
||||
private toast: Node = null;
|
||||
|
||||
private defaultUI: UIName = null;
|
||||
private defaultData: string = '';
|
||||
|
||||
private currScene: string = '';
|
||||
private currPage: BaseView = null;
|
||||
private currFocus: BaseView = null;
|
||||
|
||||
// 预制体缓存
|
||||
private prefabCache: { [name in string]: Prefab } = {};
|
||||
private sceneCache: { [name in string]: SceneAsset } = {};
|
||||
|
||||
// 全局触摸有效
|
||||
private touchEnabled: boolean = true;
|
||||
|
||||
// 记录触摸屏蔽
|
||||
private touchMaskMap = new Map<string, boolean>();
|
||||
// 记录展示加载
|
||||
private showLoadingMap = new Map<string, boolean>();
|
||||
|
||||
// 记录正在加载中的有效的ui
|
||||
private uiLoadingMap: Map<UIName, string[]> = new Map();
|
||||
// 记录正在展示中的有效的ui
|
||||
private uiShowingMap: Map<BaseView, UIName> = new Map();
|
||||
|
||||
private showQueue: IShowParams<UIName>[] = [];
|
||||
|
||||
/**UI根节点 */
|
||||
public get root() {
|
||||
return this.node.parent.parent;
|
||||
}
|
||||
|
||||
/**相机 */
|
||||
public get camera() {
|
||||
return this.canvas.cameraComponent;
|
||||
}
|
||||
|
||||
/**画布*/
|
||||
public get canvas() {
|
||||
return this.root.getComponent(Canvas);
|
||||
}
|
||||
|
||||
protected init(finish: Function) {
|
||||
const setting = UIManager.setting;
|
||||
|
||||
this.defaultUI = setting.defaultUI as UIName;
|
||||
this.defaultData = setting.defaultData;
|
||||
|
||||
super.init(finish);
|
||||
|
||||
// 预加载,符合AnyTask规则
|
||||
if (setting.preload?.length) {
|
||||
const task = Core.inst.lib.task.createAny();
|
||||
setting.preload.forEach((preload) => {
|
||||
if (preload instanceof Array) {
|
||||
task.add(preload.map(name => {
|
||||
return next => this.preload(name as any, next);
|
||||
}));
|
||||
} else {
|
||||
task.add(next => this.preload(preload as any, next));
|
||||
}
|
||||
});
|
||||
task.start();
|
||||
}
|
||||
}
|
||||
|
||||
protected onLoad() {
|
||||
this.UserInterface = this.root.getChildByName(UserInterfacePath);
|
||||
|
||||
this.root.getComponentsInChildren(Camera).forEach(camera => {
|
||||
// 避免camera.priority<0的情况,否则会造成渲染异常
|
||||
if (camera.priority < 0) camera.priority = 0;
|
||||
// 避免camera.far<=camera.near的情况,否则会造成渲染异常
|
||||
if (camera.far <= camera.near) camera.far = camera.near + 1;
|
||||
});
|
||||
director.addPersistRootNode(this.root);
|
||||
|
||||
this.createTypeRoot();
|
||||
|
||||
this.shade = instantiate(this.shadePre);
|
||||
this.shade.parent = this.UserInterface;
|
||||
this.shade.active = false;
|
||||
this.shade.getComponent(Widget).target = this.root;
|
||||
|
||||
this.loading = instantiate(this.loadingPre);
|
||||
this.loading.parent = this.node;
|
||||
this.loading.active = false;
|
||||
|
||||
// toast是后面加的,需要做容错
|
||||
if (this.toastPre) {
|
||||
this.toast = instantiate(this.toastPre);
|
||||
this.toast.parent = this.node;
|
||||
}
|
||||
|
||||
// 自动适配分辨率策略
|
||||
if (UIManager.setting.autoFit) {
|
||||
const designResolution = settings.querySettings(Settings.Category.SCREEN, 'designResolution') as { width: number, height: number, policy: number };
|
||||
const windowSize = size(screen.windowSize);
|
||||
let resolutionPolicy = designResolution.policy;
|
||||
const autoFitResolutionPolicy = function () {
|
||||
if (windowSize.width / windowSize.height > designResolution.width / designResolution.height) {
|
||||
if (resolutionPolicy === ResolutionPolicy.FIXED_HEIGHT) return;
|
||||
view.setResolutionPolicy(ResolutionPolicy.FIXED_HEIGHT);
|
||||
resolutionPolicy = ResolutionPolicy.FIXED_HEIGHT;
|
||||
} else {
|
||||
if (resolutionPolicy === ResolutionPolicy.FIXED_WIDTH) return;
|
||||
view.setResolutionPolicy(ResolutionPolicy.FIXED_WIDTH);
|
||||
resolutionPolicy = ResolutionPolicy.FIXED_WIDTH;
|
||||
}
|
||||
};
|
||||
autoFitResolutionPolicy();
|
||||
this.schedule(() => {
|
||||
if (windowSize.equals(screen.windowSize)) return;
|
||||
windowSize.set(screen.windowSize);
|
||||
autoFitResolutionPolicy();
|
||||
}, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
private createTypeRoot() {
|
||||
ViewTypes.forEach((type) => {
|
||||
const d2 = new Node(type);
|
||||
d2.layer = Layers.Enum.UI_2D;
|
||||
d2.addComponent(UIMgrZOrder);
|
||||
d2.parent = this.UserInterface;
|
||||
d2.addComponent(UITransform);
|
||||
const widget = d2.addComponent(Widget);
|
||||
widget.isAlignBottom = true;
|
||||
widget.isAlignLeft = true;
|
||||
widget.isAlignRight = true;
|
||||
widget.isAlignTop = true;
|
||||
widget.top = 0;
|
||||
widget.left = 0;
|
||||
widget.right = 0;
|
||||
widget.bottom = 0;
|
||||
widget.alignMode = Widget.AlignMode.ON_WINDOW_RESIZE;
|
||||
|
||||
if (DEV) {
|
||||
d2.on(Node.EventType.CHILD_ADDED, (child: Node) => {
|
||||
if (!child) return;
|
||||
if (child === this.shade) return;
|
||||
if (this.getBaseView(child)) return;
|
||||
this.warn(`${UserInterfacePath}/${type}下非必要请不要添加非UI节点:`, child?.name);
|
||||
}, this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private addTouchMaskListener() {
|
||||
if (!this.touchEnabled) return;
|
||||
if (this.touchMaskMap.size > 0) return;
|
||||
|
||||
for (let i = 0; i < BlockEvents.length; i++) {
|
||||
this.root.on(BlockEvents[i], this.stopPropagation, this, true);
|
||||
}
|
||||
}
|
||||
|
||||
private removeTouchMaskListener() {
|
||||
if (!this.touchEnabled) return;
|
||||
if (this.touchMaskMap.size > 0) return;
|
||||
|
||||
for (let i = 0; i < BlockEvents.length; i++) {
|
||||
this.root.off(BlockEvents[i], this.stopPropagation, this, true);
|
||||
}
|
||||
}
|
||||
|
||||
private stopPropagation(event: Event) {
|
||||
if (!this.touchEnabled || this.touchMaskMap.size > 0) {
|
||||
event.propagationStopped = true;
|
||||
if (event.type !== Node.EventType.MOUSE_MOVE) {
|
||||
this.log('屏蔽触摸');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个节点上的BaseView组件, 获取不到返回null
|
||||
*/
|
||||
private getBaseView(node: Node): BaseView {
|
||||
if (!node) return null;
|
||||
return node.components.find(component => component instanceof BaseView) as BaseView;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在所有父节点中找到一个最近的view组件
|
||||
* @param target
|
||||
* @returns
|
||||
*/
|
||||
private getViewInParents(target: Node) {
|
||||
let node = target;
|
||||
let com: BaseView = null;
|
||||
|
||||
while (node.parent && !(node.parent instanceof Scene)) {
|
||||
com = this.getBaseView(node.parent);
|
||||
if (!com) {
|
||||
node = node.parent;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return com;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在子节点中找到一个最近的view组件
|
||||
* @param target
|
||||
* @returns
|
||||
*/
|
||||
private getViewInChildren(target: Node) {
|
||||
for (let index = 0; index < target.children.length; index++) {
|
||||
const node = target.children[index];
|
||||
const com = this.getBaseView(node);
|
||||
if (com) return com;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据UI名字获取它的脚本类
|
||||
*/
|
||||
private getUIClass(name: string): typeof BaseView {
|
||||
return js.getClassByName(name) as (typeof BaseView);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据UI名字获取UI路径
|
||||
* @param name ui名字
|
||||
* @returns
|
||||
*/
|
||||
private getUIPath(name: string) {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取前缀
|
||||
* @param name ui名字
|
||||
*/
|
||||
private getUIPrefix(name: string): ViewType {
|
||||
for (let index = 0; index < ViewTypes.length; index++) {
|
||||
const typeName = ViewTypes[index];
|
||||
if (name.indexOf(typeName) === 0) {
|
||||
return typeName;
|
||||
}
|
||||
}
|
||||
this.error('getUIPrefix', `${name}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据UI名字查询父节点
|
||||
* @param name ui名字
|
||||
*/
|
||||
private getUIParent(name: string): Node {
|
||||
if (this.currScene === name) {
|
||||
return director.getScene();
|
||||
}
|
||||
|
||||
const prefix = this.getUIPrefix(name);
|
||||
for (let index = 0; index < ViewTypes.length; index++) {
|
||||
const viewType = ViewTypes[index];
|
||||
if (viewType === prefix) {
|
||||
return this.UserInterface.getChildByName(viewType);
|
||||
}
|
||||
}
|
||||
|
||||
this.error('getUIParent', `找不到${name}对应的Parent`);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据UI名字获取场景内的节点
|
||||
* @param name ui名字
|
||||
*/
|
||||
private getUIInScene(name: string): Node;
|
||||
private getUIInScene(name: string, multiple: false): Node;
|
||||
private getUIInScene(name: string, multiple: true): Node[];
|
||||
private getUIInScene(name: string, multiple = false) {
|
||||
const parent = this.getUIParent(name);
|
||||
|
||||
if (multiple) {
|
||||
const result = parent.children.filter(node => node.name === name);
|
||||
if (result.length) return result.filter(node => isValid(node, true));
|
||||
} else {
|
||||
const result = parent.children.find(node => node.name === name);
|
||||
if (result) return isValid(result, true) ? result : null;
|
||||
}
|
||||
|
||||
return multiple ? [] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据UI名字获取展示中的节点
|
||||
* @param name ui名字
|
||||
*/
|
||||
private getUIInShowing(name: string): Node;
|
||||
private getUIInShowing(name: string, multiple: false): Node;
|
||||
private getUIInShowing(name: string, multiple: true): Node[];
|
||||
private getUIInShowing(name: string, multiple = false) {
|
||||
if (multiple) {
|
||||
const result: Node[] = [];
|
||||
this.uiShowingMap.forEach((_name, com) => {
|
||||
if (_name === name) result.push(com.node);
|
||||
});
|
||||
return result;
|
||||
} else {
|
||||
let result: Node = null;
|
||||
this.uiShowingMap.forEach((_name, com) => {
|
||||
if (!result && _name === name) result = com.node;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取UI骨架Bundle名字
|
||||
* @deprecated 将会移除,请改为其它方案
|
||||
*/
|
||||
public getNativeBundleName(uiName: UIName | MiniName) {
|
||||
// 兼容旧版本
|
||||
const oldBundleName = `app-view_${uiName}`;
|
||||
const projectBundles = settings.querySettings(Settings.Category.ASSETS, 'projectBundles') as string[];
|
||||
if (projectBundles && projectBundles.indexOf(oldBundleName) >= 0) {
|
||||
return oldBundleName;
|
||||
}
|
||||
|
||||
return stringCaseNegate(uiName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取UI资源Bundle名字
|
||||
* @deprecated 将会移除,请改为其它方案
|
||||
*/
|
||||
public getResBundleName(uiName: UIName | MiniName) {
|
||||
// 兼容旧版本
|
||||
const oldBundleName = `app-view_${uiName}_Res`;
|
||||
const projectBundles = settings.querySettings(Settings.Category.ASSETS, 'projectBundles') as string[];
|
||||
if (projectBundles && projectBundles.indexOf(oldBundleName) >= 0) {
|
||||
return oldBundleName;
|
||||
}
|
||||
|
||||
return `${stringCaseNegate(uiName)}-res`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化Bundle
|
||||
*/
|
||||
private initBundle(name: UIName | MiniName, onFinish: (result: [AssetManager.Bundle, AssetManager.Bundle]) => any) {
|
||||
Core.inst.lib.task.createASync<[AssetManager.Bundle, AssetManager.Bundle]>()
|
||||
.add((next) => {
|
||||
Core.inst.manager.loader.loadBundle({
|
||||
bundle: this.getNativeBundleName(name),
|
||||
onComplete: next
|
||||
});
|
||||
})
|
||||
.add((next) => {
|
||||
Core.inst.manager.loader.loadBundle({
|
||||
bundle: this.getResBundleName(name),
|
||||
onComplete: next
|
||||
});
|
||||
})
|
||||
.start(onFinish);
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装UI
|
||||
*/
|
||||
private installUI(name: UIName | MiniName, complete?: (result: Prefab | SceneAsset | null) => any, progress?: (finish: number, total: number, item: AssetManager.RequestItem) => void) {
|
||||
if (this.sceneCache[name]) {
|
||||
complete && setTimeout(() => {
|
||||
if (!isValid(this)) return;
|
||||
complete(this.sceneCache[name]);
|
||||
});
|
||||
return;
|
||||
} else if (this.prefabCache[name]) {
|
||||
complete && setTimeout(() => {
|
||||
if (!isValid(this)) return;
|
||||
complete(this.prefabCache[name]);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const task = Core.inst.lib.task.createSync<[[AssetManager.Bundle, AssetManager.Bundle], Prefab | SceneAsset]>()
|
||||
.add(next => {
|
||||
this.initBundle(name, next);
|
||||
})
|
||||
.add((next) => {
|
||||
// 失败
|
||||
const uiBundles = task.results[0];
|
||||
if (!uiBundles || !uiBundles[0] || !uiBundles[1]) return next(null);
|
||||
|
||||
const isScene = uiBundles[0].getSceneInfo(name);
|
||||
Core.inst.manager.loader.load({
|
||||
bundle: this.getNativeBundleName(name),
|
||||
path: this.getUIPath(name),
|
||||
type: isScene ? SceneAsset : Prefab,
|
||||
onProgress: progress,
|
||||
onComplete: next
|
||||
});
|
||||
})
|
||||
.start((results) => {
|
||||
if (!isValid(this)) return;
|
||||
// 验证缓存
|
||||
const cache = this.sceneCache[name] || this.prefabCache[name];
|
||||
if (cache) {
|
||||
return complete && complete(cache);
|
||||
}
|
||||
// 验证有效
|
||||
const asset = results[1];
|
||||
if (!asset) {
|
||||
return complete && complete(null);
|
||||
}
|
||||
// 添加引用计数
|
||||
asset.addRef();
|
||||
// 添加缓存
|
||||
if (asset instanceof Prefab) {
|
||||
this.prefabCache[name] = asset;
|
||||
} else {
|
||||
this.sceneCache[name] = asset;
|
||||
}
|
||||
this.log(`加载: ${name}`);
|
||||
return complete && complete(asset);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载UI
|
||||
*/
|
||||
private uninstallUI(name: UIName | MiniName) {
|
||||
if (this.sceneCache[name]) {
|
||||
// 释放引用计数
|
||||
this.sceneCache[name].decRef();
|
||||
// 删除缓存
|
||||
delete this.sceneCache[name];
|
||||
} else if (this.prefabCache[name]) {
|
||||
// 释放引用计数
|
||||
this.prefabCache[name].decRef();
|
||||
// 删除缓存
|
||||
delete this.prefabCache[name];
|
||||
}
|
||||
|
||||
const resBundle = this.getResBundleName(name);
|
||||
const naBundle = this.getNativeBundleName(name);
|
||||
Core.inst.manager.loader.releaseAll(resBundle);
|
||||
Core.inst.manager.loader.releaseAll(naBundle);
|
||||
Core.inst.manager.loader.removeBundle(resBundle);
|
||||
Core.inst.manager.loader.removeBundle(naBundle);
|
||||
this.log(`卸载: ${name}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载ui内部资源
|
||||
*/
|
||||
public loadRes<T extends typeof Asset>(target: Component, path: string, type: T, callback?: (item: InstanceType<T> | null) => any) {
|
||||
if (typeof target === 'string') {
|
||||
Core.inst.manager.loader.load({
|
||||
bundle: this.getResBundleName(target),
|
||||
path: path,
|
||||
type: type,
|
||||
onComplete: callback
|
||||
});
|
||||
} else {
|
||||
const view = this.getBaseView(target.node) || this.getViewInParents(target.node) || this.getViewInChildren(director.getScene());
|
||||
if (view) {
|
||||
Core.inst.manager.loader.load({
|
||||
bundle: this.getResBundleName(view.viewName as UIName | MiniName),
|
||||
path: path,
|
||||
type: type,
|
||||
onComplete: callback
|
||||
});
|
||||
} else {
|
||||
this.error('loadRes', target.name, path);
|
||||
callback && callback(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载ui内部资源
|
||||
*/
|
||||
public preloadRes<T extends typeof Asset>(target: Component | UIName | MiniName, path: string, type: T, complete?: (item: AssetManager.RequestItem[] | null) => any) {
|
||||
if (typeof target === 'string') {
|
||||
Core.inst.manager.loader.preload({
|
||||
bundle: this.getResBundleName(target),
|
||||
path: path,
|
||||
type: type,
|
||||
onComplete: complete
|
||||
});
|
||||
} else {
|
||||
const view = this.getBaseView(target.node) || this.getViewInParents(target.node) || this.getViewInChildren(director.getScene());
|
||||
if (view) {
|
||||
Core.inst.manager.loader.preload({
|
||||
bundle: this.getResBundleName(view.viewName as UIName | MiniName),
|
||||
path: path,
|
||||
type: type,
|
||||
onComplete: complete
|
||||
});
|
||||
} else {
|
||||
this.error('preloadRes', target.name, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载ui内部资源
|
||||
*/
|
||||
public loadResDir<T extends typeof Asset>(target: Component, path: string, type: T, callback?: (items: InstanceType<T>[] | null) => any) {
|
||||
if (typeof target === 'string') {
|
||||
Core.inst.manager.loader.loadDir({
|
||||
bundle: this.getResBundleName(target),
|
||||
path: path,
|
||||
type: type,
|
||||
onComplete: callback
|
||||
});
|
||||
} else {
|
||||
const view = this.getBaseView(target.node) || this.getViewInParents(target.node) || this.getViewInChildren(director.getScene());
|
||||
if (view) {
|
||||
Core.inst.manager.loader.loadDir({
|
||||
bundle: this.getResBundleName(view.viewName as UIName | MiniName),
|
||||
path: path,
|
||||
type: type,
|
||||
onComplete: callback
|
||||
});
|
||||
} else {
|
||||
this.error('loadResDir', target.name, path);
|
||||
callback && callback([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载ui内部资源
|
||||
*/
|
||||
public preloadResDir<T extends typeof Asset>(target: Component | UIName | MiniName, path: string, type: T, complete?: (item: AssetManager.RequestItem[] | null) => any) {
|
||||
if (typeof target === 'string') {
|
||||
Core.inst.manager.loader.preloadDir({
|
||||
bundle: this.getResBundleName(target),
|
||||
path: path,
|
||||
type: type,
|
||||
onComplete: complete
|
||||
});
|
||||
} else {
|
||||
const view = this.getBaseView(target.node) || this.getViewInParents(target.node) || this.getViewInChildren(director.getScene());
|
||||
if (view) {
|
||||
Core.inst.manager.loader.preloadDir({
|
||||
bundle: this.getResBundleName(view.viewName as UIName | MiniName),
|
||||
path: path,
|
||||
type: type,
|
||||
onComplete: complete
|
||||
});
|
||||
} else {
|
||||
this.error('preloadResDir', target.name, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载UI
|
||||
*/
|
||||
public preload(name: UIName | MiniName, complete?: (item: AssetManager.RequestItem[] | null) => any) {
|
||||
// 验证name是否为真
|
||||
if (!name) {
|
||||
this.error('preload', 'fail');
|
||||
complete && setTimeout(() => {
|
||||
if (!isValid(this)) return;
|
||||
complete(null);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.initBundle(name, ([naBundle]) => {
|
||||
const isScene = naBundle.getSceneInfo(name);
|
||||
Core.inst.manager.loader.preload({
|
||||
bundle: this.getNativeBundleName(name),
|
||||
path: this.getUIPath(name),
|
||||
type: isScene ? SceneAsset : Prefab,
|
||||
onComplete: complete
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载UI
|
||||
*/
|
||||
public load(name: UIName | MiniName): void;
|
||||
public load(name: UIName | MiniName, complete: (result: Prefab | SceneAsset | null) => any): void;
|
||||
public load(name: UIName | MiniName, progress: (finish: number, total: number, item: AssetManager.RequestItem) => void, complete: (result: Prefab | SceneAsset | null) => any): void;
|
||||
public load(name: UIName | MiniName, ...args: Function[]): void {
|
||||
const progress = (args[1] && args[0]) as (finish: number, total: number, item: AssetManager.RequestItem) => void;
|
||||
const complete = (args[1] || args[0]) as (result: any) => any;
|
||||
|
||||
// 验证name是否为真
|
||||
if (!name) {
|
||||
this.error('load', 'fail');
|
||||
complete && setTimeout(() => {
|
||||
if (!isValid(this)) return;
|
||||
complete(null);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 异步加载
|
||||
this.installUI(name, (result) => {
|
||||
if (!result) return complete && complete(null);
|
||||
return complete && complete(result);
|
||||
}, progress);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁UI,释放资源
|
||||
* - 直接销毁,不管是否是show状态
|
||||
* - 此流程一定是同步的
|
||||
*/
|
||||
public release(nameOrCom: UIName | MiniName | BaseView) {
|
||||
const uiName = typeof nameOrCom === 'string' ? nameOrCom : nameOrCom.viewName;
|
||||
|
||||
if (!uiName) {
|
||||
this.error('release', `${nameOrCom} fail`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 传入字符串是释放所有
|
||||
if (typeof nameOrCom === 'string') {
|
||||
this.getUIInScene(uiName, true).forEach((node) => {
|
||||
const com = this.getBaseView(node);
|
||||
if (!com) {
|
||||
this.error('release', `${uiName}不存在BaseView组件`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (com.isShow) {
|
||||
this.warn('release', `${uiName}正处于show状态, 此处将直接销毁`);
|
||||
}
|
||||
if (com === this.currPage) {
|
||||
this.currPage = null;
|
||||
}
|
||||
if (com === this.currFocus) {
|
||||
this.currFocus = null;
|
||||
}
|
||||
|
||||
this.uiShowingMap.delete(com);
|
||||
|
||||
if (node && isValid(node, true)) {
|
||||
node.parent = null;
|
||||
node.destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
// 传入组件是释放单个
|
||||
else {
|
||||
if (nameOrCom.isShow) {
|
||||
this.warn('release', `${uiName}正处于show状态, 此处将直接销毁`);
|
||||
}
|
||||
if (nameOrCom === this.currPage) {
|
||||
this.currPage = null;
|
||||
}
|
||||
if (nameOrCom === this.currFocus) {
|
||||
this.currFocus = null;
|
||||
}
|
||||
|
||||
this.uiShowingMap.delete(nameOrCom);
|
||||
|
||||
const node = nameOrCom.node;
|
||||
if (node && isValid(node, true)) {
|
||||
node.parent = null;
|
||||
node.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
// 当全部释放时才清除缓存
|
||||
const nodes = this.getUIInScene(uiName, true);
|
||||
if (nodes.length === 0 || nodes.every(node => !isValid(node, true))) {
|
||||
this.uninstallUI(uiName as UIName | MiniName);
|
||||
this.log(`释放资源: ${uiName}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁全部UI,释放资源
|
||||
* - 直接销毁,不管是否是show状态
|
||||
* - 此流程一定是同步的
|
||||
*/
|
||||
public releaseAll(exclude?: UIName[]) {
|
||||
Object.keys(this.prefabCache).forEach((name: UIName) => {
|
||||
if (exclude && exclude.indexOf(name) !== -1) return;
|
||||
this.release(name);
|
||||
});
|
||||
Object.keys(this.sceneCache).forEach((name: UIName) => {
|
||||
if (exclude && exclude.indexOf(name) !== -1) return;
|
||||
this.release(name);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查UI是否有效
|
||||
* - -1: 加载失败
|
||||
* - 0: UI无效
|
||||
* - 1: UI有效
|
||||
*/
|
||||
private checkUIValid(name: UIName | MiniName, data: any, callback: (valid: -1 | 0 | 1) => any) {
|
||||
this.installUI(name, (result) => {
|
||||
if (!result) return callback(-1);
|
||||
const View = this.getUIClass(name);
|
||||
if (!View) return callback(0);
|
||||
if (!View.isViewValid) return callback(1);
|
||||
View.isViewValid((valid: boolean) => {
|
||||
callback(valid ? 1 : 0);
|
||||
}, data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新阴影的层级及显示
|
||||
*/
|
||||
public refreshShade() {
|
||||
// 借助refreshShade实现onFocus、onLostFocus(onFocus不会被每次都触发,只有产生变化时才触发)
|
||||
let onFocus = false;
|
||||
// 倒序遍历uiRoots
|
||||
let uiRoots = this.UserInterface.children;
|
||||
for (let index = uiRoots.length - 1; index >= 0; index--) {
|
||||
const uiRoot = uiRoots[index];
|
||||
if (uiRoot !== this.shade && uiRoot !== this.loading) {
|
||||
// 倒序遍历uiRoot
|
||||
let children = uiRoot.children;
|
||||
for (let i = children.length - 1; i >= 0; i--) {
|
||||
const node = children[i];
|
||||
if (node === this.shade) continue;
|
||||
|
||||
const com = this.getBaseView(node);
|
||||
if (!com) continue;
|
||||
|
||||
// 触发onFocus
|
||||
if (!onFocus && com.isCaptureFocus && com.isShow) {
|
||||
onFocus = true;
|
||||
if (this.currFocus !== com) {
|
||||
isValid(this.currFocus, true) && this.currFocus.constructor.prototype.focus.call(this.currFocus, false);
|
||||
this.currFocus = com;
|
||||
this.currFocus.constructor.prototype.focus.call(this.currFocus, true);
|
||||
}
|
||||
}
|
||||
// 添加遮罩
|
||||
if (com.isNeedShade && com.isShow) {
|
||||
const shadeSetting = Object.assign({}, UIManager.setting.shade, com.constructor.prototype.onShade.call(com));
|
||||
if (shadeSetting.blur) {
|
||||
this.shade.getComponent(UIMgrShade).init(0, 255, 255, 0, true);
|
||||
} else {
|
||||
this.shade.getComponent(UIMgrShade).init(
|
||||
typeof shadeSetting.delay !== 'number' ? 0 : shadeSetting.delay,
|
||||
typeof shadeSetting.begin !== 'number' ? 60 : shadeSetting.begin,
|
||||
typeof shadeSetting.end !== 'number' ? 180 : shadeSetting.end,
|
||||
typeof shadeSetting.speed !== 'number' ? 100 : shadeSetting.speed,
|
||||
false,
|
||||
);
|
||||
}
|
||||
this.shade.layer = node.layer;
|
||||
this.shade.parent = uiRoot;
|
||||
this.shade.active = true;
|
||||
// 以z坐标来代替2.x时代的zIndex
|
||||
this.shade.setPosition(this.shade.position.x, this.shade.position.y, node.position.z);
|
||||
|
||||
let shadeIndex = this.shade.getSiblingIndex();
|
||||
let nodeIndex = node.getSiblingIndex();
|
||||
if (shadeIndex > nodeIndex) {
|
||||
this.shade.setSiblingIndex(nodeIndex);
|
||||
} else {
|
||||
this.shade.setSiblingIndex(nodeIndex - 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.shade.active = false;
|
||||
this.shade.getComponent(UIMgrShade).clear();
|
||||
if (!onFocus) {
|
||||
isValid(this.currFocus, true) && this.currFocus.constructor.prototype.focus.call(this.currFocus, false);
|
||||
this.currFocus = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 解析prefab
|
||||
private parsingPrefab(prefab: Prefab, name: string) {
|
||||
if (!prefab) return null;
|
||||
|
||||
const node = instantiate(prefab);
|
||||
|
||||
node.active = false;
|
||||
if (node.name !== name) {
|
||||
this.warn('parsingPrefab', `节点名与UI名不一致, 已重置为UI名: ${this.getUIPath(name)}`);
|
||||
node.name = name;
|
||||
}
|
||||
|
||||
node.parent = this.getUIParent(name);
|
||||
node.getComponent(Widget)?.updateAlignment();
|
||||
return node;
|
||||
}
|
||||
|
||||
// 解析scene
|
||||
private parsingScene(asset: SceneAsset, name: string) {
|
||||
if (!asset || !asset.scene) return null;
|
||||
|
||||
if (asset.scene.name !== name) {
|
||||
this.warn('parsingScene', `场景名与UI名不一致, 已重置为UI名: ${this.getUIPath(name)}`);
|
||||
asset.scene.name = name;
|
||||
}
|
||||
|
||||
const view = this.getViewInChildren(asset.scene);
|
||||
if (!view) {
|
||||
this.error('parsingScene', `解析场景时未查询到根节点存在BaseView: ${this.getUIPath(name)}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
view.node.active = false;
|
||||
if (view.node.name !== name) {
|
||||
this.warn('parsingScene', `节点名与UI名不一致, 已重置为UI名: ${this.getUIPath(name)}`);
|
||||
view.node.name = name;
|
||||
}
|
||||
return view.node;
|
||||
}
|
||||
|
||||
private addUILoadingUuid(name: UIName, loadingUuid?: string) {
|
||||
const uuid = loadingUuid || this.createUUID();
|
||||
if (!this.uiLoadingMap.has(name)) {
|
||||
this.uiLoadingMap.set(name, [uuid]);
|
||||
} else {
|
||||
this.uiLoadingMap.get(name).push(uuid);
|
||||
}
|
||||
return uuid;
|
||||
}
|
||||
|
||||
private removeUILoadingUuid(name: UIName, uuid: string) {
|
||||
if (!this.uiLoadingMap.has(name)) return false;
|
||||
const index = this.uiLoadingMap.get(name).indexOf(uuid);
|
||||
if (index === -1) return false;
|
||||
this.uiLoadingMap.get(name).splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建UI
|
||||
*/
|
||||
private createUI(name: UIName, silent: boolean, callback: (node: Node, scene?: Scene) => any) {
|
||||
// 生成一个UI加载的UUID
|
||||
const loadingUuid = silent ? '' : this.showLoading();
|
||||
const uiLoadingUuid = this.addUILoadingUuid(name, loadingUuid);
|
||||
|
||||
// 验证name
|
||||
if (!name) {
|
||||
setTimeout(() => {
|
||||
if (!isValid(this)) return;
|
||||
// 验证本次加载是否有效
|
||||
if (this.removeUILoadingUuid(name, uiLoadingUuid) === false) {
|
||||
return this.hideLoading(loadingUuid);
|
||||
}
|
||||
callback(null);
|
||||
this.hideLoading(loadingUuid);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 判断是否已经存在节点并且是单例模式
|
||||
const node = this.getUIInScene(name);
|
||||
if (isValid(node, true) && this.getBaseView(node).isSingleton === true) {
|
||||
setTimeout(() => {
|
||||
if (!isValid(this)) return;
|
||||
|
||||
// 验证本次加载是否有效
|
||||
if (this.removeUILoadingUuid(name, uiLoadingUuid) === false) {
|
||||
return this.hideLoading(loadingUuid);
|
||||
}
|
||||
|
||||
// 验证节点是否有效
|
||||
if (isValid(node, true)) {
|
||||
if (this.currScene === name) {
|
||||
callback(node, director.getScene());
|
||||
} else {
|
||||
callback(node);
|
||||
}
|
||||
this.hideLoading(loadingUuid);
|
||||
} else {
|
||||
this.createUI(name, silent, callback);
|
||||
this.hideLoading(loadingUuid);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载UI
|
||||
this.load(name, (asset) => {
|
||||
if (!isValid(this)) return;
|
||||
|
||||
// 验证本次加载是否有效
|
||||
if (this.removeUILoadingUuid(name, uiLoadingUuid) === false) {
|
||||
return this.hideLoading(loadingUuid);
|
||||
}
|
||||
|
||||
// 是场景
|
||||
if (asset instanceof SceneAsset) {
|
||||
callback(this.parsingScene(asset, name), asset.scene);
|
||||
this.hideLoading(loadingUuid);
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证是否是单例(一个单例会有被同时load多次的情况,因为判断一个ui是否是单例,必须要至少实例化一个后才能获取)
|
||||
const node = this.getUIInScene(name);
|
||||
if (!isValid(node, true) || this.getBaseView(node).isSingleton === false) {
|
||||
callback(this.parsingPrefab(asset, name));
|
||||
this.hideLoading(loadingUuid);
|
||||
} else {
|
||||
callback(node);
|
||||
this.hideLoading(loadingUuid);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示默认View
|
||||
*/
|
||||
public showDefault(onShow?: (result?: any) => any) {
|
||||
if (this.defaultUI) {
|
||||
this.show({
|
||||
name: this.defaultUI,
|
||||
data: this.defaultData,
|
||||
onShow
|
||||
});
|
||||
} else {
|
||||
Core.inst.manager.ui.showToast('请先设置首界面\n在setting.ts中修改defaultUI', 100);
|
||||
onShow && onShow();
|
||||
this.warn('defaultUI不存在,请在setting.ts中修改');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否展示了(包括加载中和队列中)
|
||||
*/
|
||||
public isShow(name: UIName) {
|
||||
return !!this.getUIInShowing(name) ||
|
||||
this.isInQueue(name) ||
|
||||
this.isLoading(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否在队列中
|
||||
*/
|
||||
public isInQueue(name: UIName) {
|
||||
return !!this.showQueue.find((v) => { return v.name == name; });
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否在加载中
|
||||
*/
|
||||
public isLoading(name: UIName) {
|
||||
return this.uiLoadingMap.has(name) && this.uiLoadingMap.get(name).length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 放入队列
|
||||
*/
|
||||
private putInShowQueue(data: IShowParams<UIName>) {
|
||||
if (data.queue === 'join' || this.showQueue.length === 0) {
|
||||
this.showQueue.push(data);
|
||||
} else {
|
||||
this.showQueue.splice(1, 0, data);
|
||||
}
|
||||
if (this.showQueue.length === 1) {
|
||||
this.consumeShowQueue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 消耗队列
|
||||
*/
|
||||
private consumeShowQueue() {
|
||||
if (this.showQueue.length === 0) return;
|
||||
const data = this.showQueue[0];
|
||||
this.show({
|
||||
name: data.name,
|
||||
data: data.data,
|
||||
onShow: data.onShow,
|
||||
onHide: (result: any) => {
|
||||
data.onHide && data.onHide(result);
|
||||
this.showQueue.shift();
|
||||
this.consumeShowQueue();
|
||||
},
|
||||
onError: data.onError ? (error: string, code: 0 | 1) => {
|
||||
const ret = data.onError(error, code);
|
||||
this.showQueue.shift();
|
||||
this.consumeShowQueue();
|
||||
return ret;
|
||||
} : undefined,
|
||||
top: data.top,
|
||||
attr: data.attr,
|
||||
silent: data.silent
|
||||
});
|
||||
}
|
||||
|
||||
private showUI(params: IShowParams<UIName>) {
|
||||
const { name, data, onShow, onHide, onError, top = true, attr = null, silent = false } = params;
|
||||
|
||||
this.createUI(name, silent, (node, scene) => {
|
||||
if (!node) {
|
||||
this.error('show', `${name} 不存在或加载失败`);
|
||||
// 「没有指定onError」或「onError返回true」会自动发起重试
|
||||
if (onError && onError(`${name} 不存在或加载失败`, UIManager.ErrorCode.LoadError) !== true) {
|
||||
return;
|
||||
}
|
||||
this.scheduleOnce(() => this.showUI(params), 1);
|
||||
if (!silent) this.showLoading(1);
|
||||
return;
|
||||
}
|
||||
|
||||
!scene && top && node.setSiblingIndex(-1);
|
||||
|
||||
const com = this.getBaseView(node);
|
||||
this.uiShowingMap.set(com, name);
|
||||
com.constructor.prototype.show.call(com, data, attr,
|
||||
// onShow
|
||||
(result: any) => {
|
||||
this.uiShowingMap.set(com, name);
|
||||
onShow && onShow(result);
|
||||
},
|
||||
// onHide
|
||||
(result: any) => {
|
||||
this.uiShowingMap.delete(com);
|
||||
onHide && onHide(result);
|
||||
},
|
||||
// beforeShow
|
||||
(error: string) => {
|
||||
if (error) {
|
||||
this.uiShowingMap.delete(com);
|
||||
onError && onError(error, UIManager.ErrorCode.LogicError);
|
||||
} else if (BaseView.isPage(name)) {
|
||||
this.uiShowingMap.set(com, name);
|
||||
const oldCom = this.currPage;
|
||||
this.currPage = com;
|
||||
if (isValid(oldCom, true) && oldCom !== com && oldCom.isShow && oldCom.isAlwaysExist === false) {
|
||||
oldCom.constructor.prototype.hide.call(oldCom, { name });
|
||||
}
|
||||
if (scene) {
|
||||
if (oldCom !== com) {
|
||||
this.currScene = name;
|
||||
director.runSceneImmediate(scene, null, () => {
|
||||
this.log(`切换场景: ${name}`);
|
||||
});
|
||||
}
|
||||
} else if (this.currScene !== UIScene) {
|
||||
this.currScene = UIScene;
|
||||
const scene = new Scene(UIScene);
|
||||
scene.autoReleaseAssets = true;
|
||||
director.runSceneImmediate(scene, null, () => {
|
||||
this.log(`切换场景: ${UIScene}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示一个UI
|
||||
* - 此流程一定是异步的
|
||||
*/
|
||||
public show<UI extends BaseView>(params
|
||||
// @ts-ignore
|
||||
: IShowParams<UIName, Parameters<UI['onShow']>[0], ReturnType<UI['onShow']>, ReturnType<UI['onHide']>>) {
|
||||
const { name, data, queue, onError, silent = false } = params;
|
||||
|
||||
// 加入队列中
|
||||
if (queue) {
|
||||
this.putInShowQueue(params);
|
||||
return;
|
||||
}
|
||||
|
||||
this.log(`show: ${name}`);
|
||||
|
||||
// 生成一个UI加载的UUID
|
||||
const loadingUuid = silent ? '' : this.showLoading();
|
||||
const uiLoadingUuid = this.addUILoadingUuid(name, loadingUuid);
|
||||
// 判断ui是否有效
|
||||
Core.inst.lib.task.execute((retry) => {
|
||||
this.checkUIValid(name, data, (valid) => {
|
||||
// 验证本次加载是否有效
|
||||
if (this.removeUILoadingUuid(name, uiLoadingUuid) === false) {
|
||||
this.hideLoading(loadingUuid);
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载失败
|
||||
if (valid === -1) {
|
||||
this.error('show', `${name} 不存在或加载失败`);
|
||||
// 「没有指定onError」或「onError返回true」会自动发起重试
|
||||
if (onError && onError(`${name} 不存在或加载失败`, UIManager.ErrorCode.LoadError) !== true) {
|
||||
return this.hideLoading(loadingUuid);
|
||||
}
|
||||
return retry(1);
|
||||
}
|
||||
|
||||
// ui无效
|
||||
if (valid === 0) {
|
||||
this.warn('show', `${name} 无效`);
|
||||
this.uninstallUI(name);
|
||||
onError && onError(`${name} 无效`, UIManager.ErrorCode.InvalidError);
|
||||
this.hideLoading(loadingUuid);
|
||||
return;
|
||||
}
|
||||
|
||||
this.showUI(params);
|
||||
this.hideLoading(loadingUuid);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示一个UI
|
||||
* - 此流程一定是异步的
|
||||
*/
|
||||
public showAsync<UI extends BaseView>(params
|
||||
// @ts-ignore
|
||||
: IShowAsyncParams<UIName, Parameters<UI['onShow']>[0], ReturnType<UI['onShow']>>): Promise<ReturnType<UI['onHide']>> {
|
||||
return new Promise((resolve) => {
|
||||
this.show({
|
||||
...params,
|
||||
onHide(result) {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭View
|
||||
* - 此流程一定是同步的
|
||||
*/
|
||||
public hide<UI extends BaseView>({ name, data, onHide }
|
||||
// @ts-ignore
|
||||
: IHideParams<UIName, Parameters<UI['onHide']>[0], ReturnType<UI['onHide']>>) {
|
||||
const nodes = this.getUIInShowing(name, true);
|
||||
|
||||
this.log(`hide: ${name}`);
|
||||
|
||||
if (nodes.length === 0) {
|
||||
if (!this.uiLoadingMap.has(name) || this.uiLoadingMap.get(name).length === 0) {
|
||||
return this.warn('hide', `${name} 不存在`);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.uiLoadingMap.has(name)) {
|
||||
this.uiLoadingMap.get(name).forEach((loadingUuid) => this.hideLoading(loadingUuid));
|
||||
this.uiLoadingMap.get(name).length = 0;
|
||||
}
|
||||
|
||||
for (let index = nodes.length - 1; index >= 0; index--) {
|
||||
const node = nodes[index];
|
||||
const com = this.getBaseView(node);
|
||||
|
||||
if (this.currPage === com) {
|
||||
this.currPage = null;
|
||||
}
|
||||
|
||||
com.constructor.prototype.hide.call(com, data, onHide);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从顶部关闭一个View(不会重复关闭节点)
|
||||
* - 此流程一定是同步的
|
||||
*/
|
||||
public pop<UI extends BaseView>({ name, data, onHide }
|
||||
// @ts-ignore
|
||||
: IHideParams<UIName, Parameters<UI['onHide']>[0], ReturnType<UI['onHide']>>) {
|
||||
const nodes = this.getUIInShowing(name, true);
|
||||
|
||||
if (this.uiLoadingMap.has(name) && this.uiLoadingMap.get(name).length) {
|
||||
const loadingUuid = this.uiLoadingMap.get(name).pop();
|
||||
this.hideLoading(loadingUuid);
|
||||
this.log(`pop: ${name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nodes.length) {
|
||||
const node = nodes.pop();
|
||||
const com = this.getBaseView(node);
|
||||
|
||||
if (this.currPage === com) {
|
||||
this.currPage = null;
|
||||
}
|
||||
|
||||
com.constructor.prototype.hide.call(com, data, onHide);
|
||||
this.log(`pop: ${name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.warn('pop', `${name} 不存在`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从底部关闭一个View(不会重复关闭节点)
|
||||
* - 此流程一定是同步的
|
||||
*/
|
||||
public shift<UI extends BaseView>({ name, data, onHide }
|
||||
// @ts-ignore
|
||||
: IHideParams<UIName, Parameters<UI['onHide']>[0], ReturnType<UI['onHide']>>) {
|
||||
const nodes = this.getUIInShowing(name, true);
|
||||
|
||||
if (nodes.length) {
|
||||
const node = nodes[0];
|
||||
const com = this.getBaseView(node);
|
||||
|
||||
if (this.currPage === com) {
|
||||
this.currPage = null;
|
||||
}
|
||||
|
||||
com.constructor.prototype.hide.call(com, data, onHide);
|
||||
this.log(`shift: ${name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.uiLoadingMap.has(name) && this.uiLoadingMap.get(name).length) {
|
||||
const loadingUuid = this.uiLoadingMap.get(name).shift();
|
||||
this.hideLoading(loadingUuid);
|
||||
this.log(`shift: ${name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.warn('shift', `${name} 不存在`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭全部View
|
||||
* - 不关闭展示中的Page(加载中的会停止)
|
||||
* - 此流程一定是同步的
|
||||
*/
|
||||
public hideAll({ data, exclude }: { data?: any, exclude?: UIName[] } = {}): void {
|
||||
this.log('hideAll');
|
||||
// 展示中的
|
||||
this.uiShowingMap.forEach((name, com) => {
|
||||
if (BaseView.isPaper(name)) return;
|
||||
if (exclude && exclude.indexOf(name) !== -1) return;
|
||||
if (com === this.currPage) return;
|
||||
com.constructor.prototype.hide.call(com, data);
|
||||
});
|
||||
// 加载中的
|
||||
this.uiLoadingMap.forEach((value, name) => {
|
||||
if (BaseView.isPaper(name)) return;
|
||||
if (exclude && exclude.indexOf(name) !== -1) return;
|
||||
value.forEach((loadingUuid) => this.hideLoading(loadingUuid));
|
||||
value.length = 0;
|
||||
});
|
||||
}
|
||||
|
||||
public showLoading(timeout = 0) {
|
||||
this.loading.active = true;
|
||||
this.loading.setSiblingIndex(-1);
|
||||
if (this.loading.getComponent(UIMgrLoading)) {
|
||||
this.loading.getComponent(UIMgrLoading).init();
|
||||
} else {
|
||||
// 兼容旧版本
|
||||
this.loading.getComponentInChildren(UIMgrLoading)?.init();
|
||||
}
|
||||
const uuid = this.createUUID();
|
||||
this.showLoadingMap.set(uuid, true);
|
||||
if (timeout > 0) this.scheduleOnce(() => {
|
||||
this.hideLoading(uuid);
|
||||
}, timeout);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public hideLoading(uuid: string) {
|
||||
if (!uuid) return;
|
||||
this.showLoadingMap.delete(uuid);
|
||||
if (this.showLoadingMap.size === 0) {
|
||||
if (this.loading.getComponent(UIMgrLoading)) {
|
||||
this.loading.getComponent(UIMgrLoading).clear();
|
||||
} else {
|
||||
// 兼容旧版本
|
||||
this.loading.getComponentInChildren(UIMgrLoading)?.clear();
|
||||
}
|
||||
this.loading.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加触摸屏蔽
|
||||
*/
|
||||
public addTouchMask(timeout = 0) {
|
||||
this.addTouchMaskListener();
|
||||
const uuid = this.createUUID();
|
||||
this.touchMaskMap.set(uuid, true);
|
||||
if (timeout > 0) this.scheduleOnce(() => {
|
||||
this.removeTouchMask(uuid);
|
||||
}, timeout);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除触摸屏蔽
|
||||
* @param uuid addTouchMask的返回值
|
||||
*/
|
||||
public removeTouchMask(uuid: string) {
|
||||
if (!uuid) return;
|
||||
this.touchMaskMap.delete(uuid);
|
||||
this.removeTouchMaskListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示Toast
|
||||
* @param message 文本
|
||||
* @param timeout 持续时间(秒),默认2秒
|
||||
*/
|
||||
public showToast(message: string, timeout?: number) {
|
||||
if (!this.toast) {
|
||||
return this.error('showToast', '请确认首场景中「Canvas/Manager/UIManager」的「Toast Pre」属性存在');
|
||||
}
|
||||
this.toast.setSiblingIndex(-1);
|
||||
this.toast.getComponent(UIMgrToast).add({
|
||||
message, timeout
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理Toast
|
||||
*/
|
||||
public clearToast() {
|
||||
if (!this.toast) return;
|
||||
this.toast.getComponent(UIMgrToast).clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置触摸是否启用
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
public setTouchEnabled(enabled: boolean) {
|
||||
if (enabled) {
|
||||
this.touchEnabled = true;
|
||||
this.removeTouchMaskListener();
|
||||
} else {
|
||||
this.addTouchMaskListener();
|
||||
this.touchEnabled = false;
|
||||
}
|
||||
this.warn('setTouchEnabled', this.touchEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在2DUI根节点上处理事件
|
||||
*/
|
||||
public onUserInterface(...args: Parameters<Node['on']>) {
|
||||
Node.prototype.on.apply(this.UserInterface, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在2DUI根节点上处理事件
|
||||
*/
|
||||
public onceUserInterface(...args: Parameters<Node['once']>) {
|
||||
Node.prototype.once.apply(this.UserInterface, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在2DUI根节点上处理事件
|
||||
*/
|
||||
public offUserInterface(...args: Parameters<Node['off']>) {
|
||||
Node.prototype.off.apply(this.UserInterface, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在2DUI根节点上处理事件
|
||||
*/
|
||||
public targetOffUserInterface(...args: Parameters<Node['targetOff']>) {
|
||||
Node.prototype.targetOff.apply(this.UserInterface, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 立即给2DUI的子节点排序
|
||||
*/
|
||||
public sortUserInterface(name: IViewType) {
|
||||
this.UserInterface
|
||||
?.getChildByName(name)
|
||||
?.getComponent(UIMgrZOrder)
|
||||
?.updateZOrder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 屏幕截图
|
||||
* - 需要在Director.EVENT_BEFORE_RENDER事件中调用
|
||||
* @example
|
||||
* director.once(Director.EVENT_BEFORE_RENDER, () => {
|
||||
* const renderTexture = new RenderTexture();
|
||||
* const size = view.getVisibleSize();
|
||||
* renderTexture.reset({ width: size.width, height: size.height });
|
||||
* app.manager.ui.screenshot(renderTexture);
|
||||
* });
|
||||
*/
|
||||
public screenshot(renderTexture: RenderTexture, opts?: {
|
||||
/**摄像机筛选 */
|
||||
cameraFilter?: (camera: Camera) => boolean;
|
||||
/**摄像机列表 */
|
||||
cameraList?: Camera[];
|
||||
}) {
|
||||
const cameras = opts?.cameraList || director.getScene().getComponentsInChildren(Camera);
|
||||
|
||||
const cameraList = cameras.sort((a, b) => a.priority - b.priority)
|
||||
.filter(camera => {
|
||||
if (!camera.enabledInHierarchy) return false;
|
||||
if (camera.targetTexture) return false;
|
||||
return opts?.cameraFilter ? opts.cameraFilter(camera) : true;
|
||||
});
|
||||
const cameraList2 = cameraList.map(camera => camera.camera);
|
||||
|
||||
cameraList.forEach(camera => {
|
||||
camera.targetTexture = renderTexture;
|
||||
});
|
||||
director.root.pipeline.render(cameraList2);
|
||||
cameraList.forEach(camera => {
|
||||
camera.targetTexture = null;
|
||||
});
|
||||
|
||||
return renderTexture;
|
||||
}
|
||||
}
|
||||
9
extensions/app/assets/manager/ui/UIManager.ts.meta
Normal file
9
extensions/app/assets/manager/ui/UIManager.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "234f62f1-ebd3-4d14-9f6f-0b69c7096449",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
12
extensions/app/assets/manager/ui/comp.meta
Normal file
12
extensions/app/assets/manager/ui/comp.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "cc9c8895-27f9-49df-aca5-a0d1b9ba6480",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
114
extensions/app/assets/manager/ui/comp/UIMgrLoading.ts
Normal file
114
extensions/app/assets/manager/ui/comp/UIMgrLoading.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { Component, Graphics, Node, Size, UITransform, _decorator } from 'cc';
|
||||
const { ccclass, property, requireComponent } = _decorator;
|
||||
|
||||
@ccclass('UIMgrLoading')
|
||||
@requireComponent(UITransform)
|
||||
export default class UIMgrLoading extends Component {
|
||||
@property(Node)
|
||||
private loading: Node;
|
||||
|
||||
@property({ tooltip: '动画的尺寸' })
|
||||
private size: Size = new Size(60, 60);
|
||||
|
||||
@property({ tooltip: '等待几秒后开始动画' })
|
||||
private delay = 0;
|
||||
|
||||
private progress = 0;
|
||||
private ringScale = 1;
|
||||
private reverse = false;
|
||||
|
||||
private angleSpeed = 120;
|
||||
private ringSpeed = 0.02;
|
||||
|
||||
private inited = false;
|
||||
private drawing = false;
|
||||
private timedown = 0;
|
||||
|
||||
init() {
|
||||
if (this.inited) return;
|
||||
this.inited = true;
|
||||
|
||||
this.progress = 0;
|
||||
this.ringScale = 1;
|
||||
this.loading.angle = 0;
|
||||
this.reverse = false;
|
||||
|
||||
this.drawing = false;
|
||||
this.timedown = this.delay;
|
||||
this.loading.getComponent(Graphics).clear();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.inited = false;
|
||||
this.drawing = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 需要重写
|
||||
*/
|
||||
private onDraw() {
|
||||
const graphics = this.loading.getComponent(Graphics);
|
||||
const uiTransform = this.loading.getComponent(UITransform);
|
||||
|
||||
const centerX = this.size.width * (0.5 - uiTransform.anchorX);
|
||||
const centerY = this.size.height * (0.5 - uiTransform.anchorY);
|
||||
|
||||
const r = Math.min(this.size.width / 2, this.size.height / 2);
|
||||
|
||||
const allPI = Math.PI;
|
||||
const offst = 0;
|
||||
|
||||
graphics.clear();
|
||||
if (this.reverse) {
|
||||
const start = 0.5 * Math.PI + offst;
|
||||
const end = 0.5 * Math.PI + this.progress * 2 * allPI + offst;
|
||||
graphics.arc(centerX, centerY, r, start, end, true);
|
||||
} else {
|
||||
const start = 0.5 * Math.PI - offst;
|
||||
const end = 0.5 * Math.PI - this.progress * 2 * allPI - offst;
|
||||
graphics.arc(centerX, centerY, r, start, end, false);
|
||||
}
|
||||
graphics.stroke();
|
||||
}
|
||||
|
||||
protected update(dt: number): void {
|
||||
if (!this.inited) return;
|
||||
|
||||
// 倒计时
|
||||
if (!this.drawing) {
|
||||
if (this.timedown > 0) {
|
||||
this.timedown -= dt;
|
||||
}
|
||||
if (this.timedown <= 0) {
|
||||
this.drawing = true;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 旋转
|
||||
this.loading.angle -= this.angleSpeed * dt;
|
||||
if (this.loading.angle >= 360 || this.loading.angle <= -360) {
|
||||
this.loading.angle = this.loading.angle % 360;
|
||||
}
|
||||
|
||||
// 进度
|
||||
if (this.ringScale > 0) {
|
||||
this.progress = Math.min(1, this.progress + this.ringSpeed * this.ringScale);
|
||||
|
||||
if (this.progress == 1) {
|
||||
this.ringScale = -1;
|
||||
this.reverse = !this.reverse;
|
||||
}
|
||||
} else {
|
||||
this.progress = Math.max(0, this.progress + this.ringSpeed * this.ringScale);
|
||||
|
||||
if (this.progress == 0) {
|
||||
this.ringScale = 1;
|
||||
this.reverse = !this.reverse;
|
||||
}
|
||||
}
|
||||
|
||||
this.onDraw();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "4a8e5697-ae78-4a4a-8d08-17acc6823a27",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
167
extensions/app/assets/manager/ui/comp/UIMgrShade.ts
Normal file
167
extensions/app/assets/manager/ui/comp/UIMgrShade.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
import { Camera, Color, Component, Director, Material, RenderTexture, Sprite, SpriteFrame, UIOpacity, UITransform, _decorator, director } from 'cc';
|
||||
import Core from '../../../Core';
|
||||
const { ccclass, property, requireComponent } = _decorator;
|
||||
|
||||
@ccclass('UIMgrShade')
|
||||
@requireComponent(Sprite)
|
||||
@requireComponent(UIOpacity)
|
||||
export default class UIMgrShade extends Component {
|
||||
@property(Material)
|
||||
private blurMaterial: Material = null;
|
||||
|
||||
@property(SpriteFrame)
|
||||
private shadeFrame: SpriteFrame = null;
|
||||
|
||||
@property
|
||||
private _delay = 0;
|
||||
@property
|
||||
get delay() { return this._delay; }
|
||||
set delay(v) { this._delay = Math.max(v, 0); }
|
||||
|
||||
@property
|
||||
private _begin = 0;
|
||||
@property
|
||||
get begin() { return this._begin; }
|
||||
set begin(v) { if (v >= 0 && v <= 255) this._begin = v; }
|
||||
|
||||
@property
|
||||
private _end = 255;
|
||||
@property
|
||||
get end() { return this._end; }
|
||||
set end(v) { if (v >= 0 && v <= 255) this._end = v; }
|
||||
|
||||
@property
|
||||
private _speed = 10;
|
||||
@property
|
||||
get speed() {
|
||||
if (this.begin == this.end) {
|
||||
return 0;
|
||||
} else if (this.begin > this.end) {
|
||||
return this._speed > 0 ? -this._speed : this._speed;
|
||||
} else {
|
||||
return this._speed >= 0 ? this._speed : -this._speed;
|
||||
}
|
||||
}
|
||||
set speed(v) { this._speed = v; }
|
||||
|
||||
private get sprite() {
|
||||
return this.node.getComponent(Sprite);
|
||||
}
|
||||
private get opacity() {
|
||||
return this.node.getComponent(UIOpacity);
|
||||
}
|
||||
|
||||
private inited = false;
|
||||
private drawing = false;
|
||||
private timedown = 0;
|
||||
|
||||
private blurFrame: SpriteFrame = null;
|
||||
|
||||
init(delay: number, begin: number, end: number, speed: number, blur: boolean) {
|
||||
if (blur) {
|
||||
director.targetOff(this);
|
||||
this.inited = false;
|
||||
this.drawing = false;
|
||||
|
||||
this.sprite.color = Color.WHITE;
|
||||
this.sprite.customMaterial = null;
|
||||
this.sprite.spriteFrame = this.blurFrame;
|
||||
if (this.blurFrame) this.blurFrame.flipUVY = false;
|
||||
|
||||
let count = 0;
|
||||
const cameras = director.getScene().getComponentsInChildren(Camera);
|
||||
director.on(Director.EVENT_BEFORE_RENDER, () => {
|
||||
count++;
|
||||
|
||||
const renderTexture = new RenderTexture();
|
||||
const size = this.node.getComponent(UITransform);
|
||||
renderTexture.reset({ width: size.width / 2, height: size.height / 2 });
|
||||
renderTexture.addRef();
|
||||
|
||||
Core.inst.manager.ui.screenshot(renderTexture, {
|
||||
cameraList: cameras
|
||||
});
|
||||
|
||||
if (count === 1) {
|
||||
this.blurFrame = new SpriteFrame();
|
||||
this.blurFrame?.texture?.decRef();
|
||||
this.blurFrame.texture = renderTexture;
|
||||
this.blurFrame.flipUVY = true;
|
||||
this.sprite.spriteFrame = this.blurFrame;
|
||||
this.sprite.customMaterial = this.blurMaterial;
|
||||
this.blurMaterial.setProperty('blurLevel', 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (count === 5) {
|
||||
director.targetOff(this);
|
||||
this.sprite.spriteFrame.flipUVY = false;
|
||||
this.sprite.customMaterial = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.blurFrame?.texture?.decRef();
|
||||
this.blurFrame.texture = renderTexture;
|
||||
this.blurFrame.flipUVY = true;
|
||||
this.sprite.spriteFrame = this.blurFrame;
|
||||
this.sprite.customMaterial = this.blurMaterial;
|
||||
this.blurMaterial.setProperty('blurLevel', count === 2 ? 3 : 1);
|
||||
}, this);
|
||||
} else {
|
||||
director.targetOff(this);
|
||||
this.sprite.spriteFrame = this.shadeFrame;
|
||||
this.sprite.color = Color.BLACK;
|
||||
this.sprite.customMaterial = null;
|
||||
}
|
||||
|
||||
this.delay = delay;
|
||||
this.begin = begin;
|
||||
this.end = end;
|
||||
this.speed = speed;
|
||||
this.drawing = true;
|
||||
|
||||
if (this.inited) return;
|
||||
this.inited = true;
|
||||
this.timedown = this.delay;
|
||||
// 初始透明度
|
||||
this.opacity.opacity = this.timedown > 0 ? 0 : this.begin;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.inited = false;
|
||||
this.drawing = false;
|
||||
director.targetOff(this);
|
||||
this.blurFrame?.texture?.decRef();
|
||||
this.blurFrame?.destroy();
|
||||
this.blurFrame = null;
|
||||
this.sprite.spriteFrame = null;
|
||||
}
|
||||
|
||||
protected update(dt: number) {
|
||||
if (!this.inited) return;
|
||||
if (!this.drawing) return;
|
||||
|
||||
if (this.timedown > 0) {
|
||||
this.timedown -= dt;
|
||||
if (this.timedown > 0) return;
|
||||
// 初始透明度
|
||||
this.opacity.opacity = this.begin;
|
||||
}
|
||||
|
||||
const uiOpacity = this.opacity;
|
||||
if (this.speed > 0) {
|
||||
uiOpacity.opacity += this.speed * dt;
|
||||
if (uiOpacity.opacity > this.end) {
|
||||
uiOpacity.opacity = this.end;
|
||||
}
|
||||
} else if (this.speed < 0) {
|
||||
uiOpacity.opacity += this.speed * dt;
|
||||
if (uiOpacity.opacity < this.end) {
|
||||
uiOpacity.opacity = this.end;
|
||||
}
|
||||
}
|
||||
if (uiOpacity.opacity == this.end) {
|
||||
this.drawing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
9
extensions/app/assets/manager/ui/comp/UIMgrShade.ts.meta
Normal file
9
extensions/app/assets/manager/ui/comp/UIMgrShade.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "d0295d9b-b01d-493c-9e31-5ed78e6c33ab",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
111
extensions/app/assets/manager/ui/comp/UIMgrToast.ts
Normal file
111
extensions/app/assets/manager/ui/comp/UIMgrToast.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { Component, NodePool, Prefab, Tween, UIOpacity, UITransform, _decorator, instantiate, tween, view } from 'cc';
|
||||
import UIMgrToastCell from './UIMgrToastCell';
|
||||
const { property, ccclass, requireComponent } = _decorator;
|
||||
|
||||
@ccclass('UIMgrToast')
|
||||
@requireComponent(UITransform)
|
||||
export default class UIMgrToast extends Component {
|
||||
@property(Prefab)
|
||||
private cell: Prefab = null;
|
||||
|
||||
/**每条信息显示几秒 */
|
||||
private lifeTime = 2;
|
||||
|
||||
/**消失时花费几秒渐隐 */
|
||||
private outTime = 0.2;
|
||||
|
||||
/**挤压基础速度 */
|
||||
private squeezeSpeed = 200;
|
||||
|
||||
/**节点缓存池子 */
|
||||
private pool = new NodePool();
|
||||
|
||||
add(data: {
|
||||
message: string,
|
||||
timeout?: number
|
||||
}) {
|
||||
const cell = this.pool.get() || instantiate(this.cell);
|
||||
cell.setPosition(0, 0, 0);
|
||||
cell.parent = this.node;
|
||||
cell.active = true;
|
||||
|
||||
cell.getComponent(UIMgrToastCell).init(data.message);
|
||||
|
||||
cell.getComponent(UIOpacity).opacity = 255;
|
||||
tween(cell.getComponent(UIOpacity))
|
||||
.delay(data.timeout || this.lifeTime)
|
||||
.to(this.outTime, { opacity: 0 })
|
||||
.call(() => {
|
||||
this.pool.put(cell);
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
clear() {
|
||||
const children = this.node.children;
|
||||
for (let index = children.length - 1; index >= 0; index--) {
|
||||
Tween.stopAllByTarget(children[index].getComponent(UIOpacity));
|
||||
children[index].destroy();
|
||||
}
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.node.children.length;
|
||||
}
|
||||
|
||||
protected onDestroy() {
|
||||
this.pool.clear();
|
||||
}
|
||||
|
||||
protected update(dt: number) {
|
||||
const children = this.node.children;
|
||||
for (let index = children.length - 1, recovery = false; index >= 0; index--) {
|
||||
const zero = index === children.length - 1;
|
||||
const curr = children[index];
|
||||
|
||||
// 直接触发回收逻辑
|
||||
if (recovery) {
|
||||
Tween.stopAllByTarget(curr.getComponent(UIOpacity));
|
||||
this.pool.put(curr);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (zero) {
|
||||
const currUT = curr.getComponent(UITransform);
|
||||
|
||||
const lastMaxY = 0 - currUT.height / 2;
|
||||
const currMinY = curr.position.y + lastMaxY;
|
||||
|
||||
if (currMinY > lastMaxY) {
|
||||
// 存在空隙
|
||||
const addLen = Math.max(-this.squeezeSpeed * dt * (children.length - index), lastMaxY - currMinY);
|
||||
curr.setPosition(curr.position.x, curr.position.y + addLen, curr.position.z);
|
||||
}
|
||||
} else {
|
||||
const last = children[index + 1];
|
||||
const currUT = curr.getComponent(UITransform);
|
||||
const lastUT = last.getComponent(UITransform);
|
||||
|
||||
const currMinY = curr.position.y - currUT.height / 2 - 6;//6像素的间隔
|
||||
const lastMaxY = last.position.y + lastUT.height / 2;
|
||||
|
||||
if (currMinY < lastMaxY) {
|
||||
// 存在重叠
|
||||
const addLen = Math.min(this.squeezeSpeed * dt * (children.length - index - 1), lastMaxY - currMinY);
|
||||
curr.setPosition(curr.position.x, curr.position.y + addLen, curr.position.z);
|
||||
const winSize = view.getVisibleSize();
|
||||
if (currMinY > winSize.height / 2) {
|
||||
// 触发回收逻辑
|
||||
recovery = true;
|
||||
Tween.stopAllByTarget(curr.getComponent(UIOpacity));
|
||||
this.pool.put(curr);
|
||||
}
|
||||
} else if (currMinY > lastMaxY) {
|
||||
// 存在空隙
|
||||
const addLen = Math.max(-this.squeezeSpeed * dt * (children.length - index), lastMaxY - currMinY);
|
||||
curr.setPosition(curr.position.x, curr.position.y + addLen, curr.position.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
extensions/app/assets/manager/ui/comp/UIMgrToast.ts.meta
Normal file
9
extensions/app/assets/manager/ui/comp/UIMgrToast.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "cde1528c-f66c-40f4-bdd2-27bf138ce1df",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
26
extensions/app/assets/manager/ui/comp/UIMgrToastCell.ts
Normal file
26
extensions/app/assets/manager/ui/comp/UIMgrToastCell.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Component, Label, UIOpacity, UITransform, _decorator } from 'cc';
|
||||
const { ccclass, property, requireComponent } = _decorator;
|
||||
|
||||
@ccclass('UIMgrToastCell')
|
||||
@requireComponent(UIOpacity)
|
||||
@requireComponent(UITransform)
|
||||
export default class UIMgrToastCell extends Component {
|
||||
@property(Label)
|
||||
private title: Label = null;
|
||||
|
||||
init(title: string) {
|
||||
if (title.split('\n').find((v) => v.length > 30)) {
|
||||
this.title.overflow = Label.Overflow.RESIZE_HEIGHT;
|
||||
this.title.getComponent(UITransform).width = 600;
|
||||
} else {
|
||||
this.title.overflow = Label.Overflow.NONE;
|
||||
}
|
||||
this.title.string = title;
|
||||
this.title.updateRenderData(true);
|
||||
}
|
||||
|
||||
unuse() {
|
||||
this.title.string = '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "6cc631d6-b08e-4ee3-8bde-e307c4288734",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
68
extensions/app/assets/manager/ui/comp/UIMgrZOrder.ts
Normal file
68
extensions/app/assets/manager/ui/comp/UIMgrZOrder.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { _decorator, Component, Director, director, Node } from 'cc';
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
@ccclass('UIMgrZOrder')
|
||||
export default class UIMgrZOrder extends Component {
|
||||
private zOrder = false;
|
||||
private tempArr: Node[] = [];
|
||||
|
||||
protected onLoad() {
|
||||
this.checkUpdateZOrder();
|
||||
this.node.on(Node.EventType.CHILD_ADDED, this.onChildAdded, this);
|
||||
this.node.on(Node.EventType.CHILD_REMOVED, this.onChildRemoveed, this);
|
||||
if (Node.EventType.CHILDREN_ORDER_CHANGED) {
|
||||
this.node.on(Node.EventType.CHILDREN_ORDER_CHANGED, this.checkUpdateZOrder, this);
|
||||
} else {
|
||||
this.node.on(Node.EventType.SIBLING_ORDER_CHANGED, this.checkUpdateZOrder, this);
|
||||
}
|
||||
}
|
||||
|
||||
protected onDestroy() {
|
||||
director.off(Director.EVENT_AFTER_UPDATE, this.updateZOrder, this);
|
||||
this.node.off(Node.EventType.CHILD_ADDED, this.onChildAdded, this);
|
||||
this.node.off(Node.EventType.CHILD_REMOVED, this.onChildRemoveed, this);
|
||||
if (Node.EventType.CHILDREN_ORDER_CHANGED) {
|
||||
this.node.off(Node.EventType.CHILDREN_ORDER_CHANGED, this.checkUpdateZOrder, this);
|
||||
} else {
|
||||
this.node.off(Node.EventType.SIBLING_ORDER_CHANGED, this.checkUpdateZOrder, this);
|
||||
}
|
||||
}
|
||||
|
||||
private onChildAdded(child: Node) {
|
||||
this.checkUpdateZOrder();
|
||||
child.on(Node.EventType.TRANSFORM_CHANGED, this.checkUpdateZOrder, this);
|
||||
}
|
||||
|
||||
private onChildRemoveed(child: Node) {
|
||||
child.off(Node.EventType.TRANSFORM_CHANGED, this.checkUpdateZOrder, this);
|
||||
}
|
||||
|
||||
private checkUpdateZOrder() {
|
||||
if (this.zOrder) return;
|
||||
this.zOrder = true;
|
||||
director.once(Director.EVENT_AFTER_UPDATE, this.updateZOrder, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新节点树排序
|
||||
*/
|
||||
public updateZOrder() {
|
||||
if (!this.zOrder) return;
|
||||
Array.prototype.push.apply(this.tempArr, this.node.children);
|
||||
this.tempArr
|
||||
.sort((a, b) => {
|
||||
return (a.position.z - b.position.z)
|
||||
|| (a.getSiblingIndex() - b.getSiblingIndex());
|
||||
})
|
||||
.forEach((child, index) => {
|
||||
child.setSiblingIndex(index);
|
||||
});
|
||||
|
||||
// 一定要放到最后再设置false,
|
||||
// 避免更新过程中设置siblingIndex,
|
||||
// 导致无限重复调用
|
||||
this.zOrder = false;
|
||||
this.tempArr.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "ad5cb510-639e-40c2-acdd-399ad00629b9",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
9
extensions/app/assets/manager/ui/effect.meta
Normal file
9
extensions/app/assets/manager/ui/effect.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "e3ac29f9-11a4-461c-a404-9e478271b953",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
119
extensions/app/assets/manager/ui/effect/blur.effect
Normal file
119
extensions/app/assets/manager/ui/effect/blur.effect
Normal file
@@ -0,0 +1,119 @@
|
||||
CCEffect %{
|
||||
techniques:
|
||||
- name: opaque
|
||||
passes:
|
||||
- vert: vs:vert
|
||||
frag: fs:frag
|
||||
depthStencilState:
|
||||
depthTest: false
|
||||
depthWrite: false
|
||||
blendState:
|
||||
targets:
|
||||
- blend: true
|
||||
blendSrc: src_alpha
|
||||
blendSrcAlpha: src_alpha
|
||||
blendDst: one_minus_src_alpha
|
||||
blendDstAlpha: one_minus_src_alpha
|
||||
rasterizerState:
|
||||
cullMode: none
|
||||
properties:
|
||||
blurSize: { value: [750, 1334] }
|
||||
blurriness: { value: 1, editor: {range:[0, 1, 0.01], slide: true} }
|
||||
blurLevel: { value: 1, editor: {range:[1, 3, 1], slide: true} }
|
||||
|
||||
}%
|
||||
|
||||
CCProgram vs %{
|
||||
precision highp float;
|
||||
#include <cc-global>
|
||||
|
||||
in vec4 a_position;
|
||||
in vec2 a_texCoord;
|
||||
out vec2 v_texCoord;
|
||||
|
||||
vec4 vert() {
|
||||
vec4 pos = cc_matViewProj * a_position;
|
||||
v_texCoord = a_texCoord;
|
||||
return pos;
|
||||
}
|
||||
}%
|
||||
|
||||
CCProgram fs %{
|
||||
precision highp float;
|
||||
|
||||
in vec2 v_texCoord;
|
||||
#pragma builtin(local)
|
||||
layout(set = 2, binding = 10) uniform sampler2D cc_spriteTexture;
|
||||
|
||||
uniform Constant {
|
||||
vec2 blurSize;
|
||||
float blurriness;
|
||||
float blurLevel;
|
||||
};
|
||||
|
||||
// 模糊处理函数
|
||||
vec4 blur (vec2 pos) {
|
||||
float sum = 0.0;
|
||||
vec4 color = vec4(0);
|
||||
|
||||
if (blurLevel == 1.0) {
|
||||
const float blurRadius = 10.0;
|
||||
const float blurStep = 1.0;
|
||||
// 采样周边像素并求出加权平均值,得到最终的像素值
|
||||
for (float rx = -blurRadius; rx <= blurRadius; rx += blurStep) {
|
||||
for (float ry = -blurRadius; ry <= blurRadius; ry += blurStep) {
|
||||
vec2 target = pos + vec2(rx / blurSize[0], ry / blurSize[1]);
|
||||
float weight = (blurRadius - abs(rx)) * (blurRadius - abs(ry));
|
||||
target.x = clamp(target.x, 0.0, 1.0);
|
||||
target.y = clamp(target.y, 0.0, 1.0);
|
||||
color += texture(cc_spriteTexture, target) * weight;
|
||||
sum += weight;
|
||||
}
|
||||
}
|
||||
} else if(blurLevel == 2.0) {
|
||||
const float blurRadius = 20.0;
|
||||
const float blurStep = 2.0;
|
||||
// 采样周边像素并求出加权平均值,得到最终的像素值
|
||||
for (float rx = -blurRadius; rx <= blurRadius; rx += blurStep) {
|
||||
for (float ry = -blurRadius; ry <= blurRadius; ry += blurStep) {
|
||||
vec2 target = pos + vec2(rx / blurSize[0], ry / blurSize[1]);
|
||||
float weight = (blurRadius - abs(rx)) * (blurRadius - abs(ry));
|
||||
target.x = clamp(target.x, 0.0, 1.0);
|
||||
target.y = clamp(target.y, 0.0, 1.0);
|
||||
color += texture(cc_spriteTexture, target) * weight;
|
||||
sum += weight;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const float blurRadius = 30.0;
|
||||
const float blurStep = 3.0;
|
||||
// 采样周边像素并求出加权平均值,得到最终的像素值
|
||||
for (float rx = -blurRadius; rx <= blurRadius; rx += blurStep) {
|
||||
for (float ry = -blurRadius; ry <= blurRadius; ry += blurStep) {
|
||||
vec2 target = pos + vec2(rx / blurSize[0], ry / blurSize[1]);
|
||||
float weight = (blurRadius - abs(rx)) * (blurRadius - abs(ry));
|
||||
target.x = clamp(target.x, 0.0, 1.0);
|
||||
target.y = clamp(target.y, 0.0, 1.0);
|
||||
color += texture(cc_spriteTexture, target) * weight;
|
||||
sum += weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color /= sum;
|
||||
return color;
|
||||
}
|
||||
|
||||
vec4 frag () {
|
||||
// 获取纹理像素颜色
|
||||
vec4 o = vec4(1, 1, 1, 1);
|
||||
o *= texture(cc_spriteTexture, v_texCoord);
|
||||
|
||||
// 执行模糊逻辑
|
||||
vec4 color = blur(v_texCoord);
|
||||
color.a = o.a;
|
||||
o = o + (color-o) * blurriness;
|
||||
|
||||
return o;
|
||||
}
|
||||
}%
|
||||
11
extensions/app/assets/manager/ui/effect/blur.effect.meta
Normal file
11
extensions/app/assets/manager/ui/effect/blur.effect.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "1.7.1",
|
||||
"importer": "effect",
|
||||
"imported": true,
|
||||
"uuid": "b5376e0b-9e5d-4c91-b0a4-19448fd39179",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
36
extensions/app/assets/manager/ui/effect/blur.mtl
Normal file
36
extensions/app/assets/manager/ui/effect/blur.mtl
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"__type__": "cc.Material",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"_effectAsset": {
|
||||
"__uuid__": "b5376e0b-9e5d-4c91-b0a4-19448fd39179",
|
||||
"__expectedType__": "cc.EffectAsset"
|
||||
},
|
||||
"_techIdx": 0,
|
||||
"_defines": [
|
||||
{}
|
||||
],
|
||||
"_states": [
|
||||
{
|
||||
"rasterizerState": {},
|
||||
"depthStencilState": {},
|
||||
"blendState": {
|
||||
"targets": [
|
||||
{}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"_props": [
|
||||
{
|
||||
"textureSize": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 750,
|
||||
"y": 1334
|
||||
},
|
||||
"blurDegree": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
11
extensions/app/assets/manager/ui/effect/blur.mtl.meta
Normal file
11
extensions/app/assets/manager/ui/effect/blur.mtl.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "1.0.21",
|
||||
"importer": "material",
|
||||
"imported": true,
|
||||
"uuid": "a313b5eb-b939-4c44-affc-32c713440cea",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
12
extensions/app/assets/manager/ui/prefab.meta
Normal file
12
extensions/app/assets/manager/ui/prefab.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "a32ee073-dc9a-4d81-b3c1-54283a151627",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
508
extensions/app/assets/manager/ui/prefab/UIMgrLoading.prefab
Normal file
508
extensions/app/assets/manager/ui/prefab/UIMgrLoading.prefab
Normal file
@@ -0,0 +1,508 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "UIMgrLoading",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "UIMgrLoading",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 16
|
||||
},
|
||||
{
|
||||
"__id__": 18
|
||||
},
|
||||
{
|
||||
"__id__": 20
|
||||
},
|
||||
{
|
||||
"__id__": 22
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 24
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 33554432,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "com_loading",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
},
|
||||
{
|
||||
"__id__": 7
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 33554432,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 1600.0000000000002,
|
||||
"height": 2300
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "c73aXf03tEr6Ib/PoiFOWn"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "e9d91e97-574a-4fc4-925f-14bf8dd2d82c@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 1,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 0,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": null,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "cagPaWVyVAfZ9zrBMCGK2P"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Widget",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 8
|
||||
},
|
||||
"_alignFlags": 45,
|
||||
"_target": null,
|
||||
"_left": 0,
|
||||
"_right": 0,
|
||||
"_top": 0,
|
||||
"_bottom": 0,
|
||||
"_horizontalCenter": 0,
|
||||
"_verticalCenter": 0,
|
||||
"_isAbsLeft": true,
|
||||
"_isAbsRight": true,
|
||||
"_isAbsTop": true,
|
||||
"_isAbsBottom": true,
|
||||
"_isAbsHorizontalCenter": true,
|
||||
"_isAbsVerticalCenter": true,
|
||||
"_originalWidth": 68,
|
||||
"_originalHeight": 2160,
|
||||
"_alignMode": 2,
|
||||
"_lockFlags": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "9eIX1qF4BFhZE6ShzpnuQF"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "57nQao2ydNi6bfaoHEW7aP",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "loading",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 11
|
||||
},
|
||||
{
|
||||
"__id__": 13
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 15
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 33554432,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 10
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 12
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 0,
|
||||
"height": 0
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "93a3D7kcNLmKSzPl3DUiom"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Graphics",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 10
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 14
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_lineWidth": 8,
|
||||
"_strokeColor": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 247,
|
||||
"g": 155,
|
||||
"b": 17,
|
||||
"a": 255
|
||||
},
|
||||
"_lineJoin": 2,
|
||||
"_lineCap": 1,
|
||||
"_fillColor": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 247,
|
||||
"g": 155,
|
||||
"b": 17,
|
||||
"a": 255
|
||||
},
|
||||
"_miterLimit": 10,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "8cV7EpeTlFy50kiDgQAuVw"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "f1BL1+S9ZHKLm/dYTzeaLU",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 1600.0000000000002,
|
||||
"height": 2300
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "3brXLn6dBA1Zn+gXeG0T+V"
|
||||
},
|
||||
{
|
||||
"__type__": "4a8e5aXrnhKSo0IF6zGgjon",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 19
|
||||
},
|
||||
"loading": {
|
||||
"__id__": 10
|
||||
},
|
||||
"size": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 60,
|
||||
"height": 60
|
||||
},
|
||||
"delay": 1,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "07sEXIwyZEKI/ruuFHnBcu"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Widget",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 21
|
||||
},
|
||||
"_alignFlags": 45,
|
||||
"_target": null,
|
||||
"_left": 0,
|
||||
"_right": 0,
|
||||
"_top": 0,
|
||||
"_bottom": 0,
|
||||
"_horizontalCenter": 0,
|
||||
"_verticalCenter": 0,
|
||||
"_isAbsLeft": true,
|
||||
"_isAbsRight": true,
|
||||
"_isAbsTop": true,
|
||||
"_isAbsBottom": true,
|
||||
"_isAbsHorizontalCenter": true,
|
||||
"_isAbsVerticalCenter": true,
|
||||
"_originalWidth": 100,
|
||||
"_originalHeight": 100,
|
||||
"_alignMode": 2,
|
||||
"_lockFlags": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "1en2H6LWVAnbHn1Id9zrSK"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.BlockInputEvents",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 23
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "23+7We+IdLxIZlOVikvEPE"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "daRB56CWxIRa37BwtucqYI",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "fe542035-b018-493e-bea8-084fe4e01905",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "UIMgrLoading"
|
||||
}
|
||||
}
|
||||
262
extensions/app/assets/manager/ui/prefab/UIMgrShade.prefab
Normal file
262
extensions/app/assets/manager/ui/prefab/UIMgrShade.prefab
Normal file
@@ -0,0 +1,262 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "UIMgrShade",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "UIMgrShade",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 2
|
||||
},
|
||||
{
|
||||
"__id__": 4
|
||||
},
|
||||
{
|
||||
"__id__": 6
|
||||
},
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
{
|
||||
"__id__": 12
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 14
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 33554432,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 3
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 750,
|
||||
"height": 1334
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "7dX6ImdfBJcqlTYmg63cOq"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UIOpacity",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 5
|
||||
},
|
||||
"_opacity": 255,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "79YFX8QOxIqrfZHGmtwiec"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "5512993f-89ea-46fe-b788-0ecc0c2cd51c@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 0,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": null,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "22F/XyC25NNbnQRqf69Fet"
|
||||
},
|
||||
{
|
||||
"__type__": "d02952bsB1JPJ4xXteObDOr",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"blurMaterial": {
|
||||
"__uuid__": "a313b5eb-b939-4c44-affc-32c713440cea",
|
||||
"__expectedType__": "cc.Material"
|
||||
},
|
||||
"shadeFrame": {
|
||||
"__uuid__": "5512993f-89ea-46fe-b788-0ecc0c2cd51c@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_delay": 0,
|
||||
"_begin": 60,
|
||||
"_end": 180,
|
||||
"_speed": 100,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "cfGNW3HSFJZ5C0B0FM1PtW"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Widget",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"_alignFlags": 45,
|
||||
"_target": null,
|
||||
"_left": 0,
|
||||
"_right": 0,
|
||||
"_top": 0,
|
||||
"_bottom": 0,
|
||||
"_horizontalCenter": 0,
|
||||
"_verticalCenter": 0,
|
||||
"_isAbsLeft": true,
|
||||
"_isAbsRight": true,
|
||||
"_isAbsTop": true,
|
||||
"_isAbsBottom": true,
|
||||
"_isAbsHorizontalCenter": true,
|
||||
"_isAbsVerticalCenter": true,
|
||||
"_originalWidth": 750,
|
||||
"_originalHeight": 1334,
|
||||
"_alignMode": 2,
|
||||
"_lockFlags": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "229/N15SBGH4HGma+cYqM5"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.BlockInputEvents",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "82DtXS4ChDkLThlpVA53IU"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "61DZmvnnRICYRzOyDnyCeu",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "000cee21-922c-4fcd-bd39-6f80ac2436a4",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "UIMgrShade"
|
||||
}
|
||||
}
|
||||
93
extensions/app/assets/manager/ui/prefab/UIMgrToast.prefab
Normal file
93
extensions/app/assets/manager/ui/prefab/UIMgrToast.prefab
Normal file
@@ -0,0 +1,93 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "UIMgrToast",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "UIMgrToast",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 33554432,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cde15KM9mxA9L3SJ78TjOHf",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 3
|
||||
},
|
||||
"cell": {
|
||||
"__uuid__": "5aa9450d-6710-4fac-a82c-ee2939cf9411",
|
||||
"__expectedType__": "cc.Prefab"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "3aBOvgZTJM7o51sRqaqstL"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "7cYDN4HdROmJQ7qqaDYftE",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "b2a00c44-d199-4031-8fa7-ea681618b9d4",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "UIMgrToast"
|
||||
}
|
||||
}
|
||||
438
extensions/app/assets/manager/ui/prefab/UIMgrToastCell.prefab
Normal file
438
extensions/app/assets/manager/ui/prefab/UIMgrToastCell.prefab
Normal file
@@ -0,0 +1,438 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "UIMgrToastCell",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "UIMgrToastCell",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
{
|
||||
"__id__": 12
|
||||
},
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
},
|
||||
{
|
||||
"__id__": 18
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 20
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 33554432,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "title",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 3.552713678800501e-15,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 33554432,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 0,
|
||||
"height": 50.4
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "18U2eEmv1MSrJ3Kp8ChYCB"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Label",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_string": "",
|
||||
"_horizontalAlign": 1,
|
||||
"_verticalAlign": 1,
|
||||
"_actualFontSize": 30,
|
||||
"_fontSize": 30,
|
||||
"_fontFamily": "Arial",
|
||||
"_lineHeight": 40,
|
||||
"_overflow": 0,
|
||||
"_enableWrapText": true,
|
||||
"_font": null,
|
||||
"_isSystemFontUsed": true,
|
||||
"_spacingX": 0,
|
||||
"_isItalic": false,
|
||||
"_isBold": false,
|
||||
"_isUnderline": false,
|
||||
"_underlineHeight": 2,
|
||||
"_cacheMode": 0,
|
||||
"_enableOutline": false,
|
||||
"_outlineColor": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 0,
|
||||
"g": 0,
|
||||
"b": 0,
|
||||
"a": 255
|
||||
},
|
||||
"_outlineWidth": 2,
|
||||
"_enableShadow": false,
|
||||
"_shadowColor": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 0,
|
||||
"g": 0,
|
||||
"b": 0,
|
||||
"a": 255
|
||||
},
|
||||
"_shadowOffset": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 2,
|
||||
"y": 2
|
||||
},
|
||||
"_shadowBlur": 2,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "fcBV+kyJJKoLa0miLOwB0D"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "17FiiUdlpA7INUb+CSPtXV",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 80,
|
||||
"height": 70.4
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "7bhW+VfdhFIZBU1YlNH87w"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UIOpacity",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"_opacity": 255,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "85IfdKUvlDVobXX10lMYgh"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "7922bee9-7eb5-449b-884e-14ac57ae515c@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 1,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 0,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": null,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "d5KZg62HRMhqP9FpXXBvrN"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Layout",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 15
|
||||
},
|
||||
"_resizeMode": 1,
|
||||
"_layoutType": 1,
|
||||
"_cellSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 40,
|
||||
"height": 40
|
||||
},
|
||||
"_startAxis": 0,
|
||||
"_paddingLeft": 40,
|
||||
"_paddingRight": 40,
|
||||
"_paddingTop": 10,
|
||||
"_paddingBottom": 10,
|
||||
"_spacingX": 0,
|
||||
"_spacingY": 0,
|
||||
"_verticalDirection": 1,
|
||||
"_horizontalDirection": 0,
|
||||
"_constraint": 0,
|
||||
"_constraintNum": 1,
|
||||
"_affectedByScale": false,
|
||||
"_isAlign": false,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "e0RO5Tg+lAWq8B14qLuRXc"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Layout",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"_resizeMode": 1,
|
||||
"_layoutType": 2,
|
||||
"_cellSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 40,
|
||||
"height": 40
|
||||
},
|
||||
"_startAxis": 0,
|
||||
"_paddingLeft": 0,
|
||||
"_paddingRight": 0,
|
||||
"_paddingTop": 10,
|
||||
"_paddingBottom": 10,
|
||||
"_spacingX": 0,
|
||||
"_spacingY": 0,
|
||||
"_verticalDirection": 1,
|
||||
"_horizontalDirection": 0,
|
||||
"_constraint": 0,
|
||||
"_constraintNum": 2,
|
||||
"_affectedByScale": false,
|
||||
"_isAlign": false,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "b4w/hORDRDYLZbEx1j4ybh"
|
||||
},
|
||||
{
|
||||
"__type__": "6cc63HWsI5O44ve4wfEKIc0",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 19
|
||||
},
|
||||
"title": {
|
||||
"__id__": 5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "a82bayEVpB1YYG3zm38T3l"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "5aa9450d-6710-4fac-a82c-ee2939cf9411",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "UIMgrToastCell"
|
||||
}
|
||||
}
|
||||
12
extensions/app/assets/manager/ui/texture.meta
Normal file
12
extensions/app/assets/manager/ui/texture.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "f5f10a35-ca33-4eb2-81f1-52459832001b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
BIN
extensions/app/assets/manager/ui/texture/singleColor.png
Normal file
BIN
extensions/app/assets/manager/ui/texture/singleColor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 84 B |
134
extensions/app/assets/manager/ui/texture/singleColor.png.meta
Normal file
134
extensions/app/assets/manager/ui/texture/singleColor.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "5512993f-89ea-46fe-b788-0ecc0c2cd51c",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "5512993f-89ea-46fe-b788-0ecc0c2cd51c@6c48a",
|
||||
"displayName": "singleColor",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "5512993f-89ea-46fe-b788-0ecc0c2cd51c",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "5512993f-89ea-46fe-b788-0ecc0c2cd51c@f9941",
|
||||
"displayName": "singleColor",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 2,
|
||||
"height": 2,
|
||||
"rawWidth": 2,
|
||||
"rawHeight": 2,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-1,
|
||||
-1,
|
||||
0,
|
||||
1,
|
||||
-1,
|
||||
0,
|
||||
-1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-1,
|
||||
-1,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
1,
|
||||
1,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "5512993f-89ea-46fe-b788-0ecc0c2cd51c@6c48a",
|
||||
"atlasUuid": ""
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": true,
|
||||
"hasAlpha": false,
|
||||
"redirect": "5512993f-89ea-46fe-b788-0ecc0c2cd51c@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
extensions/app/assets/manager/ui/texture/toastBox.png
Normal file
BIN
extensions/app/assets/manager/ui/texture/toastBox.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
134
extensions/app/assets/manager/ui/texture/toastBox.png.meta
Normal file
134
extensions/app/assets/manager/ui/texture/toastBox.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "b250e49c-5d79-4d07-a85f-a980fca36170",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "b250e49c-5d79-4d07-a85f-a980fca36170@6c48a",
|
||||
"displayName": "toastBox",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "b250e49c-5d79-4d07-a85f-a980fca36170",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "b250e49c-5d79-4d07-a85f-a980fca36170@f9941",
|
||||
"displayName": "toastBox",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 288,
|
||||
"height": 78,
|
||||
"rawWidth": 288,
|
||||
"rawHeight": 78,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 40,
|
||||
"borderRight": 40,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-144,
|
||||
-39,
|
||||
0,
|
||||
144,
|
||||
-39,
|
||||
0,
|
||||
-144,
|
||||
39,
|
||||
0,
|
||||
144,
|
||||
39,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
78,
|
||||
288,
|
||||
78,
|
||||
0,
|
||||
0,
|
||||
288,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-144,
|
||||
-39,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
144,
|
||||
39,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "b250e49c-5d79-4d07-a85f-a980fca36170@6c48a",
|
||||
"atlasUuid": ""
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": true,
|
||||
"hasAlpha": true,
|
||||
"redirect": "b250e49c-5d79-4d07-a85f-a980fca36170@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
extensions/app/assets/manager/ui/texture/toastCell.png
Normal file
BIN
extensions/app/assets/manager/ui/texture/toastCell.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 629 B |
134
extensions/app/assets/manager/ui/texture/toastCell.png.meta
Normal file
134
extensions/app/assets/manager/ui/texture/toastCell.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "7922bee9-7eb5-449b-884e-14ac57ae515c",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "7922bee9-7eb5-449b-884e-14ac57ae515c@6c48a",
|
||||
"displayName": "toastCell",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "7922bee9-7eb5-449b-884e-14ac57ae515c",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "7922bee9-7eb5-449b-884e-14ac57ae515c@f9941",
|
||||
"displayName": "toastCell",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 100,
|
||||
"height": 100,
|
||||
"rawWidth": 100,
|
||||
"rawHeight": 100,
|
||||
"borderTop": 15,
|
||||
"borderBottom": 15,
|
||||
"borderLeft": 15,
|
||||
"borderRight": 15,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-50,
|
||||
-50,
|
||||
0,
|
||||
50,
|
||||
-50,
|
||||
0,
|
||||
-50,
|
||||
50,
|
||||
0,
|
||||
50,
|
||||
50,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
100,
|
||||
100,
|
||||
100,
|
||||
0,
|
||||
0,
|
||||
100,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-50,
|
||||
-50,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
50,
|
||||
50,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "7922bee9-7eb5-449b-884e-14ac57ae515c@6c48a",
|
||||
"atlasUuid": ""
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"hasAlpha": true,
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"redirect": "7922bee9-7eb5-449b-884e-14ac57ae515c@6c48a"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user