Pixi.js 渲染 Tilemap

Stella981
• 阅读 682

源码 预览

游戏开发中或多或少都有接触过Tilemap,在Tiled编辑器里编辑好地图之后,导出数据,然后在游戏引擎(Cocos...)里就可以直接使用了,确实很方便。

由于Pixi.js自身并不支持Tilemap的解析渲染,所以我一直在尝试了解背后的机制。最初能想到的实现就是,在一个Container里放一堆Sprite把地图拼出来,Pixi.jsTexture支持以一个BaseTexture为基础,分块读取,所以实现一个也还可以。代码差不多就是这样:

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上的代码后,于是有了今天这篇文章。

实现思路

上面是v5的实现,v4实现起来略麻烦。

利用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里。

还需要一段着色器代码:

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,然后在着色器代码里读取出来。

点赞
收藏
评论区
推荐文章
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
Unity小王子私藏的开发2D游戏的常用插件合集
Unity以开发3D游戏见长,早期版本的Unity在开发2D游戏时不慎方便,因此AssetStore出现了很多2D游戏开发引擎。现在Unity对2D游戏的支持越来越好,而这些开发2D游戏的Unity插件也得到了更多开发者的喜爱。1:RexEngine:Classic2DPlatformerEngine(https://www.os
Stella981 Stella981
3年前
Cocos Creator基础教程(9)—优化代码编辑器
CocosCreator游戏开发主要是使用JavaScript语言,这里向大家推荐VisualStudioCode和Webstorm两款JavaScript神级编辑器。这两款编辑器的安装都很简单,这里主要介绍在CocosCreator项目中如何调整编辑器配置,以提升开发效率。1\.排除干扰文件我们知道CocosCre
Stella981 Stella981
3年前
Cocos Creator基础教程(10)—预览调试
游戏预览是开发中的一个重要环节,CocosCreator游戏引擎基于JavaScript语言有着丰富强大的预览调试能力,这次我们介绍预览调试相关的技术,了解一下这方面的知识相信对你也非常有帮助。1\.游戏预览CocosCreator是跨平台的游戏开发引擎,从类别上主要分为Nativet和H5两大平台,游戏预览也分为这两大模式:
Wesley13 Wesley13
3年前
2D小游戏开发学习笔记(5)
一、围住神经猫游戏游戏玩法:玩法很简单,蓝色圆圈代表神经猫,通过点击周围圆圈把猫困住,就算游戏成功游戏效果!(https://oscimg.oschina.net/oscnet/up968a35abafe07c092eacca8126719e14a50.png)逻辑梳理:1、
Stella981 Stella981
3年前
FMOD Event System——事件树策略、加载、内存分配
FMOD最新API—EventSytem,提供了比FMODEx更高层的接口,使引擎开发人员无需关注诸如音频数据管理、播放控制、channels管理等底层细节,而把精力放在考虑如何为上层应用(如:场景/技能/UI编辑器中的音效、音乐的编辑,游戏中各种音效、音乐的播放)设计适合的框架。此外,它还提供了相应的设计工具—FMODDesigner,让音效制作人员
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
Unity 2D游戏开发教程之精灵的死亡和重生
Unity2D游戏开发教程之精灵的死亡和重生精灵的死亡和重生目前为止,游戏项目里的精灵只有Idle和Walking这两种状态。也就是说,无论精灵在游戏里做什么,它都不会进入其它的状态,如死亡。于是我们发现游戏里的精灵,即使是跳入“万丈深渊”,也依然存活,显然这种游戏逻辑无法让人接受。
Wesley13 Wesley13
3年前
12、开源游戏
在前面中我们初始化了游戏的资源,这次我们来说下地图的绘制和游戏主循环设计。地图绘制    以前说过地图是用tiled画好,导出为图片形式的,所以地图的绘制,就是把这个图片绘制到canvas的过程。这样绘制地图就简单了,使用drawImage方法绘制即可。    这里有个2问题,1是地图的大小一般肯定是大于canvas的,所以我们只是把地图