Lua下的ECS框架

转自云风的BLOG: https://blog.codingnow.com/2017/12/lua_ieaae_ecs_oue.html

前段时间,我写了一篇 浅谈《守望先锋》中的 ECS 构架 。最近想试试在 Lua 中实现一个简单的 ECS 框架,又仔细琢磨了一下。

我思考后的结论是:ECS 并不是一个新概念,它的提出其实是和语言相关的。ECS 概念的诞生起于游戏行业,相关框架基本都是基于 C++ 来开发的。它其实是对 C++ 对象模型的一个反思。ECS 针对组件组合对象,而反对 C++ 固有的基于继承的对象模型。对象模型才是 ECS 的设计核心理念。而离开 C++ 的对象模型,ECS 并不是什么新鲜的东西。

我的这个观点也不新鲜,在 ECS 的 Wikipedia 页上也有类似的说法:

In the original talk at GDC Scott Bilas compares C++ object system and his new Custom component system. This is consistent with a traditional use of this term in general Systems engineering with Common Lisp Object System and Type system as examples. Therefore, the ideas of “Systems” as a first-class element is a personal opinion essay. Overall, ECS is a mixed personal reflection of orthogonal well-established ideas in general Computer science and Programming language theory. For example, components can be seen as a mixin idiom in various programming languages. Alternatively, components are just a small case under the general Delegation (object-oriented programming) approach and Meta-object protocol. I.e. any complete component object system can be expressed with templates and empathy model within The Orlando Treaty vision of Object-oriented programming,

抛开理论不谈,如果要在 Lua 中实践,我们到底可以做点什么呢?

我认为需要有这几个方面:

首先应该对 Lua 加强类型系统。Lua 的动态性天然支持把不同的组件聚合在一起,我们把不同的 Component 放在一张表里组合成 Entity 就足够了。但如果 Component 分的很细的话,用很多的表组合成一个 Entity 对象的额外开销不小。不像 C++ ,结构体聚合的额外开销几乎为零。我们完全可以把不同 Component 的数据直接平坦放在一个 table 中,只要键值不冲突即可。但是我们需要额外的类型信息方便运行时从 Entity 中萃取出 Component 来。另外,如果是 C / Lua 混合设计的话,某些 Component 还应该可以是 userdata 。

从节省空间及方便遍历的角度讲,我们甚至可以把同类的 C Component 聚合在一大块内存中,然后在 Entity 的 table 中只保留一个 lightuserdata 即可。ECS 的 System 最重要的操作就是遍历处理同类 Component ,这样天然就可以分为 C System 和 Lua System 。数据的内聚性很高,可以直接区分开 C data 和 Lua Data 。

然后、就是方便的遍历。ECS 的 System 需要做的就是筛选出它关心的 Entity ,针对其中的 Component 做操作。如果需要筛选结果大大少于全体 Entity 数量,遍历逐个判断就会效率很低。好在在 Lua 中,我们可以非常容易地做出 cache ,只需要遍历筛选一次,在监控新的 Component 的诞生就可以方便的维护遍历用的集合了。

我写了一个初步的版本。打算等到实际使用起来再慢慢完善。

它可以实现成一个纯 Lua 版,但我特地尝试把里面的两个函数编写了 C 的等价版,看起来可以提高不少性能。

这里的 API 中, Entity 全部使用唯一数字 id 标识,而不主张直接引用 entity 的 table 。这也是一般 ECS 框架的通用做法。数字 id 可以提高健壮性,还可以避免对已经销毁的 Entity 错误的引用。如果需要 C / Lua 混合编程的话,在 C 中引用 id 也方便的多。

类型系统是这样设计的:

每个 Component 有一个 16bit 的唯一类型 id ,每个 Entity 是由若干 Component 组合而成,我将它们的类型 id 升序排列成一个字符串,作为整个 Entity 的动态类型。然后 Lua 中实现了一个 cache ,可以从这个类型字符串转换成易用的类型对象。类型对象用来对 Entity 做筛选,从里面分离出 Component 。这个类型字符串的拼接我实现的是一个 C 版本,虽然 Lua 也能实现的出来,但是效率会低很多。

大部分 Component 我主张直接平坦的放在 Entity 对象表中,但若需要用 C 结构来承载,或单独用一个子表,也提供了 Component 类型注册方法,可以在 new component 时定义一个专门的构造函数(以及 delete 时的析构函数)。

这里还提供了遍历包含特定 Component 的 Entity 集合 的迭代器。它由一个弱表实现的 cache 来管理。在第一次遍历时,会创建一个集合,收集 Entity 全体集合中符合要求的部分,把筛选出来的 id 记录下来。一旦遍历集合创建好,它还会跟踪新的 Component 的创建,自动加进来。

这里的迭代器,我同时实现了 Lua 版本和 C 版本。C 版本的性能会高一些,我认为这个 C 版本在遍历相关集合时,性能表现不会差于用 C/C++ 实现的原生容器。

以上是对 Entity 和 Component 的支持。System 相关的方法,我还没有想好可以做点什么。等用到了再完善。

Lua游戏服务器框架是一种用于开发多人在线游戏(MMORPG)的框架。其中一个常见的Lua游戏服务器框架是Giraffe,它是基于crossover框架开发的。Giraffe框架可以帮助开发者快速搭建类似bigworld引擎的多进程的MMORPG集群架构,也可以轻松地搭建其他游戏类型的集群架构。 你可以通过以下步骤来使用Giraffe框架搭建Lua游戏服务器: 1. 首先,你需要安装Lua和crossover框架。你可以在https://github.com/galenho/Giraffe.git上找到Giraffe框架的代码。 2. 下载Giraffe框架的代码,并将其添加到你的项目中。 3. 在你的Lua代码中,引入Giraffe框架的模块,并使用它提供的功能来构建你的游戏服务器。 4. 使用Giraffe框架提供的API来处理游戏逻辑、网络通信、数据库访问等功能。 5. 根据你的需求,配置Giraffe框架的集群架构,以支持多进程和分布式部署。 下面是一个简单的示例代码,展示了如何使用Giraffe框架创建一个简单的Lua游戏服务器: ```lua local giraffe = require("giraffe") -- 创建服务器实例 local server = giraffe.createServer() -- 处理客户端连接事件 server:on("connection", function(client) print("New client connected:", client.id) -- 处理客户端消息事件 client:on("message", function(message) print("Received message from client:", message) -- 处理游戏逻辑 -- 发送消息给客户端 client:send("Hello, client!") end) -- 处理客户端断开连接事件 client:on("disconnect", function() print("Client disconnected:", client.id) end) end) -- 启动服务器 server:listen(8080) print("Server started on port 8080") ``` 通过上述代码,你可以创建一个简单的Lua游戏服务器,并处理客户端的连接、消息和断开连接事件。你可以根据自己的需求,进一步扩展和定制服务器的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值