文章目录
一、观察者模式简介
观察者模式是让多个观察者监听同一个主题对象(被观察者), 主题对象状态发生变化时,会通知所有的观察者,使它们能够更新自己。
二、观察者模式在游戏开发中的应用
1.设计理念
我们设计一个Demo用来加深对观察者模式的认识。
在C++的游戏开发中,设计模式对于各模块来说尤为重要。在经典的RPG游戏中,观察者模式可以应用到英雄状态的监听上,将英雄类对象作为主题对象(被观察者),怪物类、控制类、视角类(摄像机类)等可作为观察者随时监听英雄的状态以作出自身状态的更新。接下来的示例将主要展示观察者监听英雄在地图中的坐标。
2.代码示例
2.1 首先定义一个抽象观察者类
定义抽象观察者类,以抽象出诸如摄像机类、控制类、怪物类等不同具体观察者类。
class Observer {
public:
Observer() = default;
Observer(const string& name) : name_(name) {};
virtual ~Observer() = default;
void Call(Object* obj) {
obj->Notify(GetName());
}
//声明抽象方法
virtual void Do(Object* obj) = 0;
string GetName() const {
return name_;
}
void SetName(string name) {
name_ = name;
}
private:
string name_;
};
2.2 定义具体观察者——摄像机类
class Camera : public Observer {
public:
Camera() = default;
Camera(const string& name) : Observer(name) {};
//重写父类方法
virtual void Do(Object* obj) override {
//在这里重新设置摄像机的观察位置
cout << GetName() << ":摄像机观察位置已更新,对象已显示在屏幕中间" << endl;
}
private:
//禁用拷贝构造和赋值拷贝
Camera(const Camera&) = delete;
Camera& opereator =(const Camera&) = delete;
};
2.3 定义具体观察者——控制类
class Control : public Observer {
public:
Control() = default;
Control(const string& name) : Observer(name) {};
//重写父类方法
virtual void Do(Object* obj) override {
//在这里判断对象是否越界,并作出控制反馈
cout << GetName() << ":对象即将越界,移动停止" << endl;
//cout << "对象未越界,继续移动" << endl;
}
private:
//禁用拷贝构造和赋值拷贝
Control(const Control&) = delete;
Control& opereator =(const Control&) = delete;
};
2.4 定义具体观察者——怪物类
class Monster : public Observer {
public:
Monster() = default;
Monster(const string& name) : Observer(name) {};
//重写父类方法
virtual void Do(Object* obj) override {
//在这里根据对象坐标,计算接近路径
cout << GetName() << ":路径重新计算成功" << endl;
}
private:
//禁用拷贝构造和赋值拷贝
Monster(const Monster&) = delete;
Monster& opereator =(const Monster&) = delete;
};
2.4 定义抽象被观察者类——实体类
有时我们的被观察者不应该只是玩家所控制的英雄,可能是别的什么,为了代码的扩展性着想,对被观察者也要进行抽象。
class Object {
public:
Object() = default;
virtual ~Object() = default;
// 声明抽象方法
virtual void Notify(Object* obj) = 0;
private:
vector<Observer*> observers_;
};
2.5 定义具体被观察者类——英雄类
class Hero : public Object {
public:
Hero() = default;
// 声明抽象方法
virtual void Notify(Object* obj) = 0;
private:
vector<Observer*> observers_;
};
为了方便,我将上述所有类的定义放在了同一个文件,名为Observer.h
//Observer.h
#include <iostream>
#include <vector>
using namespace std;
//前向声明
class Observer;
class Object {
public:
Object() = default;
virtual ~Object() = default;
// 声明抽象方法
virtual void Notify(const string& name) = 0;
void AddObserver(Observer* obj) {
observers_.push_back(obj);
}
protected:
vector<Observer*> observers_;
};
class Observer {
public:
Observer() = default;
Observer(const string& name) : name_(name) {};
virtual ~Observer() = default;
void Call(Object* obj) {
obj->Notify(GetName());
}
//声明抽象方法
virtual void Do(float posx, float posy) = 0;
string GetName() const {
return name_;
}
void SetName(string name) {
name_ = name;
}
private:
string name_;
};
class Camera : public Observer {
public:
Camera() = default;
Camera(const string& name) : Observer(name) {};
//重写父类方法
virtual void Do(float posx, float posy) override {
//在这里重新设置摄像机的观察位置
cout << GetName() << ":摄像机观察位置已更新,对象已显示在屏幕中间" << endl;
}
private:
//禁用拷贝构造和赋值拷贝
Camera(const Camera&) = delete;
Camera& operator = (const Camera&) = delete;
};
class Control : public Observer {
public:
Control() = default;
Control(const string& name) : Observer(name) {};
//重写父类方法
virtual void Do(float posx, float posy) override {
//在这里判断对象是否越界,并作出控制反馈
cout << GetName() << ":对象即将越界,移动停止" << endl;
//cout << "对象未越界,继续移动" << endl;
}
private:
//禁用拷贝构造和赋值拷贝
Control(const Control&) = delete;
Control& operator = (const Control&) = delete;
};
class Monster : public Observer {
public:
Monster() = default;
Monster(const string& name) : Observer(name) {};
//重写父类方法
virtual void Do(float posx, float posy) override {
//在这里根据对象坐标,计算接近路径
cout << GetName() << ":路径重新计算成功" << endl;
}
private:
//禁用拷贝构造和赋值拷贝
Monster(const Monster&) = delete;
Monster& operator =(const Monster&) = delete;
};
class Hero : public Object {
public:
//通知方法
virtual void Notify(const string& name) {
size_t size = observers_.size();
for (size_t i = 0; i < size; ++i) {
if (name == observers_[i]->GetName()) {
observers_[i]->Do(posx_, posy_);
}
}
}
private:
float posx_, posy_;
};
2.5 main.cpp测试代码
#include "Observer.h"
int main() {
Camera ca("camera"); //创建摄像机
Control co("controller"); //创建控制类对象
Monster mo1("monster1"); //创建怪物1
Monster mo2("monster2"); //创建怪物2
Monster mo3("monster3"); //创建怪物3
Monster mo4("monster4"); //创建怪物4
Monster mo5("monster5"); //创建怪物5
Hero hero; //创建英雄
//添加观察者
hero.AddObserver(&ca);
hero.AddObserver(&co);
hero.AddObserver(&mo1);
hero.AddObserver(&mo2);
hero.AddObserver(&mo3);
hero.AddObserver(&mo4);
hero.AddObserver(&mo5);
//监听被观察者
ca.Call(&hero);
co.Call(&hero);
mo1.Call(&hero);
mo2.Call(&hero);
mo3.Call(&hero);
mo4.Call(&hero);
mo5.Call(&hero);
return 0;
}
2.5 输出结果
3 总结
上述代码Demo只是为了展示观察者模式的设计思路,并没有将具体的方法完整实现,虽然上述Demo并不是最好的体现观察者模式的应用场景,但希望能对各位读者起到理解上的帮助。在群体通知下(上述代码属于观察者主动调用的单个通知)观察者模式的观察者数量最好不要太多,不然性能会大打折扣。