游戏开发中或多或少都有接触过Tilemap
,在Tiled
编辑器里编辑好地图以后,导出数据,而后在游戏引擎(Cocos...)里就能够直接使用了,确实很方便。编辑器
因为Pixi.js
自身并不支持Tilemap
的解析渲染,因此我一直在尝试了解背后的机制。最初能想到的实现就是,在一个Container
里放一堆Sprite
把地图拼出来,Pixi.js
的Texture
支持以一个BaseTexture
为基础,分块读取,因此实现一个也还能够。代码差很少就是这样:this
const rect = new PIXI.Rectangle(0, 0, this.square, this.square) for (let i = 0, sn = 0; i < this.size.height; i++) { for (let j = 0; j < this.size.width; j++) { sn = this.data[i * this.size.width + j] if (!sn) continue sn-- rect.x = (sn % this.tilesets.columns) * this.square rect.y = (~~(sn / this.tilesets.columns)) * this.square // 分块读取 const tile = new PIXI.Sprite(new PIXI.Texture(this.tilesheet, rect)) tile.position.set(j * this.square, i * this.square) tile.anchor.set(.5) this.addChild(tile) } }
一天看到Pixi.js
的做者在Codepen
上的代码后,因而有了今天这篇文章。code
上面是v5的实现,v4实现起来略麻烦。orm
利用shader去渲染Tilemap。咱们从Tilemap
导出的JSON
数据能够知道,主要的数据其实就是地图元素(瓦片)在图集中的索引。如何在着色器里拿到索引数据呢?索引
若是你看了上面Codepen
的代码,或许你就知道了:游戏
const bitmap = new PIXI.Graphics() for (let i = 0; i < layer.height; i++) { for (let j = 0; j < layer.width; j++) { const index = layer.data[i * layer.width + j] - 1, column = this.mapData.tilesets[0].columns, x = index % column, y = Math.floor(index / column) bitmap.beginFill((x << 16) + (y << 8), index !== -1 ? 1 : 0) bitmap.drawRect(j, i, 1, 1) bitmap.endFill() } } // 生成纹理后面会传入纹理单元 必定要设置:PIXI.SCALE_MODES.NEAREST core.renderer.generateTexture(bitmap, PIXI.SCALE_MODES.NEAREST)
建立一个bitmap
,把地图的索引数据变成颜色值储存在这个bitmap
里。ci
还须要一段着色器代码:游戏开发
precision highp float; uniform sampler2D tilesheet, bitmap; uniform float tileSize, tileColumn; uniform vec2 tilesheetSize, mapSize; varying vec2 vTextureCoord, vVertexPosition; void main() { vec4 color = texture2D(bitmap, vTextureCoord) * 255.0; if (color.a == 0.0) discard; vec2 coord = (vec2(color.r, color.g) * tileSize + mod(vTextureCoord * mapSize, tileSize)) / tilesheetSize; gl_FragColor = texture2D(tilesheet, coord); }
这里还有一些Pixi.js
的操做,我就不写出来了。弄明白原理,就行。感受和法线贴图是一个道理,虽然我并无研究过。主要就是把索引数据变成纹理上传到GPU
,而后在着色器代码里读取出来。开发