共计 12380 个字符,预计需要花费 31 分钟才能阅读完成。
简介: 本文以「余额宝 3D 跑酷游戏」为例,介绍了前端如何疾速上手 Web 3D 游戏的开发。
作者 | RichLab 楺楺 诚空
本文以「余额宝 3D 跑酷游戏」为例,介绍了前端如何疾速上手 Web 3D 游戏的开发。跑酷游戏是余额宝七周年的主玩法,用户通过做工作来获取玩游戏的机会并且解锁游戏道具,从而在游戏中取得更多的金币,最终能够利用金币兑换一些权利,同时咱们也在游戏中植入了一些礼包,先看看具体成果。
游戏设计
咱们把游戏的 3D 场景分成了三大模块,别离是赛道、金币(道具)和人物。
赛道设计
赛道蕴含了楼房和高空,因为人物须要不停地往前跑,基于相对运动的原理,咱们复制了两段楼房(如图 1),并同时做逆时针旋转,当旋转至 -theta 角度的时候,把楼房的旋转角度置为 0(如图 2)。高空是一个静止的圆弧模型,通过扭转纹理的 UV 值来实现高空滚动的成果。
图 1 赛道结构图
图 2 楼房静止轨迹
金币布局
由以上图 1 可知,咱们以 theta 角度的圆弧为一个管制单元,咱们心愿能管制游戏的总时长、每段圆弧旋转的工夫,以及每段圆弧摆放的金币行数,这些参数如何管制 3D 场景的运作呢?依据已知字段推导出以下几条公式(蓝色字段为可配参数):
- 须要生成金币的总行数 =(游戏总时长 / 圆弧旋转 theta 角度的工夫)x 每段圆弧摆放的金币行数
- 每两行金币之间的工夫距离 = 游戏总时长 / 须要生成金币的总行数
- 每行金币呈现的工夫 = 每两行金币之间的工夫距离 x 金币索引
这里次要得出 游戏总时长 和 每行金币呈现工夫 之间的关系,而每行金币该如何摆放以及道具呈现的机会由具体的业务逻辑管制,这里不开展来讲。最终咱们失去了一个管制金币摆放的队列:
`[
{
"index": 0, _// 索引,代表每一行_
"item": {
"position": "center", _// 摆放地位_
"type": "coin" _// 应该摆放的模型类型_
},
"time": 0 _// 每行金币呈现的工夫_
},
{
"index": 1,
"item": {
"position": "left",
"type": "coin"
},
"time": 0.25
},
// more……
]`
这个队列如何与咱们的 3D 场景关联呢?
由以上图 2 可知,一共有两段圆弧在交替旋转,假如每段圆弧摆放的金币行数定义为 rowsPerPart,以后圆弧的索引定义为 index,那么每次旋转至 0 度的时候,取 [index * rowsPerPart, (index + 1), rowsPerPart] 区间的数据进行摆放。数组中 position 示意摆放地位,一共有左中右三条道,也可能三条道都摆放,依据配置创立金币节点,并设置好节点的 position。type 示意应该摆放的模型类型,除了金币还可能是道具、礼包、终点线等。
开发流程
设计好游戏思路之后,能够正式开始制作咱们的游戏啦~ ????
跑酷游戏是通过 Oasis Editor 开发的,这是一个 web 3D 内容在线开发平台,底层用的是 Oasis 3D(蚂蚁自研的 3D 引擎)。这时候你可能会问,为什么要用 Oasis Editor 开发呢?????
接下来分为「场景搭建」、「逻辑开发」、「业务联动」来解说整个 3D 工作流。
场景搭建
上传资产
在编排场景之前咱们须要先上传好游戏资产,个别美术提供的模型文件格式为 fbx 或 gltf,纹理举荐应用 webp 格局,咱们在资源区右侧点击上传。
在开发过程中,美术可能常常须要替换纹理,所以倡议美术将纹理与模型解绑,通过手动上传的模式将纹理绑定到模型上,防止同时加载两个纹理。
如图,咱们曾经在资源区上传好楼房、道具、金币等模型和相应纹理。
场景编排
有了资产之后咱们须要绑定到节点上,而后进行场景编排,如下视频以楼房和高空为例进行演示:
- 创立场景树
- 绑定 GLTF 模型
- 编辑器 PBR 材质,绑定纹理
- 调整编辑器相机,拷贝编辑视角
- 转换相机视角,微调相机参数
依照同样的办法咱们实现了整个场景的编排,某些节点须要通过脚本管制展现,能够点击场景树右边的小眼睛进行暗藏,场景成果如下:
粒子系统
游戏开发的时候,常常会用到粒子系统来帮忙咱们实现一些比拟酷炫的成果,在咱们这个我的项目中,在人物节点 (person) 上面有 2 个子节点,别离来负责吃到金币 (coinParticle) 和道具 (toolParticle) 时的粒子成果,游戏过程中成果如下:
当咱们点击选中一个粒子节点的时候,编辑器右侧会进去对应的属性面板,属性面板中就可能看到咱们的粒子组件以及相干参数,通过设置参数能够调整咱们的粒子成果:
接下来一步就是来设置参数来管制咱们的粒子成果了,上面给大家介绍下几个罕用参数:
逻辑开发
以上场景可由前端帮助美术同学进行搭建,接下来这一步就正式进入编程阶段了。
脚本能力
1、cli
Oasis Cli 是连贯业务和 Oasis 3D 编辑器的桥梁,在应用咱们引擎的时候,倡议提前装置好 Cli 的环境:
tnpm i @alipay/oasis-cli -g
装置好 Cli 之后,咱们就能够将场景导出到咱们的本地我的项目,并且随时将最新的场景编排同步至本地。首先,咱们进入跑酷我的项目根目录,并执行如下命令,将咱们曾经建好的 3D 场景和以后我的项目连贯:
oasis pull sceneId
下面的 pull 命令中,sceneId 是咱们的场景 id,执行完该命令后,会在根目录下主动增加了 1 个目录和 1 个文件,如下:
当咱们须要对场景进行编辑,并且将最新批改同步至本地,咱们只须要执行如下命令即可:
oasis dev
2、金币转动
这里以金币转动为例演示如何增加脚本管制,首先在资源面板增加一个脚本,而后在将脚本挂在节点上:
实现这一步后,咱们就能够在 coinAni 的脚本中实现对 coin 节点的管制了,金币始终旋转咱们在脚本的 onUpdate 中解决即可:
`onUpdate() {
const {node} = this;
TWEEN.update();
if (this._isRotate && node.parentNode.isActive) {
node.setRotationAngles(0, globalVal.coinAngle % 360, 0);
}
}`
碰撞检测
利用碰撞检测来反馈人物与金币之间的碰撞,首先须要给人物和金币都加上碰撞体突围盒。Oasis Editor 提供了立方体碰撞体和球型碰撞体,引擎会在每帧更新时计算本节点的 collider 与其余 collider 的相交状况,球型碰撞体之间只须要比拟球心间隔与两个半径之间的大小关系,而立方体碰撞体须要计算八个顶点的地位关系,所以应用球型碰撞体性能上会好一些。
如下图,咱们给人物增加了一个球型碰撞体,能够调节它的球心和半径。可视化突围盒只是编辑器运行时的插件,因而不会呈现在咱们的 H5 场景中。
编辑完碰撞体突围盒之后,咱们须要在脚本中进行碰撞检测,监听 collision 事件:
`let cd = node.createAbility(o3.ACollisionDetection);
cd.addEventListener(‘collision’, e => {
const colliderNode = e.data.collider.node; // 拿到被碰撞的节点
const name = colliderNode.name;
// do something…
});`
Shader
???? 嘿嘿,看到 Shader 别急着划走,把握了 Shader 你就能够:
- 自定义光照、物理等模型,能够开发更多酷炫的成果
- 可能优化渲染性能
- 可能帮忙咱们排查渲染上的问题
列举几个 Shader 的成果,更多成果能够返回 shadertoy:
1、什么是 Shader
Shader(着色器)是运行在 GPU 上的小程序,这些小程序为图形渲染管线的某个特定局部而运行,它用于通知图形硬件如何计算和输入图像。为了更深刻理解 Shader 的原理,咱们须要理解 OpenGL 的渲染流水线,这里以渲染跑酷游戏的高空模型为例:
CPU 利用阶段
咱们在 3.1.1 中上传了高空的 fbx 模型文件,其中蕴含了顶点地位、UV、法线、切线等信息,CPU 将这些信息加载到显存中,而后设置渲染状态,通知 GPU 如何进行渲染工作。最初 CPU 会收回渲染命令(Drawcall),由 GPU 接管并进行渲染。
GPU 渲染管线
GPU 渲染管线蕴含了几何阶段和光栅化阶段,顶点着色器(Vertex Shader)和片元着色器(Fragment Shader)别离位于这两个阶段中。
几何阶段:顶点着色器接管 CPU 传过来的顶点数据,通常在这个阶段做一些空间变换、顶点着色等操作。接着会通过裁剪,把不在相机视线中的顶点裁剪掉,并剔除某些图元,而后将物体坐标系转换到屏幕坐标系。
光栅化阶段:两个顶点之间有很多个像素,片元着色器会对像素进行解决,除了进行纹理采样,还会将像素与灯光进行计算,产生反射、折射等成果。同一个屏幕像素点可能会有多个物体,这时候须要通过 alpha 测试、深度测试、模板测试、混合(blend)等解决,把同一地位的像素进行过滤或合并,最终渲染到屏幕上。
2、如何编写 Shader
Oasis Editor 中写 Shader 须要通过这几个步骤:
(1)、在资源区中增加“Shader 材质”,而后绑定到模型上
(2)、编辑 Shader 材质,属性面板中提供了常见的渲染状态配置,也能够间接编辑着色器定义(ShaderDefine)。
整个 ShaderDefine 构造如下,其中 vertexShader 和 fragmentShader 别离寄存顶点着色器和片元着色器代码,采纳 GLSL (OpenGL 着色语言,OpenGL Shading Language)编写。states 用来定义渲染状态管制对象,对应上文提到的合并阶段。
`export const ShaderMaterial = {
vertexShader: “,
fragmentShader: “,
states: {},
uniforms: {},
attributes: {},
};`
(3)、如果要动静扭转材质参数值,须要创立脚本,在节点每帧执行的回调函数中批改属性值。
上面通过跑道滚动和光波两个示例来解说。
3、跑道滚动
如 2.1 中所述,跑道是一个静止的圆弧模型,通过扭转纹理的 UV 值来实现跑道滚动的成果。为了实现给人物打光的成果,咱们在根底色彩纹理下面叠加了一张突变纹理,并给人物加上了一个动态的暗影(实际上是一个面片)。
(根底色彩纹理)
(突变纹理)
=
(叠加成果)
相干的 Shader 代码如下:
“export const ShaderMaterial = {
// Vertex Shader 代码
vertexShader: `
uniform mat4 matModelViewProjection;
uniform float utime;
attribute vec3 a_position;
attribute vec2 a_uv;
varying vec2 v_uv;
varying vec2 v_uv_run;
void main() {gl_Position = matModelViewProjection * vec4(a_position, 1.0);
v_uv = a_uv;
v_uv_run = vec2(v_uv.s, v_uv.t + utime);
}
`,
// Fragment Shader 代码
fragmentShader: `
varying vec2 v_uv;
varying vec2 v_uv_run;
uniform sampler2D texturePrimary;
uniform sampler2D textureLight;
void main() {vec4 texSample = texture2D( texturePrimary, v_uv_run).rgba;
vec4 texLightSample = texture2D(textureLight, v_uv).rgba;
gl_FragColor = vec4(texSample.rgb * texSample.a + texLightSample.rgb * texLightSample.a, texSample.a);
}
`,
states: {},
}“
Vertex Shader 和 Fragment Shader 都蕴含了一个 mian 入口函数。
首次看 Shader 代码会发现很多生疏的符号,其中 uniform、attribute 和 varying 都是变量限定符,attribute 只能存在于 Vertex Shader 中,个别用来搁置程序传过来的顶点、法线、色彩等数据;uniform 是程序传入到 Shader 中的全局数据;varying 次要负责在 Vertex Shader 和 Fragment Shader 之间传递变量。
mat4、vec3、sampler2D 都是根本变量类型,别离代表矩阵、向量和纹理,前面的数字代表 n 维,例如 mat4 示意 4×4 矩阵。
本例的 Vertex Shader 中,顶点地位 a_position 与 matModelViewProjection 矩阵相乘,其实是把三维世界的物体投影到二维的屏幕上。a_uv 寄存了 UV 信息,咱们想要把一张贴图贴到模型外表,须要纹理映射坐标,即 UV 坐标,别离代表横纵两个方向。为了使高空能滚动起来,咱们须要每帧扭转 UV 的纵坐标,并通过变量 v_uv_run 传递给 Fragment Shader。
在 Fragment Shader 中,texturePrimary 和 textureLight 都是从 CPU 程序传过来的纹理。通过 texture2D 采样根底色彩纹理 texturePrimary,失去了纹理贴图在模型上滚动的成果。接着拿采样后的色彩值与通明突变纹理 texLightSample 进行叠加,失去了近亮远暗的成果。
最初,咱们在 CPU 中每帧更新 utime 的值,并传入 Shader。
`onUpdate(deltaTime) {
if (!this.running || !this._streetMaterial) return;
// 赛道滚动
this._time -= deltaTime * 0.0002;
this._time %= 1.0;
this._streetMaterial.setValue(‘utime’, this._time);
}`
4、光波特效
人物吃到吸吸卡之后会有一个光波特效,因为是不规则动画,咱们采取了帧动画来实现。首先须要拿到这样 n_n 的帧序列。留神,浏览器会对纹理尺寸进行限度,能够通过 gl.MAX_TEXTURE_SIZE 拿到这个值,最好别超过 2048_2048。
接着在 Shader 中进行纹理采样。假如一个 100 * 100 的正方形,它的顶点着色器运行 4 次(因为有 4 个顶点),但片元着色器会运行 10000 次,所以尽量把 UV 等计算放在 Vertex Shader 中,再通过 varying 传给 Fragment Shader。代码如下:
“export const ShaderMaterial = {
// Vertex Shader 代码
vertexShader: `
attribute vec3 a_position;
attribute vec2 a_uv;
uniform mat4 matModelViewProjection;
uniform float uFrame;
varying vec2 v_uv;
void main(void)
{gl_Position = matModelViewProjection * vec4(a_position, 1.0);
float cellCount = 8.0;
float row = floor(uFrame / cellCount); _// 以后第几行_
float col = mod(uFrame, cellCount); _// 以后第几列_
float cellSize = 1.0 / cellCount;
v_uv = vec2(a_uv.s * cellSize + col * cellSize, a_uv.t * cellSize + row * cellSize);
}
`,
// Fragment Shader 代码
fragmentShader: `
varying vec2 v_uv;
uniform sampler2D uDiffuseMap;
void main(void)
{gl_FragColor = texture2D(uDiffuseMap, v_uv);
}
`,
states: {},
uniforms: {
uDiffuseMap: {
name: 'uDiffuseMap',
type: o3.DataType.SAMPLER_2D
},
uFrame: {
name: 'uFrame',
type: o3.DataType.FLOAT
}
},
attributes: {},
};“
CPU 须要传入帧序列纹理 uDiffuseMap,还要每帧更新 uFrame 的值:
`onUpdate(deltaTime) {
// update per frame
if (this.material) {
this.frame++
if (this.frame > 57) {this.frame = 0;}
this.material && this.material.setValue('uFrame', this.frame)
}
}`
业务联动
余额宝跑酷是一个跑在 h5 环境下的我的项目,其中就波及到业务层 (react) 和游戏层 (oasis),咱们在业务层和游戏层之间加了一个胶水层(gameController) 来进行两者通信,构造如下:
从下面结构图能够看出,作为胶水层的 gameController,次要做了 2 件事件,一个是给业务层提供 api 调用,并且告诉游戏层,另外一个是监听游戏层的音讯,并且告诉业务层,上面来看看示例:
`import * as o3 from ‘@alipay/o3’;
export default class GameController extends o3.EventDispatcher {
constructor (rootNode, dispatch) {super();
this._dispatch = dispatch;
this._oasis = this._rootNode.engine;
_// 获取须要监听的节点_
this._rootNode = rootNode;
this._magnetCollidNode = rootNode && rootNode.findChildByName('magnetCollid');
this._buildNNode1 = rootNode && rootNode.findChildByName('part1');
this._buildNNode2 = rootNode && rootNode.findChildByName('part2');
this._streetNode = rootNode && rootNode.findChildByName('street');
_// 注册监听_
this.getMessage(rootNode);
}
_// 注册监听_
getMessage(rootNode) {
_// 注册监听游戏层音讯_
this._magnetCollidNode.addEventListener('magnetCoinCollide', (event) => {
_// 反馈给业务层_
this._dispatch && this._dispatch({type: 'collideHappen', payload:{ type: 'coin'}});
});
_// todo 其余节点注册监听_
}
_// 给业务层调用的 api_
gameInit(iconList, gameData) {const gameInit = new o3.Event('gameInit');
gameInit.data = {
iconList,
gameData,
};
this._oasis && this._oasis.resume();
_// 告诉游戏层_
this._buildNNode1.trigger(gameInit);
this._buildNNode2.trigger(gameInit);
this._streetNode.trigger(gameInit);
}
}`
性能优化
调试工具
工欲善其事必先利其器,当咱们须要对我的项目进行性能优化的时候,咱们首先须要剖析性能瓶颈点,而后隔靴搔痒,很侥幸的是 chrome 自身就自带性能剖析工具 (Performance:关上页面进入开发者工具即可看到),如下:
除了性能调试工具外,有时候咱们还会遇到一些渲染异样,大多是给到 GPU 的数据有问题,而这部分数据咱们没法 console.log,chrome 提供了一个十分好用的插件 (Spector.js) 帮忙咱们查看每一帧的数据,如下:
升高三角面
三角面越多,gpu 的计算量也会越大,联合游戏理论的玩法,咱们对三角面这块的优化次要就是不同模型进行减面,最终三角面从 20 万 + 升高到 6 万 +,具体如下:
1、人物这块,因为在跑动过程中,咱们始终只能看到反面,所以把人物后面的三角面全副去掉
2、金币这块,在保障视觉效果看起来比拟圆的前提下尽可能的缩小三角面
3、楼房和人物相似,把赛道内部的游戏过程中基本看不到的面去除
晋升帧率
晋升帧率实质上就是缩小 cpu 的运算工夫,通过后面提到的剖析工具剖析,咱们发现节点数量过多是导致 cpu 运算量大的次要起因,所以咱们的优化重点是在升高节点数量上,最终咱们的 fps 在低端机下面从 10 优化到 25,上面来具体说下:
1、金币模型外面有很多没有用的空节点,这个咱们找美术同学帮忙从新简化模型文件
2、金币模型简化后,其实模型外面还有 2 个节点(其中有一个 rootnode 其实没啥用,和美术同学交换,反馈是目前没有方法去掉),加上挂载模型的节点,咱们一个金币对象其实就有 3 个节点,为了进一步优化,咱们通过代码动静去掉多余节点并进行节点合并。
3、应用对象池来防止重复创立金币。在主循环中,对一些循环呈现的元素,咱们一种优化伎俩就是在初始化的时候当时创立肯定数量的对象,而后用的时候来取,用完就还回来,而缓存创立好的对象的构造就是咱们的对象池了。对象池带来的益处:缩小主循环过程中创建对象带来的开销、能够无效防止因创立开释等操作带来的 GC。咱们游戏中金币数量很多,并且是高频呈现的,所以要用对象池来缓存,相应的设计如下:
`class CoinPool {
private _originNode = null;
private _pool = [];
constructor () {
}
init (originNode: o3.Node, capacity: number = 5) {
this._originNode = originNode;
this._genNode(capacity);
}
destroy () {
this._originNode = null;
this._pool.length = 0;
}
getNode () {
if (this._pool.length === 0) {this._genNode();
}
return this._pool.shift();
}
putNode (node: o3.Node) {
if (this._pool.indexOf(node) === -1) {this._pool.push(node);
}
}
_genNode (num: number = 1) {
const pool = this._pool;
for (let i = 0; i < num; ++i) {let node = this._originNode.clone();
_// 对金币模型节点的优化在这里对立解决_
changeParent(node);
purifyNode(node);
pool.push(node);
}
}
}`
对象池应用形式:
`_// 创立并初始化_
const originCoin = node.findChildByName(‘coinParent’); // 挂载金币模型的节点
const coinPool = new CoinPool();
coinPool.init(originCoin, 24);
// 从池子外面获取金币节点
const coinNode = coinPool.getNode();
// 金币节点不须要应用了,进行回收
coinPool.putNode(coinNode);
// 整个节点池销毁
coinPool.destroy();`
其余
上述两项其实都是针对跑酷我的项目自身做的一些特定优化,其余我的项目未必可能齐全照搬,咱们的尘沫大神针对业务方面的性能优化做了比拟通用全面的总结,这里简略列举一下:
语言
- 应用枚举:在标记判断 if 或 switch 语句中尽量应用 number 型枚举, 防止应用字符串作为判断标记,字符串作为判断标记性能损耗较大
- 应用 Number 做 Object 的 Key:Object 作为 Map 应用时尽量不要应用 string 作为 Key, 而是偏向应用 Number 作为 Key,其中 Number 的范畴越小性能越高,通常小于 65535 性能较优
- 应用“.”拜访对象属性:防止应用 [“string”] 拜访对象的属性和办法,会导致 JIT 优化生效,应应用“.”拜访属性
- 尽量应用 for 循环遍历:帧级调用尽量应用 for 循环进行遍历操作晋升性能, 绝对于语法糖循环更纯正, 须要提前缓存长度 n 进行循环判断,缩小纹理寻址性能损耗
逻辑
- 多用对象池机制:因为 JS 自身机制和原理,须要防止在帧循环中 new 对象, 防止 GC 卡顿, 在业务开发中的模型形象强烈建议应用对象池机制做对象治理
- 善用实例或动态全局变量:除了对象池机制防止 GC 外,还须要利用实例或动态全局变量缩小 GC 损耗,比方一些用于直达数学计算的长期变量可应用动态全局变量缓存,另外一些可逐实例的类变量可缓存为实例全局变量,缩小应用时的频繁 new 操作带来的开销和 GC。
- 慎用事件:在大型项目中慎用事件, 事件自身的灵活性是一把双刃剑,在解耦的同时也带来了逻辑可读性低等艰难,尤其在多人合作开发的我的项目中,所以在业务零碎中该解耦的模块用事件,不须要的中央须要用明确的设计调用逻辑解决, 切记不要因为设计的懈怠把我的项目搞乱
资源优化
- 模型合并优化:美术需将不可独立挪动的模型尽可能合并缩小渲染批次,同时留神不要合并场景范畴跨度过大的模型导致模型无奈裁剪的问题
-
材质优化:
- 尽可能合并材质,材质作为三维引擎的合并根基,所有引擎级渲染批次的合并前提都是应用雷同材质,所以要放弃材质对象尽可能的少
- 材质模型抉择须要依据美术格调尽量精简,比方间接把光照合并在漫反射贴图的的卡通格调模型能够间接抉择 unlit 材质,而无需应用简单的 PBR 材质模型
- 贴图优化:贴图尺寸不可能自觉谋求品质应用超大尺寸,须要评估理论我的项目贴图光栅化后的理论显示像素来应用靠近的贴图尺寸,否则应用过大尺寸不仅得不到成果手机还节约显存。除此之外还可应用纹理压缩优化显存
-
像素填充率优化:
- 尽量减少全屏渲染的绘制, 比方 UI 或遮罩应用相似全屏但大部分通明的图片绘制会带来大幅的 GPU 渲染累赘
- 在挪动端等高 DPI 的设施中可适当升高 DPI 配置,缩小 GPU 累赘
玩法系统优化
-
碰撞系统优化:
- 善用被动碰撞和被动碰撞概念, 缩小被动碰撞器能够大幅缩小碰撞检测的循环遍历次数
- 善用碰撞组概念,将物体划分所属碰撞组和可与之产生碰撞的组作为过滤器,依据业务规定划分能够缩小不必要的碰撞检测循环
- 跑酷弯道优化:可尝试利用顶点着色器模仿弯道跑酷成果,缩小 CPU 端相干跑酷弯道逻辑的计算累赘,升高美术制作复杂度
Oasis 3D V2.x To V3.x
随着 Oasis 3D 服务的业务数量越来越多、业务负责度越来越大,也暴露出不少问题,为此,咱们对现有引擎进行了大重构,也就是 V3.x 版本,此版本次要指标是:更快、更不便、更高效。
这里先简略介绍几个重构模块,心愿让大家有个初步体感。
资源管理模块
资源管理模块咱们从底层实现进行了大重构,次要目标是简化开发者的应用,上面是 v2.x 版本和 v3.x 版本加载一个带有骨骼动画的模型示例,比照能够看出 v3.x 版本的 api 是特地精简的,除了 api 的简化外,性能上咱们还提供了下载重试、重试距离、下载超时、下载进度、勾销下载等。
V2.x 版本加载资源:
`let gltfRes = new Resource(“skin_gltf”, {
type: “gltf”,
url: “xxx.gltf”
});
let resourceLoader = new ResourceLoader(engine);
resourceLoader.load(gltfRes, (err, gltf) => {
if (err) return;
const fairyPrefab = gltf.asset.rootScene.nodes[1];
const fairy1 = fairyPrefab;
rootNode.addChild(fairy1);
const animator = fairy1.addComponent(Animation);
const animations = gltf.asset.animations;
animations.forEach((clip) => {
animator.addAnimationClip(clip, clip.name);
});
animator.playAnimationClip(“Take 001”);
});`
V3.x 版本加载资源:
`const {defaultSceneRoot, animations} = await engine.resourceManager.load(“xxx.gltf”);
rootEntity.addChild(defaultSceneRoot);
const animator = root.getComponent(Animation);
animator.playAnimationClip(“Take 001”);`
数学库
数学库整个进行重构,次要有 2 方面改善:写法更简捷、性能更优。老的数学库都是函数式的,并且向量、四元数等低层其实都是 Array,而 V3.x 采纳 Class 的形式来实现,底层数据结构改为 object。
新的数学库不仅反对更为丰盛的写法,性能下面,通过数学库重构以及应用数据库相干的优化,性能晋升比拟明细,上面是咱们的测试后果:
在线 coding
目前咱们编辑器实现了在线 coding,意味着你只须要一台电脑,并且装置一个浏览器,即可实现 3D 我的项目的创立、开发、公布等,界面如下:
在下面的界面中,即可实现在线 coding,而后保留,即可实时查看最新的成果。进一步的,咱们还提供了事件面板,模仿和业务层的交互,这样咱们就能够在 3D 我的项目中自测残缺个流程,而后公布给业务层应用,如下:
当咱们开发完我的项目后,须要交付给业务方应用,在 V3.x 中,咱们只须要点击公布至对应平台即可(这块还在继续优化中),如下:
原文链接
本文为阿里云原创内容,未经容许不得转载。