5

服务端游戏编程之设计模式(上)

 3 years ago
source link: https://www.lanindex.com/%e6%9c%8d%e5%8a%a1%e7%ab%af%e6%b8%b8%e6%88%8f%e7%bc%96%e7%a8%8b%e4%b9%8b%e8%ae%be%e8%ae%a1%e6%a8%a1%e5%bc%8f%ef%bc%88%e4%b8%8a%ef%bc%89/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

服务端游戏编程之设计模式(上)

2017/06/28 · Leave a comment

最近利用碎片的时间读到了《游戏编程模式》(作者:Robert Nystrom)一书,感叹于作者对游戏编程模式这块的理解之深,总结出来的内容深入浅出,样例也是从经典的场景中抽象出来,恰到好处。

虽然整本书的内容都是从游戏客户端的角度出发,同时并不影响其他端人员的阅读,所以说我写这篇文章的目的也十分简单:想从游戏服务端的角度出发,参照原书的章节,总结一下我对于游戏编程设计模式理解。

文章未对各种设计模式的经典实现进行讲解,因为这类资料太多,还望见谅。

关于游戏服务端设计模式的个人理解概述:

  • 相比较GOF书里提到的,能完全契合使用的设计模式比较少,一些需要变化后使用;
  • 在满足需求的前提下,使用设计模式的目的是为了实现高内聚、低耦合,增加可维护性;
  • 合适、合理、优雅的使用;切勿强行滥用、炫技(为了解耦而解,导致代码已经支离破碎,不利于阅读维护)!

命令模式

游戏中通常会有一个充满UI交互界面,通过点击UI上面的控件发送命令字与服务端交互,参考下面这个拙劣的比喻图:

命令模式拙劣的比喻图

每种交互基本与一个服务端的命令字对应(xxxx Command)。原始的实现大致如下:

class Command
{
public:
virtual ~Command() {}
//命令字的核心执行,纯虚函数需要在子类实现
virtual void execute() = 0;
//命令字的操作撤销,纯虚函数需要在子类实现
virtual void undo() = 0;
};
class MoveUnitCommand : public Command
{
//MoveUnit命令的实现
};

作为服务端,撤销操作这种需求显然是不适合在命令字处理侧单独实现的;其次,为了每个命令字单独实现一个类,成本有点高。当然并不是说这种方式不好,只是说我们核心需求是“execute”这个执行函数,完全可以简化一下:

class Command
{
typedef int32_t (*cmdFunc)(PlayerData *, const string &, string &);
public:
int32_t regCmd()
{
//在cmdFuncMap注册命令字,可以定义成虚函数,继承时重写
}
int32_t procCmd(int32_t cmdId, PlayerData *p, const string &req, string &rsp)
{
map<int32_t, cmdFunc>::iterator iter = cmdFuncMap.find(cmdId);
if (iter == cmdFuncMap.end()) {
return -1;
}
return iter->second(p, req, rsp);
}
private:
map<int32_t, cmdFunc> cmdFuncMap;
};

把原本需要实现的类退化成函数,在满足需求的同时简化了整体结构。当然你也可以自己封装类似于class PlayerCommand和class FightCommand继承类,在内部添加一些特定处理。

享元模式

享元模式的存在是为了解决有些含有共性数据的问题,将内部状态(the intrinsic state)与外部状态(the extrinsic state)区分开来。其中内部状态是一些共性的数据,外部数据是每个实例特有的数据。

就服务端来说我们不关心图形的绘制以及场景的生成步骤,但是需要关心场景由什么组成,场景长宽高(3D场景)以及场景中存在哪些的实例。这些实例可能会达到数以千计,以怪物实例来说,不推荐把实例实现成这样(注意这是不推荐的例子):

class MonsterModel
{
//怪物内部状态
};
class MonsterA
{
public:
MonsterModel *model;
//A怪物的外部状态
};

或许直接写成这样,直接依靠ID来区分怪物会更加直观

class Monster
{
int64_t uid;//全局唯一id
int32_t id; //怪物的类型id
//...其他属性
};

虽然可能会说这样增加耦合度:A拥有的属性B不一定拥有,会造成内存与效率上的“浪费”。

但是对于服务端来说这样聚合度更高,更加方便于局部的管理,这也是一个寻求平衡的过程。

观察者模式

如果你是一个有经验的游戏服务端从业人员,并且游戏的主逻辑是放在服务端来算的话,那么肯定会对“统计类”系统(例如成就系统)的“插入式”代码苦恼不已。

观察者模式契合了这类需求:变化->通知改变->接收处理。

但是对于游戏这类需求的几宗罪:

  • 强行耦合原有的系统代码;
  • 需求加入的点比较分散,甚至会出现一需多点的情况;

个人觉得这个模式在游戏服务端来说不是必须的,有三个原因:

  • 观察者模式并不能解决上面的问题,但是可以利用这个模式降低耦合度;
  • 每个类需要强行加了一层继承关系,实现特有的Update方法;
  • 有些异步callback接口没有类从属关系,会导致代码逻辑统一性的问题;

另外从搜索引擎的结果来看,这个模式没有十分优雅的应用。当然,如果您有更好关于观察者模式的游戏服务端实现样例欢迎交流一下:D。

这篇文章的是游戏服务端设计模式系列的上篇,章节结构是根据《游戏编程模式》,下篇会谈谈个人对原型、单例、状态模式在游戏服务端中的使用心得。

(全文结束)

转载文章请注明出处:漫漫路 - lanindex.com

Leave a Comment Cancel reply

Your email address will not be published.

在此浏览器中保存我的名字、电邮和网站。


Recommend

  • 25
    • www.tuicool.com 4 years ago
    • Cache

    函数式编程让你忘记设计模式

    本文是一篇《Java 8实战》的阅读笔记,阅读大约需要5分钟。 有点标题党,但是这确实是我最近使用Lambda表达式的感受。设计模式是过去的一些好的经验和套路的总结,但是好的语言特性可以让开发者不去考虑这些设计模式。面向对象常...

  • 2
    • rollingstarky.github.io 1 year ago
    • Cache

    Node.js 设计模式笔记 —— Streams 流编程

    Streams 是 Node.js 的组件和模式中最重要的几个之一。在 Node.js 这类基于 event 的平台上,最高效的实时地处理 I/O 的方式,就是当有输入时就立即接收数据,应用产生输出时就立即发送数据。 Buffering vs st...

  • 47
    • blog.xizhibei.me 5 years ago
    • Cache

    微服务设计模式之断路器

    记得小时候,有次自己用手拿了两根铁丝插进插座(别问为什么,我在探索真理……),于是至今仍然记得那种全身酥麻以及恶心的感觉,但是幸好时间不长,两秒之后就停了,然后我家就停电了——对的:空气开关起作用了,我的小命就这么留下来了。

  • 58
    • blog.xizhibei.me 5 years ago
    • Cache

    微服务设计模式之 API 网关

    API 网关是目前非常成熟的一种微服务与外界通讯方式的一种选型,当前你的架构是从单体架构 Monolithic 迁移过来的时候,你会发现新的服务无法很好地从旧有系统中接管流量。 这个接管,或者说迁移的过程很复杂,也...

  • 64

  • 29
    • 微信 mp.weixin.qq.com 5 years ago
    • Cache

    微服务通信的设计模式

  • 14

    [译] 微服务的设计模式 了解微服务...

  • 13
    • dockone.io 4 years ago
    • Cache

    微服务设计模式

    微服务架构已经成为现代应用程序开发的主流。虽然说它能够解决某些问题,但却也不是万金油。而我们在使用这个体系架构时,还有许多的问题要我们解决。这就需要学习这些问题的通用模式,并通过可复用的解决方案来解决问题。因此,有必要讨论微服...

  • 21
    • violetzijing.is-programmer.com 3 years ago
    • Cache

    设计模式 - 服务定位器模式

    服务定位器模式(Service Locator Pattern)用在我们想使用 JNDI 查询定位各种服务的时候。考虑到为某个服务查找 JNDI 的代价很高,服务定位器模式充分利用了缓存技术。在首次请求某个服务时,服务定位器在 JNDI 中查找服务,并缓存该服务...

  • 8
    • kubesphereio.com 2 years ago
    • Cache

    K8s 中单体服务设计模式

    K8s 中单体服务设计模式Singleton 服务模式确保了应用的一个实例同时只有一个是激活的,但又是高度可用的。这种模式可以从应用内部实现,也可以完全委托给 Kubernetes。 Kubernetes 提供的主要功能之一是能够轻松透明地扩展应用。Pods 可以通过单一命令...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK