Hi there 👋

  • 欢迎来到我的Blog,我是 Fred
  • 目前是一个独立开发者
  • Let’s make something fun.

理解 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

在过去的一个月里

已经一个多月没有写 This Week In Moeif 的周记了,在过去的一个月里,发生了很多事情。年前我们早早的回家了,不过游戏开发的工作依然在不断进行着。年后也过了正月十五才回的上海。 从做独立开发者开始,生活就没有了假期与工作日的明显界限,因为时间都是自己的,做的事情也是自己想做的,并且所做的一切,也都是自己的积累。有时候也会忙到很晚,但是明显没有打工时的那种身心疲惫。 1. 游戏码农:那些打工的日子 在过去的一个月里,首先我们上线了《游戏码农:那些打工的日子》这个游戏。并且这个游戏已经有了广告收入,虽然不多,但是已经开始有了。2月份的收入如下图 这个游戏可以目前安卓和iOS版本都已上线,可以直接在Taptap,好游快爆,AppStore等搜索 游戏码农 即可找到,也可以直接从下面连接进入应用平台。 Appstore: https://apps.apple.com/cn/app/id1607035933 TapTap: https://www.taptap.com/app/230651 好游快爆:https://www.3839.com/a/141332.htm 2. 小镇危机:来自丧尸的问候 昨天,我们又上线了另一个游戏《小镇危机:来自丧尸的问候》。这个游戏的资源包是买的,但原版是英文版,我们自己改成了中文版,然后加入了排行榜系统,兑换码系统。目前游戏也已在安卓和iOS平台上线。 AppStore: https://apps.apple.com/cn/app/id1611293156 TapTap: https://www.taptap.com/app/231533 好游快爆:https://www.3839.com/a/141899.htm 3. 接下来的规划 就在今天,我们确定了接下来要开发的一个项目,我们乐观的预计,会比前两个游戏拥有更多的用户量,以及更多的收入。敬请期待。 4. 另外欢迎加入我们的 Moeif Games 玩家交流群:163359029 萌一小栈 欢迎关注微信公众号 萌一小栈,博客文章同步推送

February 28, 2022 · 1 min · fred

Roguelike In Rust 05: 生成地牢

这一节我们将会创建一个地牢,地牢由不同的房间组成,房间与房间之间是联通的,玩家可以行走。下面是最终运行效果图。 ...

February 22, 2022 · 4 min · fred

Roguelike In Rust 04: 地图与行走

接着上一节的内容,这一节将创建地图,然后玩家可以在地图中行走。地图分为可行走区域和不可行走区域。我们可以认为地图是由一个一个的格子组成。 先看一下最终的效果 ...

February 8, 2022 · 4 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

Roguelike In Rust 03: 实体和组件

对于游戏开发者,可能或多或少听说过或者使用过 ECS 的结构。现在大多数游戏使用的还是面向对象的模式,面向对象编程并没有什么问题,但是在游戏开发过程中,当游戏不断的超出原有的设计时,从整个工程的扩展来说,ECS 拥有更好的扩展性。 下面是一个面向对象的例子 BaseEntity Monster MeleeMob OrcWarrior ArcherMob OrcArcher BaseEntity 是一个基类,包含了基本的数据和代码,用于表示地图上的一个实体。Monster 是一个怪物,MeleeMob 是一个近战单位,它会发现近处的目标,然后干掉它。ArcherMob 是一个可以远程攻击的单位。完成这样一个实体逻辑,可能首先需要按照不同的单位,将逻辑分开,并且抽出共同的逻辑,放到基类中。如果这时增加了一个兽人的逻辑,它即可以近战,又可以远程,并且在完成某些任务时,会变得更友好,怎么办?那就改逻辑呗,继续抽象,分离和整理代码。有很多已经上线的游戏是这样做的。但是,如果使用 ECS,这样的扩展会变得更加容易。 Entity Component 的模式会消除面向对象模式中的层级结构,取而代之的是使用 Components 来描述一切东西,例如一个兽人,一只狼,一瓶药水等等。Component 只是数据,给一个实体,添加多个 Component。 例如,我们可以创建一个实体,然后赋予它一些 Components,Position,Renderable,MelleAI,RangedAI,Hostile,Friendly 等等。对于近战单位,可以给它 MelleAI,Renderable 等。对于远程单位,可以给它 RangedAI,Renderable。对于兽人,在完成任务前,它有 Hostile 组件,而完成任务后,会变得更加友好,那就移除 Hostile 然后添加 Friendly。可以按照游戏逻辑随意地去组合。 ECS 中的 “S” 叫做 System。System 会从 Components 中读取数据,然后执行自己的逻辑代码。例如在单位的显示逻辑中,用于显示的 System 会从单位的 Position 和 Renderable 组件中拿取数据,然后用于在地图上显示实体。 对于小型游戏来说,在使用 ECS 时可能会感觉比面向对象写了更多的代码,前期确实是这样,但是对于后期添加逻辑,ECS 就变得容易了很多。 使用 ECS 很重要的一点就是要知道 ECS 只是处理组合的一种方式,但并不是唯一的方式。对于实现功能来说,并没有什么唯一的方式,在了解了原理后,按照自己的习惯和喜好来就行。 接着上一节的 hellorust 工程,使用 Specs 库 cargo add specs cargo add specs-derive Cargo.toml...

February 3, 2022 · 6 min · fred

Roguelike In Rust 02: Hello Rust

Rust 算是一个新兴的语言,近两年非常火了,但是他首次出现是在 2010 年。Rust 有非常好用的工具 cargo 和 rustup。 如果要学习 Rust 这门编程语言,也有很多优秀的教程。 The Rust Programming Language Book Learn Rust by Example 24 Days of Rust Rust’s Ownership Model for Javascript Developers Rust 编辑器可以使用 VSCode 在终端中使用命令 cargo new hellorust 可以创建一个名为 hellorust 的工程。src/main.rs 是起始文件。Cargo.toml 是工程的配置文件,里面包含了一些其他库的引用信息。 此时 src/main.rs 的代码如下 fn main() { println!("Hello, world!"); } fn 是函数的关键字 main 是主函数名,也就是整个程序的起始函数。 println! 是一个打印宏 使用命令 cargo run 可以编译并运行工程。 一些有用的 cargo 命令 cargo init 初始化一个新的工程 cargo build 会下载所有的依赖库,并且编译整个项目 cargo update 会根据 Cargo....

February 2, 2022 · 1 min · fred