天天看點

cocos creator主程入門教程(六)—— 消息分發

五邑隐俠,本名關健昌,10年遊戲生涯,現隐居五邑。本系列文章以TypeScript為介紹語言。

本篇開始介紹遊戲業務架構相關的内容。在遊戲業務層,所有需要隔離的系統和子產品間通信都可以通過消息分發解耦。例如網絡傳回通知、資料更新同步到界面等。

消息分發基于觀察者模式設計。需要處理消息的地方向消息中心注冊監聽回調,派發消息時,調用消息中心的派發接口周遊該消息的監聽隊列,調用對應的回調方法。

先定義監聽回調類型

/**
 * 消息監聽回調方法
 */
export type NotifyListener = (src: any, data: any) => void;
      

  

通過key-value方式儲存監聽隊列

private static msg2listDict: Dictionary< string, Array<NotifyListenerInfo> > = new Dictionary< string, Array<NotifyListenerInfo> >();
      

接口定義

/**
     * 添加多次監聽者,需要手動移除
     * @param msg 
     * @param listener 
     * @param target 
     */
    public static addListener(msg: string, listener: NotifyListener, target?: any): void {}

    /**
     * 添加單次監聽者,事件觸發後即移除
     * @param msg 
     * @param listener 
     * @param target 
     */
    public static addOnceListener(msg: string, listener: NotifyListener, target?: any): void {}

    /**
     * 移除指定消息指定的監聽者
     * @param msg 
     * @param listener 
     */
    public static removeMsgListener(msg: string, listener: NotifyListener): void {}

    /**
     * 移除指定消息所有監聽者
     * @param msg 
     */
    public static removeMsgAllListeners(msg: string): void {}

    /**
     * 移除指定目标對指定消息的監聽
     * @param msg 
     * @param target 
     */
    public static removeTargetMsgListen(msg: string, target: any): void {}

    /**
     * 移除指定目标所有消息監聽
     * @param target 
     */
    public static removeTargetAllMsgListen(target: any): void {}

    /**
     * 派發消息
     * @param msg 
     * @param src 
     * @param data 
     */
    public static notify(msg: string, src: any, data: any): void {}
      

在添加移除實作中,需要注意某消息可能正在派發。

對于一個消息新添加的監聽者,應該在目前隊列消息派發完後再派發,是以,添加一個待添加隊列

private static listener2add: Array<NotifyListenerInfo> = [];
      

在添加監聽者時做以下判斷

// 該消息正在派發,放入待添加隊列
        if (NotifyCenter.notifyMsgs.indexOf(msg) >= 0) {
            NotifyCenter.listener2add.push(info);
            return;
        }
      

同樣在移除監聽者時,可能正在派發消息,避免對隊列的修改導緻for循環異常,添加一個待移除隊列,派發消息時,如果該監聽者在移除隊列則不派發。在消息派發完後再将其移出隊列

private static listener2remove: Array<NotifyListenerInfo> = [];
      

在移除監聽者時做以下判斷

// 該消息正在派發,放入待移除隊列
                if (NotifyCenter.notifyMsgs.indexOf(msg) >= 0) {
                    NotifyCenter.listener2remove.push(list[i]);
                } else {
                    list.splice(i, 1);
                }
      

派發消息時周遊指定消息下的隊列

// 隊列不存在,不需要處理
        let list = NotifyCenter.msg2listDict.get(msg);
        if (!list) {
            return;
        }

        // 标記消息正在派發,多個消息可能同時在派發,同一消息可能标記多次
        NotifyCenter.notifyMsgs.push(msg);

        // 處理消息派發
        for (let i = 0, n = list.length; i < n; i++) {
            NotifyCenter._dispatch(list[i], src, data, false);
        }
      

派發消息時先判斷是否在移除隊列

// 在移除隊列,不派發
        if (NotifyCenter.listener2remove.indexOf(info) >= 0) {
            return;
        }
      

目前隊列派發完後檢查待添加隊列

// 處理待添加隊列派發
        for (let i = 0, n = msg2add.length; i < n; i++) {
            if (listener2add[i].msg == msg) {
                NotifyCenter._dispatch(listener2add[i], src, data, true);
            }
        }
      

引入消息分發中心,隔離的系統、子產品間通過消息監聽和派發通信,避免互相引用耦合。

消息分發先說到這裡,下一篇我們将介紹遊戲業務層架構。

繼續閱讀