Dungeon Mizarka 006

程序码端基础建设

Zenject简述

UI的版面配置暂时来说是足够的,接下来要开始将实际的功能逐一加进来。而为了要达成这部份,还是要花些时间回到程序码端建构类似於Manager概念的物件。而这部份就用Zenject当做基底。Zenject是一套相依性注入的简易框架,利用它当做基底可以有效解决相依性拿取、引用的问题。

解决相依性的套件最大的帮助是解决滥用Singleton的问题,不过探讨这部份不是此开发记录的重点。总之使用了Zenject後,已经有好几年没有在Unity的Runtime中弄出任何Singleton了。有兴趣的开发者可以自行研究,这套件的资讯在很多日文Unity开发网站上均有深入的使用内容可以参考,而英文网站则是比较简易使用的资料,对於初学者有相当大的帮助。

有些可惜的是这套件目前处於消极维护的状态,也是少数几个一、二年没有什麽重大更新(真的没有什麽更新),但依然是在制作专案时少数几个会率先加进来的外挂之一(另一个就是UniRx,後续会有机会介绍)。

而现在加入这些套件可以直接利用OpenUPM,在package.json里直接放入下列这段,基於某些历史原因,放在OpenUPM的名称是Extenject而不是Zenject,虽然名称有异但为同一套外挂

"scopedRegistries": [
  {
    "name": "package.openupm.com",
    "url": "https://package.openupm.com",
    "scopes": [
      "com.svermeulen.extenject"
    ]
  }
]

由於这是微框架性质的套件,每个人用法不同,就不再这里赘述。该外挂里的范例虽然以实际开发角度来看不太合理,但很适合拿来了解Zenject怎麽使用。

加入Zenject

为了让Code端有个比较穏定的架构,导入Zenject进行Manager的制作。由於是多场景的使用结构,故以SceneContext为主体,搭配ProjectContext进行全场景功能引用。

实际架构

为了之後会用Addressable进行多数资料拿取的动作,参照了官方影片,进行了一些调整

从影片中3:04的截图包含了整个架构的概观

将主要载入资料的Scene划分出来,也就是提供了一个Loader(Initialization)概念的Scene。而这个Loader并没有做太多事,只是下载Addressable资料。而这个资料里包含了主选单和实际游玩场景。

而Loader会利用VS进行载入Addressable的动作,这里利用VS最主要的考量是它的状态机和弹性。多数时间利用VS所得到的弹性是可以胜过效能上的损失,更不用说利用视觉化的状态机可以达到很不错的分类、分批执行,但VS也是有令人觉得不方便的地方,像是namespace一改,引用的function就整坏掉。

这让我想起很久以前的Unity版本,好像是4.x版,那时可以用C#的namespace了,但调整namespace的名称也会让元件跑掉是一样的。

而另一点则是Regenerate Units会因为专案里的外挂或是asmdef数量太多而导致时间拉长,真的很久。多数时间都会落在10分钟左右。如果几天才Regenerate一次,累积的时间也还好,看起来像是一杯咖啡的时间。但用VS的经验,特别是前期开发时,不停的增加新的型别、型别函式,都需要进行Regenerate,VS端才会有相对应的改变。因此,一天会Regenerate很多次,倘若一天六到七次就一个小时没了。

不过它特有的弹性也还是很适合不确定性高的开发,就算是有些不便利,也还是可以接受。

调整加入Zenject除了处理程序码端的相依性,也连带着将VS会需要用到的相依性一并处理。不过,处理VS端没有办法像程序码端那縻方便。程序码端原则上利用这样的写法即可

[Inject]
public void SomeFunc(
    IRefType1 refType1,
    IRefType2 refType2)
{

}

然而要放到VS里,只能利用已拿取到的相依性型别,再自行注入到VS的Variables元件里

[Inject]
public void SomeFunc(
    IRefType1 refType1,
    IRefType2 refType2)
{
    var v = gameObject.GetComponent<Variables>();
    v.declarations.Set("refType1", refType1);
    v.declarations.Set("refType2", refType2);
}

虽然调整成利用Zenject很花时间,但为了之後全数都进入到Addressable中进行载入和引用,这样调整是必需的。

Addressable使用补充

使用Addressable有时会将Handler存下来以供後续移除使用,然而移除是以Async方式进行。若是移除的时间点是在Dispose里,会因为Dispose本身并不是也不能以Async方式进行清除。只能依赖其它的机制进行。

这几篇有看来相当不错的解决方案

在没有C# 8.0的规范下一样可以自行处理。

不过,没有试过,不清楚

  • 能否在Unity里使用
  • 能否在Mobile里使用

或许比较好的方式是清除动作不要留到Dispose里进行。


<<:  【Day18】浏览器物件模型--BOM

>>:  Day 3 跑一下 Tensorflow 范例

Day 6 - Function 时空旅行 (1) - 参数优化

前言 Array 跟 Object 两兄弟的故事告一段落了,接着是 Object 在外面养(?)的另...

css z-index

今天来说如何在css中改变各个物件的阶层,需要用到z-index这个语法 首先创造出三个交错重叠的方...

Tortoise SVN 环境建置

1.下载 https://tortoisesvn.net/downloads.html 2.安装 :...

Day16 - 准备 GitLab 的 GitOps 环境

GitOps 介绍 GitOps 是一种 DevOps 的解决方式,方法是以 Git 为中心,将应用...

第13章:分析、储存与存取系统日志介绍(二)

前言 继上次介绍了系统日志与相关的介绍後,接下来介绍该如何使用journalctl将日志档案永久的储...