|前端如何快速上手 Web 3D 游戏的开发( 五 )


最后 , 我们在 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、光波特效
|前端如何快速上手 Web 3D 游戏的开发
本文插图
人物吃到吸吸卡之后会有一个光波特效 , 由于是不规则动画 , 我们采取了帧动画来实现 。 首先需要拿到这样nn的帧序列 。 注意 , 浏览器会对纹理尺寸进行限制 , 可以通过 gl.MAX_TEXTURE_SIZE 拿到这个值 , 最好别超过20482048 。
|前端如何快速上手 Web 3D 游戏的开发
本文插图
接着在 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)来进行两者通信 , 结构如下:
|前端如何快速上手 Web 3D 游戏的开发
本文插图
从上面结构图可以看出 , 作为胶水层的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 = http://news.hoteastday.com/a/{ iconList, gameData, }; this._oasis && this._oasis.resume(); // 通知游戏层 this._buildNNode1.trigger(gameInit); this._buildNNode2.trigger(gameInit); this._streetNode.trigger(gameInit); } }性能优化