游戏编程模式——观察者模式
文章目录
概述
观察者模式是我们在游戏中常用到的一种设计模式,比方说在UI系统中,我们消耗了金币,要更新当前UI界面上所有和金币相关的元素,如果UI上只有一个修改项还好,但当修改的元素过多时,一个个修改就会很麻烦,也容易遗漏,而且代码耦合性也很高,通常我们可以用观察者模式来解决类似的问题。cocos2dx中的事件分发器就采用了观察者模式的思想。
下面是根据《 游戏编程模式》写的一个简单的观察者模式实例,采用定长数组来保存观察者,可以优化成链式观察者。
应用场景:游戏中需要一个成就系统,当我们触发玩家角色坠落时,触发成就系统的一个成就事件,而玩家坠落是由物理系统处理的,要避免两个系统之间代码耦合。
本文只是展现观察者模式的一个应用实例,不对观察者模式进行详细介绍,具体的介绍可以参考《游戏编程模式》或其他设计模式经典书籍。
类图
如图,避免了成就系统和物理系统的耦合。
代码实现
被观察对象——Subject
Subject.h
#pragma once
#include "Observer.h"
#include "Event.h"
class Subject {
public:
void addObserver(Observer* observer);
void removeObserver(Observer* observer);
private:
Observer* observers_[MAX_OBSERVERS];
int numObservers_ = 0;
protected:
void notify(const Entity& entity, Event event);
};
Subject.cpp
#include "Subject.h"
void Subject::addObserver(Observer* observer) {
observers_[numObservers_] = observer;
++numObservers_;
}
void Subject::removeObserver(Observer* observer) {
}
void Subject::notify(const Entity& entity, Event event) {
for (int i = 0; i < numObservers_; ++i) {
observers_[i]->onNotify(entity, event);
}
}
物理系统类 Physics
Physics.h
#pragma once
#ifndef PHYSICS
#include "Subject.h"
class Physics :public Subject
{
public:
void updateEntity(Entity& entity,Event event);
};
#endif // !PHYSICS
Physics.cpp
#include "Physics.h"
void Physics::updateEntity(Entity& entity,Event event) {
Subject::notify(entity, event);
}
观察者抽象类 Observer
Observer.h
#pragma once
#ifndef OBSERVER
#include "Entity.h"
#include "Event.h"
#define MAX_OBSERVERS 100
class Observer {
public:
virtual ~Observer() {};
virtual void onNotify(const Entity& enity, Event event) = 0;
};
#endif // !OBSERVER
可以看到Observer类中有一个纯虚函数 —— onNotify() ,所以Observer类是一个抽象基类,不能被实例化
成就Achievements
成就Achievements类继承于Observer类,是一个具体的观察者,通过重写了父类Observer的纯虚函数onNotify()来实现不同观察者对同一监听事件的不同处理方法。
Achievements.h
#pragma once
#ifndef ACHIEVEMENTS
#include "Observer.h"
#include "Entity.h"
#include "Event.h"
class Achievements :public Observer {
public:
virtual void onNotify(const Entity& enity, Event event);
};
#endif
Achievements.cpp
#include "Achievements.h"
void Achievements::onNotify(const Entity& enity, Event event) {
switch (event)
{
case EVENT_ENTITY_FALL:
if (enity.isHero()) {
printf("Get achievement - falling hero");
}
break;
//处理其他事件
default:
break;
}
}
实体类(Entity)
Entity.h
#pragma once
#ifndef ENTITY
#include<string>
class Entity {
public:
Entity() {};
virtual ~Entity() {};
bool isHero() const;
private:
std::string type;
};
#endif // ENTITY
Entity.cpp
#pragma once
#ifndef ENTITY
#include<string>
class Entity {
public:
Entity() {};
Entity(std::string s) {
type = s;
};
virtual ~Entity() {};
bool isHero() const;
private:
std::string type;
};
#endif // ENTITY
监听事件 (Event)
Event.h
#pragma once
#ifndef EVENT
typedef int Event;
#define EVENT_ENTITY_FALL 10001 //物体下落事件
#endif // !EVENT
-
Event并不是定义的新类,只是对int 类型的一个别名,在Event.h下申明对应的事件ID,不同的Event对应不同的触发事件。
-
同时当事件很多的时候,我们可以在Event.h中直接看到当前观察的事件类型,而不用去一行行翻具体的执行代码。
-
此外,我们还可以增加相关的方法,就像cocos引擎的事件分发器那样可以在代码中手动注册监听事件
main方法
main.cpp
#include "Entity.h"
#include "Achievements.h"
#include "Physics.h"
int main() {
Entity myHero = Entity("hero"); // 定义了一个hero实体
Physics phySystem = Physics(); // 定义了一个物理系统
Achievements achSystem = Achievements(); // 定义了一个成就系统
phySystem.addObserver(&achSystem); // 让成就系统观察物理系统
phySystem.updateEntity(myHero, EVENT_ENTITY_FALL); //hero实体触发了物理系统中的实体下坠事件
return 0;
}