理解 Entity Component System

Entity-Component-System (ECS) 是一种架构模式。这种模式广泛地应用在游戏开发中。ECS 使用组合原则,因此这种模式使程序具有更好的灵活性和扩展性。游戏场景中的所有对象都被视为一个实体 (Entity)。这种模式在默认情况下也具有更高的性能。 Entity-Component-System 有三个部分组成 Entity (实体) Component (组件) System (系统) 什么是 Entity Entity 可以理解为一个对象的标识,它没有任何具体的数据和行为,只是标识一个东西。在实现上,通常可以用一个 Struct 来实现。而组件,为其提供数据。 例如,我们要实现一个太空版本 Minecraft,所有游戏中能看到的,有形的东西,都算作一个实体。一搜飞船,一个角色等等。 什么是 Component 组件是附加到实体的可重用模块,它是实体的单一行为描述。组件提供了实体的表现,行为,和功能。不同组件的组合,可以创造出不同类型的逻辑实体。 什么是 System 一个 System 在运行时会遍历很多组件,以此实现高效的性能。例如渲染,物理,寻路。系统为组件提代了全局的管理和服务。 我们可以使用系统来分离逻辑和数据,系统可以用来处理逻辑组件,充当数据容器。 关于系统的例子 处理重力加速度 将速度应用到一个向量上 根据 AI 的设计,来控制机器人的输入 渲染 (位置,Sprite) 处理玩家输入 组合 我们可以组合不同的 Component,以及设置 Component 不同的数据值,来配置具体的实体。 ECS 的优势 降低代码量以及复杂度 对于逻辑扩展拥有很高的灵活性 对于 3D 和 VR 需要大量渲染逻辑的项目有性能优势 让非技术人员更方便地编写脚本 可以分离庞大复杂的类结构 代码可重用和可组合性很强 更加方便的单元测试 可以支撑复杂的 VR 程序 运行时组件的替换 多进程和多线程友好 分离数据和功能 更加灵活地定义游戏对象 提供了解耦,封装,模块化,可重用性方法,以此构成一个干净的设计。 ECS 的劣势 不能像 MVC 那样直观定义逻辑 要用好 ECS,需要更多的思考组件的设计 ECS 需要写大量的小型代码,增加了出错的风险 目前的应用没有面向对象广泛 ECS 示例 看下面的图,这是一个兔子的实体,其中有很多组件被附加到了实体上,Placeable、Huggable、Consumable、Hopping 等 …...

April 3, 2022 · 1 min · fred

Unity 完整的热更新方案和流程

在开发商业游戏时,热更新是一个很重要的模块,这里讲的热更新不是指仅仅修复Bug,而是进行游戏功能的更新。简单来讲,就是启动游戏后,跑个条,下载资源和代码,然后再进入游戏。本篇博客所写的内容并不是最优的解,只是完成了热更新这个事情而已,具体使用还需要使用者根据自己的项目来具体来看。 这里采用的方案是使用 AssetBundle 和 xLua。使用 AssetBundle 是为了资源的完全自主控制。而整个游戏的逻辑部分,则使用 xLua 来实现。当然,C# 的代码不可能一点没有,只是一些核心的功能模块,一般写好后就不会改变的东西,或者对性能要求很高的东西,放在 C# 就可以。 整个功能分为编辑器部分,和运行时部分。编辑器部分就是编 Bundle,生成版本文件等。而运行时部分就是从 CDN 下载版本文件,对比版本号及本地资源是否有要更新的,如果有,则更新,更新完后进入游戏。没有,则直接进入游戏。 编辑器部分 编辑器部分主要就是生成 Bundle 文件,首先,我是按目录来划分 Bundle 的,任何一个目录下的文件(不包括子目录)则会打成一个 Bundle。例如下面的目录结构 Res/ - ConfigBytes/ - UI/ - LuaScripts/ - Data/ - ItemsData/ - CharactersData/ 首先 Res 目录是资源的主目录,下面有各种子目录(Res 目录下不会有需要打 Bundle 的文件)。ConfigBytes 目录下的文件,会打成一个 Bundle。UI 目录下的文件会打成一个 Bundle。LuaScripts 目录下的文件会打成一个 Bundle。Data 目录下的 ItemsData 目录中的文件会打成一个 Bundle,Data 目录下的 CharactersData 目录会打成一个Bundle。如果 Data 目录下存在文件(非目录),则这些文件会打成一个 Bundle。简单来讲,就是会按文件夹来决定哪些文件打成一个Bundle,检查的时候只会取一个文件夹下的文件,而不会递归取这个文件夹下的子目录。 LuaScripts 目录在开发时会放在 Assets 目录外面,与其同级,在编 Bundle 时,会拷贝 LuaScripts 目录及下面的所有文件,按原有目录结构,拷贝到 Res 目录中,并且会将每一个 xxx.lua 文件的扩展名改为 xxx....

March 20, 2022 · 14 min · fred

一个游戏兑换码生成及验证方案

在开发独立游戏《小镇危机:来自丧尸的问候》时,需要设计一个兑换码的功能,但是在网络上也没有找到合适的方案,所以这里就自己考虑了一种。这里会说明思路,具体的实现,可以按照自己的方式去定制。 我们的游戏中有两种资源可以兑换,一种是星星,一种是钻石。所以就需要在兑换码中通过某种方式表示出来。本质上就是将自己想兑换的数据,通过某种方式的变换,隐藏在兑换码中,然后服务器是知道解码方式的,通过解码,即可还原出玩家兑换的是什么,然后将兑换物品回给玩家。 我们的游戏兑换码为14位的,里面包含的信息有兑换类型,兑换数量,这两个信息,例如 LMA60XV7380QBH。 1. 兑换码生成和验证过程 兑换码由大写字母和数字组成,对于兑换码中的每一个字符,我们可以使用的字符为 [0, 9] 和 [A, Z]。 确定要表示的信息,所占用的字符数。对于我们的游戏来说,兑换类型只需要 1 个字符即可,而兑换数量,需要占用 4 个字符,到此,已经消耗掉 5 个字符。 兑换码的数据校验和,会占用2个字符,到此消耗掉 7 个字符。 剩下的 7 个字符,为唯一的随机字符串,用于填充验证码的。 根据上面的步骤,首先生成第 3 步所需要的 7 位随机字符串。由于我们每一位可用的字符数是36个,所以最终的排列组合有上百亿种,我们不需要那么多,几百万个足够了。这里要注保证唯一性。生成的字符串如下所示 DLDWVEQ WM8YB8M MNUP5RR 3RG7X8D VUBPD8J J3L3ZR1 1Y1ALB3 R6PRATR 然后表示出兑换码要兑换的数据,假设这里要兑换星星,并且兑换的数量为 10000 个。首先使用一个字符来表示星星,例如使用 S 来表示,当然,也可以将 36 个字符进行分组,然后从组中去随机一个,这样更不容易被破解。然后兑换的数量,可以转为 16 进制,或者 34 进制都行。假设这里使用 34 进制来表示,10000 转为 34 进制就是 8M4,不足4位,前面进行补0,也就是 08M4。进制转换的逻辑需要自己写。 取一个随机字符串,与要兑换的信息进行组合,组合的方式随意,只要最后恢复时使用相同的方式即可。假设这里将随机字符串的前3位放在兑换码的开始,然后跟上兑换类型,后三位放在最后。这里使用上面第一个随机字符串为例子。经过这一步组合,得到的字符串如为 DLDS08M4WVEQ。 然后计算上面得到的字符串的校验和,为 AD,将 AD 放在最后,这里随意,可以按自己想放的位置去放,自己知道就好了。现在得到的字符串为 DLDS08M4WVEQAD。 最后需要做的就是将上面得到的字符串进行打乱,按什么样的方式打乱呢,这里需要事先生成几套排列方案,例如 [8, 1, 5, 9, 2, 4, 0, 12, 7, 10, 13, 11, 3, 6],也就是字符中第一个字符放到索引 8 的位置,第二字符放到索引 1 的位置,第三个字符放到索引 5 的位置,以此类推。我们可以生成多套这样的排列方案,然后使用字符串中的其中一位,来表示,例如我们使用第2位的L来表示使用哪一套方案,将 L 转换为 int 数值,然后对排列套数进行 % 操作,得到使用哪一套排列方案,进行排列。这里要注意,第2位不能变,也就是不受随机排列影响,忽略掉,否则服务器没法恢复。...

March 1, 2022 · 1 min · fred

我的独立游戏下载量和广告收入情况

经过了几个月的开发,我的游戏终于上线了。这篇博客来聊一下目前这个游戏的下载量和广告收入情况。 《游戏码农:那些打工的日子》是一个轻松的模拟经营文字游戏,游戏的主角是一个普普通通的小码农,刚刚来到一线城市,开始自己的职业打工生涯。游戏拥有丰富的玩法,轻松的升级体验,多种不同的赚钱方式,实现财富自由,指日可待。 目前已经上线 AppStore,TapTap,好游快爆等平台。 AppStore: https://apps.apple.com/cn/app/id1607035933 TapTap: https://www.taptap.com/app/230651 好游快爆:https://www.3839.com/a/141332.htm 下图是 Android 平台的广告收入情况 下图是 iOS 平台的广告收入情况 通过上面的广告收数据可以知道,从 2022.01.27 到 2022.02.05 这几天,Android 平台一共收入了 111 块钱,而 iOS 共收入了 70 块。 下面是各个渠道的下载量统计 从上线以来,目前总玩家数有2982,期中日活玩家平均不到200。在渠道统计中有一个为 Development 的渠道,占了总玩家数的大部分,但是,这个渠道的包不是我发的,而是被破解了广告的一个包,所以其实真正导致收入的玩家数,是很少的。 综上,这个游戏从玩法上来说,玩家粘性一般。从收入上来说,也是几乎可以忽略了。假设包没有被破解,可能收入会翻倍,不过那也没多少,和之前打工时的工资,根本无法比。 萌一小栈 欢迎关注微信公众号 萌一小栈,博客文章同步推送

February 6, 2022 · 1 min · fred

游戏码农:那些打工的日子

经过了几个月的开发,我的游戏终于上线了。 《游戏码农:那些打工的日子》是一个轻松的模拟经营文字游戏,游戏的主角是一个普普通通的小码农,刚刚来到一线城市,开始自己的职业打工生涯。游戏拥有丰富的玩法,轻松的升级体验,多种不同的赚钱方式,实现财富自由,指日可待。 目前已经上线 AppStore,TapTap,好游快爆等平台。 AppStore: https://apps.apple.com/cn/app/id1607035933 TapTap: https://www.taptap.com/app/230651 好游快爆:https://www.3839.com/a/141332.htm 找工作 游戏开始需要完善简历信息,然后选择中意的公司进行投递和面试,拿到 Offer 后,不同的公司,给出的薪资略有差距,需要仔细考虑。 租房子 压一付一的租房模式,大大缓解了刚开始打工的年轻人,到底是住的远一点,舒服一点,便宜一点。还是住的近一点,老破小,还贵。 点外卖还是自己做饭 自己做饭,需要先购买食材,然后根据菜谱来做,挑战很大,但是成本低。点外卖,省力,就是花钱有点多。 学习 想要提高自己的专业经验,光打工是不够的,还得多多学习。 休息 在家里的时候,可以打打游戏,看看小说,睡睡觉,打工也是一个长期的活,同时也要照顾好自己噢。 通勤 打车很舒服,消耗体力也小,坐地铁便宜,但是人挤人,应该怎么选择呢?似乎还得看看自己的存款 上班 认认真真完成工作,也别忘记偶尔摸个鱼,或者钱很多可以把自己的工作任务偷偷外包给同事。 投资 光靠打工是很难实现财富自由的,还是投资吧,股票、基金、贵金属等等,多种投资模式自己选择,真实的市场波动。 资产 有些东西可能平时用不着,但是很贵。 赚钱小游戏 偶尔休闲一下,打两把小游戏,顺便赚点钱,当然,也可能输。 萌一小栈 欢迎关注微信公众号 萌一小栈,博客文章同步推送

February 4, 2022 · 1 min · fred

Gameplay 02 游戏中的跳跃

游戏中的跳跃,就是以某一个速度起跳,克服重力。而以不同的速度,起跳的最高点是不一样的,很难去量化,所以我们可以定义每次起跳的最大高度,然后求出起跳速度,即可做到一切可控。 自由落体公式 $h = \frac{1}{2}gt^2$。速度 $v = gt$,这个是瞬时速度,但是自由落体的速度是均匀变化的,所以平均速度就是下落高度中间时刻的速度,所以才得出 $h = \frac{1}{2}gt^2$。 我们的目标是求出跳跃的速度。 首先,假设我们以一个初始速度 $j$ 来跳跃,这个是一个向上的速度,但是因为重力的存在,所以我们的跳跃速度会因为重力的向下抵消,而逐渐趋向于 0。当 $j$ 慢慢被抵消到 0 时,我们也就达到了以 $j$ 这个速度来跳跃所能到达的最大高度。 对于任意时刻 $t$,速度 $v = j - gt$,当 $j - gt = 0$ 时,也就是我们所能到达的最大高度。所以,到达最大高度的时间为 $t = \frac{j}{g}$。 根据上面的公式,我们可以知道,在以 $j$ 来跳跃的整个过程中,任意时刻的高度为 $h = jt - \frac{gt^2}{2}$。 由于我们上面已经知道了,在 $t = \frac{j}{g}$ 时,我们将达到最大跳跃高度。将 $\frac{j}{g}$ 带入到任意时刻的高度公式中 $h = jt - \frac{gt^2}{2}$。我们可以得到 $h = j(\frac{j}{g}) - \frac{g(\frac{j}{g})^2}{2}$。 化简上面的公式将得到 $h = \frac{j^2}{g} - \frac{\frac{j^2}{g}}{2} = \frac{j^2}{g} - \frac{j^2}{2g} = \frac{j^2}{2g}$。...

January 22, 2022 · 1 min · fred

Gameplay 01 游戏开发中的平滑移动

这篇博客,聊一聊在游戏开发中,控制角色或者其他物体的平滑移动。现实中,一个物体,从静止加速到匀速运动状态,或者从匀速运动状态变为静止状态,都会有一个过程。就像人走路,或者汽车开动。 一个物体在运动状态下,会有一个速度变量,也就是我们期望的速度。而从静止到运动状态的过程,会有一个加速度。 而游戏开发中,要做到一个物体平滑的运动,也就是模拟出这个从静止,通过加速度,达到期望速度的过程。 游戏是按帧运行的,在初始的时候,目标运动速度为0,随着每一帧的流逝,目标速度会不断累加一帧中的加速度,当累加到期望速度时,则会以期望速度来运动。 代码如下,这里是在 Unity 中实现的,但是原理对于所有游戏引擎通用 using System.Collections; using System.Collections.Generic; using UnityEngine; public class MovingSphere : MonoBehaviour { // 用户设定最大期望速度 [SerializeField, Range(0f, 100f)] private float maxSpeed = 10f; // 用户设定最大加速度 [SerializeField, Range(0f, 100f)] private float maxAcceleration = 10f; // 当前的运动速度 private Vector3 velocity; // Update is called once per frame void Update() { // 通过读取用户输入,来确定期望速度向量(向量包含了大小和方向) Vector2 playerInput; playerInput....

January 6, 2022 · 1 min · fred

编译 Apple Silicon 版本 Aseprite

编译苹果 M1 芯片版本的 Aseprite,步骤如下 ...

December 1, 2021 · 1 min · fred

《音乐与僵尸:Zombie Rhythm》开发日志

《音乐与僵尸:Zombie Rhythm》经过了一个多月的开发,终于上线了。这是一个融合了丧尸和音乐节奏的休闲游戏。这篇文章就聊一下这个游戏的整个开发过程。 ...

October 12, 2021 · 1 min · fred

《我在美国学游戏设计》笔记

从情感体验出发,探索出最合适的机制来表达情感。 ...

September 27, 2021 · 1 min · fred