Seija
首页
Travellings
登录
注册
首页
文章
引擎中Template DSL的设计思考总结
## 设计目的 在以ecs为基础的系统中我们要用代码一行行的写出场景内的物体,是非常冗长的。例如说我们要创建一个图片就得写这么多东西。 ``` var entity = Entity.New(); var t = entity.addComponent[Transform](); var rect = entity.addComponent[Rect2D](); var image = entity.addCoponent[ImageRender](); entity.addComponent[Transparent](); image.texture = res.image; image.color = Color.white; rect.width = 320; rect.height = 240; ``` 很显然这个时候scala这个语言的语法不适合用来描述这种数据。当然我们可以用宏或者各种语法糖来让他好看一些,但是我们根本不想他过scala的编译器,我们希望它是可以运行时动态加载的**灵活的配置文件**。我们现在需要一种新的DSL,并且我们希望这种新的DSL是一种通用的文本标准,它即可以手写也可以通过场景编辑器,UI编辑器等任何第三方的软件所读写。 我们这个时候可能会联想到Unity的Prefab,事实上我想让我们先忘掉它因为我们要走一条不同的路,我们要的是DSL而不是依赖于编辑器的二进制或者人类无法手动编辑的yaml文件,并且unity的prefab耦合了很多东西如果我们拿他类比的话反而会让问题变的很复杂。 如果非要比较的话我希望他更像xaml或者jsx之类的东西,但是我希望他更基础更渐进一些,也就是说他不会与UI等框架强关联他只是一个最底层基础的东西,我们可以逐步的扩展他,或者换句话说我希望他更无侵入一些,用lisp那边的说法就是自下而上的设计。 在下面我会把我们要的Template DSL简写为TDSL。 ## 我们要的 TDSL有哪些选择 第一种情况非常简单,TDSL只是静态的数据,他不能传递参数,他只是设定好的**静态数据** 。 第二种它是一个函数它接受很多参数,最后根据参数实例化出我们的Entity Tree。 第三种会更复杂一些他是某种**对象或者组件**的树。这些对象被从TDSL创建出来之后会有各种关联和持续执行的逻辑。 现在我们针对三种情况分别讨论一下 ### 静态的数据 我想这种情况是最简单的假如我们用xml来表示一下上面的图片的话 ```
``` 看起来不错但是假如我们想复用这个结构怎么办呢。例如说我们想改一下rect的大小,想改一下引用的图片。也不是没有办法,我们可以先把他实例化出来,然后用代码去修改里面的属性。 但是假如我们想让他支持嵌套呢。例如说我们把这个image配置保存到image.xml里,同样配置一个**Label**保存到label.xml里。然后我们配置一个button.xml ```
``` 我们定义了一个Ref作为特殊标签来引入其他文件。看起来我们的确把它们嵌套了起来。但是我们无法复用的时候修改属性的问题在这种情况下被放大了,假如要修改label的文字我们需要写代码去找到label的entity然后获取到组件再去修改。嵌套的越深找起来越麻烦。 所以我们又引入了id这个东西。引入了id这个东西同样也就引入了id作用域的问题。我们就把id的作用域规定到当前文件吧。然后支持"panel.okbtn.label"这种查找方式。看起来这就是静态数据方式的极限了。 缺点非常明显,设置属性全都得写到代码里。并且我们为了减少设置属性代码的编写,我们可能会写很多"aa_image.xml" "bb_image.xml"结构相同但是属性不同的文件。 我们把这种方式所能提供的功能的极限展示出来了,那么这种方式也就到这里了。 ### 作为一种函数 作为一种函数那么就允许加载TDSL的时候传递参数,我们修改一下Image那个配置看看 ```
``` 看起来解决了复用的时候修改参数的问题。 那么嵌套一下呢 ```
``` 那么怎么把引用的时候的属性传递给Ref里面呢,又得定义规则了。我们把Ref里写的属性通过"params.xxx"这种命名来给子文件用。看起来解决了复用时候属性修改的问题。 现在参数会传递给顶层文件。顶层文件需要把参数通过Ref的属性依次传递下去。听起来还不错,就像函数调用一样。 现在引入一个新的问题,我们上面传递的参数都是组件的属性值,怎么把一个Entity当参数传递给子Template文件呢 这里写出参数模式最终的例子,会包含所有的语法规则。 ``` //Image.xml
//label.xml
//Button.xml
``` 如上所示Ref类型的标签可以通过子元素"Param."的方式传递Entity类型的参数,在引用的Template中通过"UseParam."使用参数。 然后组件也统一放在了Components里面了,更清晰一些。 最后可以属性参数通过"|"的方式连接起来,当左边的属性在params里不存在时向右移动使用下一个 到这里函数模式的Template已经进化完全了,我发现他已经完全完成了初始的需求,并且一个Template其实就是一个函数并且还是无副作用那种,这点让我非常满意。 ### 组件树 其实通过上面参数模式的逐步进化,TDSL已经完成了他职责范围内的所有功能。根据我们开篇提到的自下而上设计原则TDSL不会再往组件树这个方向演近了。 但是我想到了一种完全区别于WPF的数据绑定和elm的mvu模式的一种全新的UI架构模式。就像上面说的TDSL完全可以作为一个底层功能服务于上层的UI框架。等设计UI框架的时候再详细展开讨论。
登录
登录
注册
最热文章
引擎中Template DSL的设计思考总结
10-19
ReaderT 设计模式
04-23
非主流引擎开发不出来 (n+1) : purescript侧结构设计
04-04
FRP系统的设计
03-17
非主流引擎开发不出来 (1) : 轻骨架
02-11
非主流引擎开发不出来 (0) : 引擎定位
12-09
Rust的ECS库specs
11-20
Haskell类型类高级扩展详细说明
05-31
CMake 速览
05-29
尼采导读:超人与永恒轮回
02-24
为什么elm的结构并不是最合理的?
02-20
React速览
02-20
尼采命运之爱
02-18
AspNetCore 速览
02-17
由Haskell和面向对象引出的关于抽象的思考
12-26
二进制文件压缩工具upx
12-24
Reflex介绍
12-17
Web的前端渲染和WebApi
12-16
前端FRP框架深度踩坑
12-16
Yesod - RESTful (11)
12-16
链接
github
gitee