Event Management in Game--Interpretation of Observer Mode

Preface:

Recently, I browsed an article on the Internet about an overview of software design patterns, which is relatively clear and understandable.
http://c.biancheng.net/view/1317.html

1. Introduction of observer mode

In the real world, many objects do not exist independently, and a change in the behavior of one or more objects may lead to a change in the behavior of one or more other objects, such as: a red light stops; a bell rings at the end of a class and all students leave the classroom to move freely, and so on.

The same is true in the software world, for example, the relationship between data in Excel and polyline, pie and column charts; the relationship between models and views in MVC mode; the event source and event handler in event model. All of these are very convenient to implement in observer mode.
Definition and characteristics of patterns

Definition of Observer mode: A one-to-many dependency among multiple objects in which all dependent objects are notified and automatically updated when the state of an object changes. This mode is sometimes referred to as Publish-Subscribe mode, Model-View mode, and it is an object behavior mode.

The observer mode is an object behavior mode with the following main advantages.
Decreases the coupling between the target and the observer, which is abstract. Compliant with the principle of inversion of dependency.
A trigger mechanism is established between the target and the observer.

Its main drawbacks are as follows.
The dependency between the target and the observer is not completely broken, and circular references may occur.
When there are many observer objects, the publication of notifications takes a lot of time, which affects the efficiency of the program.

2. Observer Simple Code

We use code abstraction to understand the observer's principles and the shortcomings described above:

abstract class Subject {
    protected observers: Observer[] = [];
    //Increase Observer Method
    public add(observer: Observer): void{
        this.observers.push(observer);
    }

    //Delete Observer Method
    public remove(observer: Observer): void {
        this.observers.pop()// Simple use of pseudocode for deletion
    }
    public abstract notifyObserver(): any; //Notify Observer Method
}

//Specific objectives
class ConcreteSubject extends Subject {
    public notifyObserver(): void{
        this.observers.forEach((obs) => {
            obs.response();
        });
    }
}
//Abstract observer
interface Observer {
    response(): any; //reaction
}
//Specific Observer 1
class ConcreteObserver1 implements Observer {
    public response(): void{
        cc.log("Specific observer 1 responds!");
    }
}
//Specific Observer 1
class ConcreteObserver2 implements Observer {
    public response(): void{
        cc.log("Specific observer 2 responds!");
    }
}

//When used
    let subject: Subject = new ConcreteSubject();
    let obs1: Observer = new ConcreteObserver1();
    let obs2: Observer = new ConcreteObserver2();
    subject.add(obs1);
    subject.add(obs2);
    subject.notifyObserver();

3. Use of observers in cocosCreator

We use observers for decoupling purposes, such as cc.game provided in the engine:
1. Define events anywhere: cc.game.on;
2. Distribute events anywhere: cc.game.dispatchEvent;
It's quite convenient to use, but remember to delete since you've registered to listen for events; sometimes I like to implement one myself, or sometimes I need to write a similar observer myself.

Not rigorous but to show the observer's internal logic:

export default class EventCenter {
    private static eventMap: Map<string, EventHandler[]> = new Map<string, EventHandler[]>();
    //Event Registration
    static registEvent(eventName: string, callBack: Function, target: Object): void {
        if (eventName == undefined || callBack == undefined || target == undefined) {
            return;
        }
        if (this.eventMap.get(eventName) == undefined) {
            this.eventMap[eventName] = new Array<EventHandler>();
        }
        let handler: EventHandler = new EventHandler(target, callBack);
        EventCenter.eventMap[eventName].push(handler);
    }

    //Event Distribution
    static postEvent(eventName: string, param?: any): void { //Distribution can pass parameters
        let handlers = EventCenter.eventMap[eventName];
        if (handlers == undefined) { return; }
        for (let i = 0; i < handlers.length; i++) {
            let handler = handlers[i]
            try {
                handlers.callBack.call(handler.target, param);
            }
            catch (errer) {
                cc.log(errer.message);
                cc.log(errer.stack.toString());
            }
        }
    }

    //Delete Events
    static removeEvent(eventName: string, callBack: Function, target: Object): void {
        let handlers = EventCenter.eventMap[eventName];
        if (handlers == undefined) { return; }

        for (let i = 0; i < handlers.length; i++) {
            let handler = handlers[i]
            if (handler != undefined && handler.target == target && handler.callBack == callBack) {
                handler[i] = undefined;
                break;
            }
        }
    }
}

//Structure of events
class EventHandler {
    target: object;
    callBack: Function;

    constructor(target: object, callBack: Function) {
        this.target = target;
        this.callBack = callBack;
    }
}

Posted by gershon on Sun, 17 Oct 2021 09:35:03 -0700