网站公告 | 全新unity3d 完整学习路线,最强课程配套、服务!详情点击
查看: 3875|回复: 0
收起左侧

[已翻译] 替换Unity3d的核心架构:结构体

[复制链接]

[已翻译] 替换Unity3d的核心架构:结构体[复制链接]

胖啊 发表于 2018-1-2 16:07:07 [显示全部楼层] |只看大图 回帖奖励 |倒序浏览 |阅读模式 回复:  0 浏览:  3875
译者: 王磊(未来的未来)   审校:崔国军(飞扬971)
(这是有关将Unity3D转换为一级实体组件系统的一系列帖子之一。

该如何存储游戏数据:使用 GameObject? 还是MonoBehaviour?还是ScriptableObject?

Unity的架构的这三个支柱是对游戏实现和性能的诅咒。。。在架构上,他们是伟大的:他们是为什么Unity易于新手使用,能让新手在几个小时内而不是几个星期理解上手的大关键因素。
在大型的Unity游戏项目中,他们被嫌弃,因为太慢,很难在一个场景中达到几十万(或数百万,数千万,...)的量级的时候还能正常工作。Unity没有为这种情况提供工具和支持 -它只是变得越来越难以使用,运行速度越来越慢。
在实体系统的世界中,我们讨厌他们,因为它们本来就非茶馆慢和有bug-无论Unity的编码器有多大,他们永远不会使它们像我们的首选方法那样工作的非常顺利(接着往下看)。
但是。。。替换它们迫使你替换大部分的Unity编辑器!
我们的实体系统的大多数功能需要新的编辑器GUI。如果我们要向编辑器添加大块代码的话,替换GO / MB / SO会比正常情况便宜得多。 它可能具有独立于实体系统的巨大益处。
如果我们要替换它们,我们将用什么替换它们? 具有几乎相同名称和工作方式的自定义版本? 还是会有什么不同?

到底什么是Unity的'GameObject'?
据我所知,GameObject的真正含义是:
GameObject的定义:场景中存在的东西,它有一个位置信息、旋转信息和缩放信息。它可以“嵌入”在任何其他的GameObject内部,这意味着对其他对象的位置信息、旋转信息和缩放信息的任何更改都将合并到此对象上。这是可以将Unity“组件”(MonoBehaviours)附加到自己身上的唯一一样东西。 任何不是GameObject且不附加到GameObject的东西,每次加载/重新加载/播放/恢复场景的时候都会被删除。
从代码的角度看,看看后端系统,还有很多东西。 GameObject(GO)有一个稍微奇怪的名字,当你仔细考虑这个事情的话。你在Unity开发的过程是如此频繁的使用它,以至于你没有注意到它的奇怪之处。”游戏。。。对象”。。。?

GameObject服务的其他目的
这里有一些明显的目的(如果我想到更多的话,我会稍后进行编辑):
  • 保证一切对象都有GetInstanceID()方法。
  • Unity的序列化系统需要它才能正常工作(这是因为设计的选择,而不是因为序列化需要这个-C#有内置的系统,可以做同样的工作)。
  • 使编辑器能够“选择”任何标签/窗口中的任何内容,并在所有其他标签/窗口中选择该内容(例如,在场景中单击,检查器会更新以显示该内容上的组件)。
  • 。。。可能加载的其他东西。它是如此常用,我几乎没有注意到。
  • 当进行编辑器内部处理的时候,Unity往往可以“忽略”那些不是游戏本身的类,因为这些类不扩展GameObject。
  • 在官方文档的行之间进行读取,并且大大简化,这是在幕后发生的。我怀疑特别是在有序列化发生的情况下。
  • 这可能是一个巨大的不便。当“GameObject子类”很神奇的表现的与“所有其他类”都不同,也没有什么指示,这很难调试代码。
  • 由于每个GO都有一个变换组件。。。你可以做自动、通用、高质量的遮挡剔除。
  • 注意:为了完美地工作,你也希望每个GameObject有一个.bounds变量。 为什么Unity最初的架构师会没有这么做呢? 对不起,我不知道。

如何存储游戏数据:实体系统是一种理想的方式

我们知道这一点,它已经被频繁的讨论过了:如果你的编程语言支持它的话,那么就使用结构。
根据定义,结构体是对内存使用来说最高效、CPU使用最高效、多线程安全、类型安全的数据存储方式。在主流语言中没有比它更好的方法。C#和C ++都支持结构体。我想我们终于找到了一个合适的!
可能有一些非常棒又非常聪明的高级数学方法可以做到更好的效果,或通过使用原始汇编和代码优化来对市场上的每个CPU单独做一个处理。 或者,或者。。。但是:在“正常”的和常用的方法里面,这是你能得到的最好的方法。
但这在实践中如何工作? 在Unity中GO的所有“其他”角色都是怎么样的?

结构体的。。。。ID(GetInstanceID)
简单的ECS实现通常把它们的原始指针;交给它们的组件,但是这样做的话,那会弄脏ID。
但是大多数(所有?)高级实现都有某种间接方法-一个智能指针,或是一个整数“索引”,可以通过这个整数“索引”将组件存储/管理器映射到内存中的实际点。
如果我们把“高级”特征变成一个要求,那么我们应该可以很好的解决这个问题。

结构体的。。。序列化
  • 开始编写你自己的Unity 序列化系统的版本,它只用于结构体。
  • 同一天完成。
  • 发现它的运行速度比Unity内置的系统快10倍。
  • 。。。具有更少的错误。
  • 利润!
结构是最简单的你可以尝试序列化和反序列化的事情。Unity的开发者可能希望他们可以限制我们把所有的东西都交给结构来解决-它会使他们的生活在某些方面更容易。

用户编写的结构:特殊规则?
我们需要为游戏中出现的用户编写的结构添加特殊的“规则”吗?
不! 事实证明,C#的内置反射系统能够检查每个结构,显示我们每个字段/属性/等等,并让我们轻松地读取和写入它们。不管是私有的还是公共的都没有问题。

警告:结构体会使得C# 的.SetValue()失效
是的,这很糟糕 – 但这个问题与Unity无关,是由于核心C#所引擎的。
当我们实现实体系统的时候需要小心。但它对用户代码/游戏代码没有影响。

结构体的。。。场景视图(变换)
没有! 就是不行!
这是我们将要解决的Unity的架构错误之一:我们不需要一切物体都有一个变换组件。
然而,它预示着一个更微妙的问题,我们最终将必须处理:
实体系统是一种基于表/关系/ 关系数据库管理系统的方式来存储和访问数据。这样的系统在存储和检索树结构数据(例如面对对象编程的类+对象)方面非常低效。
Unity场景中的变换层次结构是一个巨大的树。事实证明,它实际的结构比它需要的更大(因为Unity的规则,一切物体都必须有一个变换组件)-但我们在游戏开发中使用这种树状结构浪费了所有的时间。所以这是一个问题,我们必须回来解决掉。

更大量的存储:结构处于什么位置?

结构位于最底层,单个的ECS组件我们将使用结构体。
但是我们如何存储这些结构体?
为你的ECS选择正确的聚合数据结构本身是一个主要议题。 C#给了我们(几乎)完全的控制力,我们可以以我们想要的方式来做这个事情。
在这一点上:我不确定。
作为一个临时解决方案,我将专注于使存储系统可替换,以便我可以快速实现一个初级的版本,然后轻松地用更好的东西进行替换 - 而不必重写任何其他系统。

它能正常工作么?

是的。我试过这个系统 - 我写了一个完全替代UnityInspector选项卡的东西,只适用带有组件的实体系统,其中”组件”是任意的结构体。这很丑陋,但是它能正常工作:
DSC0000.png
一切都是自动生成的(包括颜色,每个结构都获得了一个全局唯一的背景颜色,我发现当你有成百上千的组件类型的时候,它会变得更容易)。
唯一棘手的问题是C#的FieldInfo.SetValue(...)的问题,如上所述。

进一步的阅读

。。。这里面的一些内容我可能会在以后的文章进行讨论:

【版权声明】
原文作者未做权利声明,视为共享知识产权进入公共领域,自动获得授权。

+1
3867°C
沙发哦 ^ ^ 马上
因分享而快乐,学习以自强!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

VR/AR版块|Unity3d|Unreal4|新手报道|小黑屋|站点地图|沪ICP备14023207号-9|【泰斗社区】-专注互联网游戏和应用的开发者平台 ( 浙ICP 备 13006852号-15 )|网站地图

© 2001-2013 Comsenz Inc.  Powered by Discuz! X3.4

1
QQ