H5 游戏引擎的 Entity 层级设计

大家好,我是 AlphaLu。

这次给大家介绍笔者所在团队自研的 H5 游戏引擎(小世界,一个非常有意思的项目!)的 Entity 层级设计方案,基于层级机制,我们就能灵活调整 Entity 的层级:

需求描述

我们说的 Entity 层级是指视觉层面上的,当两个 Entity 出现在画布的同一个位置,就必然要确定它们间的覆盖关系,而我们希望这种覆盖关系是由我们确定的,并且是可调整的,具体来说我们想要对 Entity 做如下操作: 上移一层、下移一层、置于顶层、置于底层、锁到顶层、锁到底层、释放锁层

方案设计

PIXI 层级机制

PIXI 绘制特点

我们的游戏引擎使用 PIXI 作为渲染引擎,它的绘制特点如下:

  1. 以 Container 作为绘制的单位元素,先绘制的 Container 会被后绘制的 Container 覆盖;
  2. Container 的绘制顺序和其在父 Container 的 children 里的位置顺序一致,因为渲染过程是深度优先遍历 Container 树的;
  3. Container 在其在父 Container 的 children 里的位置由其 zIndex 属性和添加顺序确定,Container 的 sortChildren 方法会将它的子 Container 按照 zIndex 递增的顺序排列,若 zIndex 相同,则默认使用添加的顺序;
  4. PIXI 在渲染过程中,会深度优先遍历 Container 树进行绘制,当发现某个 Container 的 sortDirty 属性为 true,就执行其 sortChildren 方法,再接着遍历绘制;
  5. Container 的 sortDirty 变为 true 通常是因为发生了 addChild 或者更新了其 zIndex。

使用 PIXI

ECSM 架构中,Entity 由 PIXI 绘制,一个 Entity 对应一个 PIXI Container,具体来说就是:

  1. 在创建 Entity 时,引擎会给每个 Entity 内置一个 Transform Component;
  2. 当 Entity 被添加到场景时,引擎将 Entity 的 Transform Component 收集到 Render System,在完成添加后,我们会得到一棵 Entity 树;
  3. 在每帧都执行的 tick 函数里,Render System 会统一处理所有的 Transform Component:先统一给每个 Entity 创建一个 PIXI Container,再统一将这些 Container 添加到对应的父 Container 下,这样我们就得到一棵 Container 树。
  4. PIXI 渲染 3 中的 Container 树,绘制出整个游戏场景。

这个过程有点问题:因为 Render System 是通过列表遍历来处理 Transform Component的,导致 Container 的添加顺序取决于 Transform Component 在 Render System 里的存储顺序,这不是我们所希望的。我们希望 Container 的添加顺序和 Entity 的添加顺序保持一致,因为比如我们想要后添加的 Entity 覆盖先添加的 Entity

Entity 层级机制

我们发现此时的 Entity 树和 Container 树上的节点并不是一一对应的,其实 Entity 树是基于游戏场景逻辑构建的,它的结构才是我们应该关心和维护的,也就是我们应当把 Entity 树的结构信息同步给 Container 树,而所谓的结构信息,就是指各节点在其父节点 children 里的位置顺序。

我们借鉴 PIXI Container 的层级概念,实现 Entity 的层级机制:

  1. Entity 有 zIndex 属性,表征它的视觉层级;
  2. Entity 有 sortChildren 方法,会将它的子 Entity 按照 zIndex 递增的顺序排列,若 zIndex 相同,则默认使用添加的顺序;
  3. Entity 有 sortDirty 属性,表征它的 children 是否发生变化,比如当 Entity 添加了子 Entity,意味着它的 children 发生变化,需要重新排序,那么标记其 sortDirty 为 true;
  4. 引擎会在每一帧内收集 sortDirty 变为 true(通常是发生了 addChild、更新了 zIndex) 的 Entity到一个队列中,然后在下一帧渲染前清空该队列;
  5. 引擎清空该队列的过程就是把队列中的 Entity 逐个取出,执行其 sortChildren 方法,然后再将其 children 顺序同步给其 Container 的 children,这样就能保证 Entity 树和 Container 树的一致性。

需要注意的是,在 5 中同步后,记得把 PIXI Container 的 sortDirty 置为 false,避免 PIXI 在渲染时又重复执行 sortChildren。

Entity 层级移动

基于 Entity 层级机制,我们来实现将 Entity 上移一层、下移一层、置于顶层、置于底层。

现在有一个 Entity 的 children 如下:

js 复制代码
[et1, et2, et3, et4, et5] // children
[  0,   0,   1,   3,   3] // 对应的 zIndex

上 | 下移一层

假设需要将 et2 上移一层,我们就把 et2 往后移一个位置:

js 复制代码
[et1, et3, et2, et4, et5] // children
[  0,   1,   0,   3,   3] // 对应的 zIndex

我们发现此时的 zIndex 并不是递增的,为了保证 sortChildren 后 et2 还是紧随 et3 后,我们需要修改 et2 的 zIndex:

js 复制代码
[et1, et3, et2, et4, et5] // children
[  0,   1,   1,   3,   3] // 对应的 zIndex

置于顶 | 底层

类似地,如果我们想把 et2 置于顶层,就先把 et2 移动到最后面,然后修改其 zIndex 为最大值即可:

js 复制代码
[et1, et3, et4, et5, et2] // children
[  0,   1,   3,   3,   3] // 对应的 zIndex

Entity 层级锁定

基于 Entity 层级机制,我们来实现将 Entity 锁到顶层、锁到底层、释放锁层,这里锁到顶层的含义是该 Entity 在被释放锁层前将永远处于顶层,并且它的层级不会被改变。

现在有一个 Entity 的 children 如下:

js 复制代码
[et1, et2, et3, et4, et5] // children
[  0,   0,   1,   3,   3] // 对应的 zIndex

锁到顶 | 底层

假设需要将 et2 锁到顶层,我们就先把 et2 移动到最后面,然后修改其 zIndex 为 Infinity 即可:

js 复制代码
[et1, et3, et4, et5, et2] // children
[  0,   1,   3,   3, Infinity] // 对应的 zIndex

Infinity 能保证在 sortChildren 后,et2 都是排在最后一个位置。

另外,我们需要在 Entity 层级移动功能的实现中排除被锁到顶层或底层的 Entity。

释放锁层

当我们想要 et2 不被锁到顶层时,就修改其 zIndex 为最大值即可:

js 复制代码
[et1, et3, et4, et5, et2] // children
[  0,   1,   3,   3,   3] // 对应的 zIndex

这样 et2 被释放锁层时,还是处于最顶层,可以避免非预期的视觉突变效果。

总结

本文给大家介绍了 H5 游戏引擎的 Entity 层级设计方案,先描述我们需要的 Entity 层级调整功能,然后分析在 ECSM 架构中单纯使用 PIXI 层级机制的问题,接着设计了 Entity 层级机制,最后讲解基于该机制实现 Entity 层级移动、Entity 层级锁定,欢迎大家交流意见。

相关推荐
Smile_Gently1 小时前
前端:最简单封装nmp插件(组件)过程。
前端·javascript·vue.js·elementui·vue
luckycoke8 小时前
小程序立体轮播
前端·css·小程序
一 乐8 小时前
高校体育场管理系统系统|体育场管理系统小程序设计与实现(源码+数据库+文档)
前端·javascript·数据库·spring boot·高校体育馆系统
懒羊羊我小弟8 小时前
常用Webpack Loader汇总介绍
前端·webpack·node.js
祈澈菇凉8 小时前
ES6模块的异步加载是如何实现的?
前端·javascript·es6
我爱学习_zwj9 小时前
4.从零开始学会Vue--{{组件通信}}
前端·javascript·vue.js·笔记·前端框架
顾比魁9 小时前
XSS盲打:当攻击者“盲狙”管理员
前端·网络安全·xss
黑客老李9 小时前
新手小白如何挖掘cnvd通用漏洞之存储xss漏洞(利用xss钓鱼)
java·运维·服务器·前端·xss
晚风予星9 小时前
简记|LogicFlow自定义BPMN元素节点
前端
Json____10 小时前
使用html css js 开发一个 教育机构前端静态网站模板
前端·css·html·js·前端学习·企业站·教育机构网站