惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Visual Studio Blog
Martin Fowler
Martin Fowler
aimingoo的专栏
aimingoo的专栏
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
Cybersecurity and Infrastructure Security Agency CISA
C
Cisco Blogs
S
Securelist
博客园 - Franky
P
Proofpoint News Feed
量子位
雷峰网
雷峰网
Security Latest
Security Latest
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Latest news
Latest news
L
Lohrmann on Cybersecurity
W
WeLiveSecurity
月光博客
月光博客
Hacker News: Ask HN
Hacker News: Ask HN
宝玉的分享
宝玉的分享
GbyAI
GbyAI
小众软件
小众软件
M
MIT News - Artificial intelligence
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
Secure Thoughts
The Cloudflare Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
IT之家
IT之家
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
Threat Research - Cisco Blogs
Stack Overflow Blog
Stack Overflow Blog
有赞技术团队
有赞技术团队
Attack and Defense Labs
Attack and Defense Labs
Y
Y Combinator Blog
Scott Helme
Scott Helme
O
OpenAI News
Know Your Adversary
Know Your Adversary
AWS News Blog
AWS News Blog
阮一峰的网络日志
阮一峰的网络日志
A
About on SuperTechFans
云风的 BLOG
云风的 BLOG
V
Vulnerabilities – Threatpost
博客园 - 【当耐特】
K
Kaspersky official blog
Microsoft Azure Blog
Microsoft Azure Blog
S
SegmentFault 最新的问题
Forbes - Security
Forbes - Security
腾讯CDC
NISL@THU
NISL@THU
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
T
Tailwind CSS Blog

重归混沌的BLOG

给silly实现了一个ernro模块 | 重归混沌的BLOG 给silly实现了一个ernro模块 | 重归混沌的BLOG 第一次在生产环境使用 Vibe Coding | 重归混沌的BLOG 第一次在生产环境使用 Vibe Coding | 重归混沌的BLOG API 设计的艰难抉择 | 重归混沌的BLOG API 设计的艰难抉择 | 重归混沌的BLOG 十年 | 重归混沌的BLOG 十年 | 重归混沌的BLOG 在Go语言中如何使XML加载内存无限趋近于0 | 重归混沌的BLOG 在Go语言中如何使XML加载内存无限趋近于0 | 重归混沌的BLOG 对跨服玩法中的分布式一致性问题进行简单抽象 | 重归混沌的BLOG 对跨服玩法中的分布式一致性问题进行简单抽象 | 重归混沌的BLOG Go语言逃逸分析之slice和map | 重归混沌的BLOG Go语言逃逸分析之slice和map | 重归混沌的BLOG 谈谈观测 | 重归混沌的BLOG 谈谈观测 | 重归混沌的BLOG 写了个AI Agent服务端 | 重归混沌的BLOG 写了个AI Agent服务端 | 重归混沌的BLOG 谈谈代码设计中“严丝合缝” | 重归混沌的BLOG 谈谈代码设计中“严丝合缝” | 重归混沌的BLOG 一次艰难的线上游戏服务器内存排查经历 | 重归混沌的BLOG 一次艰难的线上游戏服务器内存排查经历 如何基于LanguageServerProtocol来编写lint工具 谈谈游戏服务器中RPC模块的设计 谈谈游戏服务器代码抽象 最近碰到的一个分布式一致性问题 谈谈游戏服务器的自动化测试 对Raft协议的一点理解 使用mmap来学习/proc/pid/smaps 2023(完) 再次实现了一个Lua性能分析器 终于给Silly的定时器增加了取消功能 一次虚拟内存排查经历 游戏服务器分布式数据的一种同步的思路 为silly增加了互斥锁 2022(完) Go语言之闭包篇 一例误用unsafe包引起的内存问题 Go语言之内存篇 初识Go语言 重新抽象图形API 给Lua实现了一个数学库 谈谈跨平台图形API的抽象 寻路和Flocking算法的结合 行为树的一种高效实现 内测过程中Shader出现的问题 彻底解决多国语言 谈谈数据库的选型 再谈Lua热更新(终) 初窥Rust 关于游戏服务器的服务拆分 ECS的初步实现 屏幕空间(SreenSpace)的想象力 一些对辐射度量学的理解 深度缓冲和半透明渲染 Mysql的间隙锁 更新一些GPU相关知识 2020 地形渲染之爬过的坑 Lua5.3 GC源码阅读(5) 实现一个数据库存储队列 再学计算机图形学入门 再谈分布式服务架构 游戏上线一个月后的反思 一次并发Bug 双向链表的三种实现 谈谈随机数的使用 再谈性能优化 2019 Lua中的函数式编程 重构登录逻辑 Unity资源管理(续) 谈谈Unity的资源管理 一次关于Cache的性能分析 历史之2018 DC3算法 移动平台native代码遭遇的坑 从CPU层面谈谈优化 开卷有益(UNIX编程艺术篇) GC竞争问题 通过Mesh投影来实现贴花系统 谈谈我对数据同步的理解 又一个类型提升引起的Bug Lua5.3 GC源码阅读(4) Lua5.3 GC源码阅读(3) Lua5.3 GC源码阅读(2) Lua5.3 GC源码阅读(1) 三角形光栅化时遇到的坑 一次git事故 再见2017 又一个lua调试器 客户端缓存落地方案 Paxos算法 HTTP服务器的特点 一次性能优化经历 关于CPU分支预测 C程序中让两个不同版本的库共存 实现了一个AOI模块 一个高可伸缩的游戏服务器架构 关于网络协议封装的一些新想法
ECS初探
重归混沌 · 2021-08-04 · via 重归混沌的BLOG

开始之前先说两句题外话。


GAMES202的作业4是白炉测试,但是我到目前为止还没有做.

其主要原因是,关于GGX BRDF我有点迷惑了.

本来按照LearnOpengl和其他参考书里面讲的, 一般光照计算会分为两部分. 一部分为Diffuse, 一部分为Specular.

Diffuse又可以看作是次表面散射的一种简化。这样我们可以用菲涅尔反射, 来计算一束光线有多少反射出去(BRDF项),有多少进入物体内部进行次表面散射(Diffuse项), 然后把两部分加起来就行了。

但是闫神讲课时说:由于已经采用了微表面模型,就不能在与宏观表面模型Diffuse的假设一同采用,同样在物理上也是错误的,能量不能保证守恒,可能会出现发光的BRDF的情况。由于不同角度、不同粗糙度损失的能量是完全不同的,因此直接加一个Diffuse是完全错误的。计算机视觉识别材质采用了这种方法。如果你用了这种做法,别说闫神教过你。

这话一说,一下子就给我整不会了, 以致于我到现在还没弄明白到底怎么是对的,迟迟没办法做白炉测试。

我可能需要GAMES202的同学来讨论一下:D。


GAMES202告一段落之后,就顺便学习了一下Unity的SRP,教程使用的是catlikecoding

老实讲,我在看这个教程的过程中只有一个体会,心累(当然这并不是教程的问题)。

我最开始对Unity的SRP期望是这样的:在C#中有一些库函数,并且在Shader端也有相匹配的库函数。当我需要成熟的功能时,我调一下C#的函数,然后在Shader中再调用相应的Shader库函数。就可以直接使用他的某个功能了。

然而并不是这样,尤其是catlikecoding上来就搞阴影。Unity中的C#是有一些API可以给我们用,Shader也会有一些内置变量,直接被设置好了。但是怎么用这些变量,是需要我们有足够的Unity知识之后才能应用的。它并不像是一个封装良好的库函数。

这让我在学习过程中很疑惑,到底有多少个Shader内置变量,他们分别是被哪些API进行修改的。我并没有发现一个很好的文档,可以让我根据某个C# API来查询,他会修改哪些Shader变量,这些Shader变量都是什么含义。

这就像盲人摸象一样。以至于我很怀疑,如果我们要做一个项目。到底是应该根据SRP写自己的RenderPipeline, 还是应该魔改URP的RenderPiepline。如果Shader的内置变量五花八门,修改他们的API也很多。那势必就会踩很多坑。如果这样,还不如魔改URP来的安全。

不过在看完整个教程后,我发现SRP除了提供一些基础的渲染功能外,主要额外提供的辅助就实时阴影和烘焙相关部分。这些信息量并不算大,所以上面提到的坑问题也就不存在了。


下面开始进入正题。

关于ECS,我大概花了一周时间来学习理论知识。学习时间尚短,大概率我现在的感受都是错误的,不过我认为还是值得记录下来,以备后面反思时使用。

ECS早已有之,但是它真正在国内火起来,应该要从《守望先锋》架构设计和网络同步算起。

在看完《守望先锋》架构设计和网络同步之后, 我接着看了一下Wiki

Wiki给了一个渲染方面的例子: “一个“系统”,它遍历所有具有物理和可见组件的实体,并绘制它们。可见组件通常可以包含一些关于实体外观的信息(例如人类、怪物、四处飞舞的火花、飞箭),并使用物理组件知道在哪里绘制它。另一个系统可能是碰撞检测。它会遍历所有具有物理组件的实体,因为它不关心实体是如何绘制的。”

乍一听,觉得ECS就是完美啊,就跟当年他们教我OO时,给我举例子做UI一样,各种继承,各种多态,简直完美啊。

但是,历史的经验告诉我OO在非UI领域一点也不好用,以致于他们要出各种设计模式来解决OO带来的坑。

不管怎么样,即然大家都在吹ECS,它肯定是有过人之处的。

抱着试试看的态度,我模拟把我们游戏的客户端逻辑使用ECS进行落地。

第一关就给我难住了,Component到底该如何拆分,拆分粒度是多大。上一次这么手足无措,还是在大约12年前, 我在实模DOS下,往0xB800(显存)地址处写入ASCII码,但是屏幕什么都没有显示。同样的没有经验,同样的资料匮乏。

直到我看到A Data-Driven Game Object System中的一个句话“Each component is a self-contained piece of game logic”,我猛然间醒悟了,我们需要根据业务需要,设计System逻辑,然后根据System来拆分Component(也许叫设计Component更好, 之所以叫拆分是因为我在模拟怎么用ECS实现我们客户端的所有功能, 拆分这个词,在一定程度上其实误导了我)。

我回忆了一下,在日常逻辑的开发中,尤其是已经上线的项目。在新增一个系统时,我往往会单独设计他的数据结构,并存储在数据库的不同位置。而所有系统最终是通过UID这个entity_id来关联起来的。

举个例子:假如我们有一个Bag系统和一个Mail系统,我们的代码组织往往会类似下面情况:

//Bag.cpp
namespace bag {
static std::unordered_map<uint32_t, db::bag> bags;

void bag_add(uint32_t uid, int money, int count)
{
    auto &bag = bags[uid];
    add money into bag and save db
}

}

//Mail.cpp
namespace mail {
    std::unordered_map<uint32_t, db::mailbox> mailboxes;

    void mail_fetch(uint32_t uid, uint32_t mailid)
    {
        auto &mb = mailboxes[uid];
        auto &m = get_mail_by_mailid(mb, mailid);
        bag_add(uid, m.attach.money, m.attach.count);
    }
}

对比可以发现,这其实和ECS的模型很像,只是ECS模式约束更严格,System之间不允许相互调用。

上面这个系统本来就是松散耦合,再举个更复杂的例子,我前几年写的回合制战斗系统。

在整个战斗系统中,buff,hurt,heal,skill这些计算逻辑,往往会操作着hero不同部位的数据。这些计算逻辑读取的数据区域可能会相互重叠,比如hurt,heal都需求读取hero的属性值,而hurt往往还会读取部分buff的属性以便做伤害分摊。

如果按照OO的思路,hero类往往会持有buff,hurt,heal,skill等类的实例,但是由于这几个系统往往需要相互读取对方的部分数据,以至于buff,hero,heal,skill中往往还会持有一个hero的指针, 这样到处都是循环引用。不但不能解耦合,还会让问题变的更糟糕。

对于这种强耦合的逻辑,我采用了Lua虚拟机的实现方式,我把所有用到的数据全部定义成结构体,然后把buff,hero,heal,skill全部实现为纯逻辑,这些纯逻辑可以直接访问它们需要的任何数据结构。

这样只要我能定精准定义好每个结构的字段的含义,各种逻辑都根据数据的含义来执行相应的计算就好了,模块之间大幅解耦,我想这也是贴近ECS模型的一种实现。同样它也不是ECS,因为逻辑模块之间有相互调用。

但是我想使用ECS来实现业务逻辑时,和以上两种实现模式的思路或多或少都会有相似之处,尤其是第二种,感觉更相似。

但我有两个疑虑:

1.因为战斗系统是我一个人开发的,我当然可以从全局精心设计出合适的数据结构。但是如果在多人协作情况下,除非像例子1那样,本来就是松散耦合,否则我对能否设计出合适的Component数据结构是存疑的。

2.因为System之间不进行直接交互,所有交互都是通过Component进行的,这会造成全局变量陷阱。回忆一下,我们刚开始写代码时,都被谆谆教导不要使用全局变量,这是有原因的。

不管怎么样,我打算先实现一个Lua版的简易ECS框架,真实体验一把再说。毕竟没有使用就没用发言权。