常用游戏框架
常用框架
- ecs [^文章]
- 服务器架构[^常用的]
前置内容
1. 同步机制
帧同步 moba atc game
按帧同步客户端所有协议给所有玩家
每个客户端都在执行相同的操作,所以最后的结果一样。
需要使用定点数,当你的逻辑中使用了浮点数的时候,因为每一台机器的不同,浮点最后的有效值其实是不一样的,所以为了保证逻辑的同步性,再游戏逻辑中,需要使用定点数。
状态同步 mmo等
状态改变再同步
状态帧同步 fps游戏
状态帧同步就是状态同步和帧同步的概念相结合。客户端向服务器上传操作,服务器跑逻辑,但是又在按照固定的每一帧下发所有玩家的状态(属性位置等)给每一个客户端实现同步。
2. 定点数 浮点数
帧同步中 定点数 作为一个标准存在
定点数小数 [^计算方式]
计算方式和整数一样
- 浮点数 定点化
java 可以用bigdecimal
浮点数的定点化 [^设计用例]
IEEE 754 规定,浮点数的表示方法为:
最高的 1 位是符号位 s,接着的 8 位是指数E,剩下的 23 位为有效数字 M。
ECS框架 和 传统编程方式
主要说明ecs框架 部分内容会带出区别
守望先锋框架使用了这个模式[^守望先锋的架构和网络]
什么是ECS
Entity Component System (ECS) 是一个 gameplay 层面的框架,它是建立在渲染引擎、物理引擎之上的,主要解决的问题是如何建立一个模型来处理游戏对象 (Game Object) 的更新操作。
Entity(实体,本质上是存放组件的容器)
可以说就是传统引擎中的 Game Object 。但在这个系统下,它仅仅是 C/Component 的组合。它的意义在于生命期管理。
System(系统,根据组件数据处理逻辑状态的管理器)
C 和 S 是这个框架的核心。System 系统(或者模块)对于游戏来说,每个模块应该专注于干好一件事,而每件事要么是作用于游戏世界里同类的一组对象的每单个个体的,要么是关心这类对象的某种特定的交互行为。比如碰撞系统,就只关心对象的体积和位置,不关心对象的名字,连接状态,音效、敌对关系等。它也不一定关心游戏世界中的所有对象,比如关心那些不参与碰撞的装饰物。所以对每个子系统来说,筛选出系统关心的对象子集以及只给它展示它所关心的数据就是框架的责任了。
Component(组件,游戏所需的所有数据结构)
把每个可能单独使用的对象属性归纳为一个个 Component ,比如对象的名字就是一个 Component ,对象的位置状态是另一个 Component 。
ECS 框架設計方式
设计观念:DOP(data-oriented programming) 面向数据编程
组合大于继承: DDD(Date-Driver-Development)数据驱动开发
和面向对象开发区别是 设计出发点是基于我需要什么数据 而不是要什么玩法逻辑
上图大概意思就是,有三个Entity,其中A和B具有相同的Component,还有一个system能够通过Translation和Rotation计算出LocalToWorld。
先有一个World,它是系统和实体的集合,而实体就是一个ID,这个ID对应了组件的集合。组件用来存储游戏状态并且没有任何行为,系统拥有处理实体的行为但是没有状态。
entity – int id (可以是一个id或者构建成一个类型)
| - Component1 附加在实体上的数据 (血量 )
| - Component2 包含多个 组件 (速度等)
| - Component3 组件 主要是数据 尽可能不涉及函数逻辑动态的注册删除组件 很灵活
system 描写组件的逻辑 我可以查询到所有我想要的实体
项目1[^entt]:c++ 但是大量C++ 模板元编程 看起来不方便
推荐项目2[^entitas]:c# unity 还有c++版本 比较好理解 实际项目有enttpong也可以看一下
ecs的一些设计概念 复杂的文字部分
每个 Entity 是由多个 Component 组合而成,共享一个生命期;而 Component 之间可以组合在一起作为 System 筛选的标准。我们在开发的时候,可以定义一个 System 关心某一个固定 Component 的组合;那么框架就会把游戏世界中满足有这个组合的 Entity 都筛选出来供这个 System 遍历,如果一个 Entity 只具备这组 Component 中的一部分,就不会进入这个筛选集合,也就不被这个 System 所关心了。
ECS 的设计就是为了管理复杂度,它提供的指导方案就是 Component 是纯数据组合,没有任何操作这个数据的方法;而 System 是纯方法组合,它自己没有内部状态。它要么做成无副作用的纯函数,根据它所能见到的对象 Component 组合计算出某种结果;要么用来更新特定 Component 的状态。System 之间也不需要相互调用(减少耦合),是由游戏世界(外部框架)来驱动若干 System 的。如果满足了这些前提条件,每个 System 都可以独立开发,它只需要遍历给框架提供给它的组件集合,做出正确的处理,更新组件状态就够了。编写 Gameplay 的人更像是在用胶水粘合这些 System ,他只要清楚每个 System 到底做了什么,操作本身对哪些 Component 造成了影响,正确的书写 System 的更新次序就可以了。一个 System 对大多数 Component 是只读的,只对少量 Component 是会改写的,这个可以预先定义清楚,有了这个知识,一是容易管理复杂度,二是给并行处理留下了优化空间。
作者自己也说,最终有 40% 的组件就是单件。单件本身其实就和传统面向对象模型差不多了。但是数据和方法分离还是很有意义。我们在用面向对象模式做开发的时候也会碰到一个对象有几个不同的方法,某些方法关注这部分状态、另一些方法关注另一部分状态,还有一些方法关注前面几组状态的集合。这里的方法就是 ECS 中的系统、状态就是组件。将数据和方法分离可以将不同的方法解耦。如果用传统的 C++ 的面向对象模式,很可能需要用多继承、组合转发等等复杂的语法手段。
脚注
【构建你自己的ECS系统】1-什么是ECS_哔哩哔哩_bilibili
[^守望先锋的架构和网络]: 《守望先锋》架构设计与网络同步 - 掘金 (juejin.cn)
[^设计用例]: 校招基础——浮点数的定点化 - 咸鱼FPGA - 博客园 (cnblogs.com)
[^计算方式]: (23条消息) 定点小数的运算_定点小数运算_wanrenqi的博客-CSDN博客
[^常用的]: (23条消息) ARPG、MMORPG、MOBA、卡牌类、棋盘类游戏服务器架构图_游戏架构_森明帮大于黑虎帮的博客-CSDN博客
[^文章]: 游戏开发中的ECS 架构概述 - 知乎 (zhihu.com)
[^entt]: entt: Gaming meets modern C++ - a fast and reliable entity-component system (ECS) and much more (gitee.com)
[^entitas]: HelloMa/Entitas-Cpp (gitee.com)