[CocosCreator]自定义事件(订阅/发布)管理器

        欢迎喜欢或者从事CocosCreator开发的小伙伴请加入我的大家庭CocosCreator游戏开发Q群:26855530

        首先,小伙伴应该知道CocosCreator本身已经实现了自己一套事件传播机制的,例如:

this.node.on('foobar', this._sayHello, this);
cc.game.on("foobar", this._sayHello, this);
cc.director.on("foobar", this._sayHello, this);

以上三种都是可以提供给开发者自行使用的事件传播,其中第一种是使用上是比较平凡的,而且有一定的局限性,只能在同一树节点,由下往上传播,后两者是基于全局的cc.game或者cc.director,基本可以做到无死角传播,但是官方并不提倡使用.


        综上所诉,就没有一个能吊打全部的事件管理器吗?答案还真没有,哈哈~既然没有,那我们就自己撸一个,造个轮子!所以以下的代码就诞生了......

import Singleton from "../base/Singleton";
import SysLog from "../utils/SysLog";
import GamePublicUtil from "../utils/GamePublicUtil";

interface IEvent {
    func: Function,
    ctx: unknown
}

/**
 * 事件(订阅/发布)管理器
 */
export default class EventManager extends Singleton {

    private _eventMap: Map<string, Array<IEvent>> = new Map<string, Array<IEvent>>();

    role: cc.Node;
    private _timer;
    private _duration: number = 2000;

    private _delayMap: Map<string, any> = new Map();

    constructor() {
        super();
        this._delayMap.clear();
    }

    static get instance() {
        return super.getInstance<EventManager>();
    }

    on(eventName: string, func: Function, ctx: unknown) {
        if (this._eventMap.has(eventName)) {
            this._eventMap.get(eventName).push({func, ctx});
        } else {
            this._eventMap.set(eventName, [{func, ctx}]);
        }
    }

    off(eventName: string, func: Function, ctx: unknown) {
        if (this._eventMap.has(eventName)) {
            let events = this._eventMap.get(eventName);
            let index = events.findIndex(i => i.func === func && i.ctx === ctx);
            index > -1 && events.splice(index, 1);
            if (events.length == 0) {
                this._eventMap.delete(eventName);
            }
        } else {
            SysLog.warn(`事件解绑失败:事件名(${eventName})不存在`);
        }
    }

    emit(eventName: string, detail?: any) {
        if (this._eventMap.has(eventName)) {
            this._eventMap.get(eventName).forEach(({func, ctx}) => {
                typeof detail === "undefined" ? func.call(ctx) : func.call(ctx, detail);
            })
        }
    }

    /**
     * 延迟事件通知
     * @param eventName
     * @param detail
     */
    delayEmit(eventName: string, detail?: any) {
        if (this._delayMap) {
            this._delayMap.set(eventName, detail);
            if (!this._timer) {
                this._timer = setInterval(this.delayEvent.bind(this), this._duration);
            }
        }
    }

    private delayEvent() {
        if (this.role && !GamePublicUtil.getIsTalkingNpc() && this._delayMap.size > 0) {
            let next = this._delayMap.entries().next();
            if (next) {
                let key: string = next.value[0];
                let val = next.value[1];
                this.emit(key, val);
                this._delayMap.delete(key);
            }

            if (this._delayMap.size === 0 && this._timer) {
                //没有延迟事件停止轮询
                clearInterval(this._timer);
                this._timer = null;
            }
        }
    }

    clear() {
        this._delayMap.clear();
        this._eventMap.clear();
    }

    log() {
        if (this._eventMap.size > 0) {
            this._eventMap.forEach((events, eventName) => {
                SysLog.info(`事件名:${eventName}`);
                SysLog.info(`   订阅者信息:`);
                events.forEach((event) => {
                    // @ts-ignore
                    SysLog.info(`           ${event.ctx.node.name}`);
                })
            })
        } else {
            SysLog.info(`事件管理器暂无任何信息`);
        }
    }
}

代码很简单,我就不多阐述,一些类也是我自己封装的,大佬们需要的话可以看我往期的文章,或者直接删除吧,其中有一个还是我自己根据我项目需求做的一个延时事件处理,根据自身需求修改或删除.下面看使用:


监听(订阅):

import EventManager from "../manager/EventManager";

const {ccclass, property} = cc._decorator;

@ccclass
export default class NewClass extends cc.Component {

    onEnable() {
        EventManager.instance.on("sayHI", this.sayHI, this);
    }

    private sayHI(mes) {
        console.log(mes);
    }

    onDisable() {
        EventManager.instance.off("sayHI", this.sayHI, this);
    }
}

触发(发布):

EventManager.instance.emit("sayHI", "hi~");

需要注意的是,这个也是个全局变量,到处都能使用,但是订阅方应该注意在适当时机,比如以上代码在onDisable生命周期取消订阅,避免造成不必要的内存泄露问题等~好的,期待下期的更新吧~

猜你喜欢

转载自blog.csdn.net/qq183293/article/details/132402091
今日推荐