前段时间迷上了一款微信MUD小游戏,复古风格一下回忆起2012年前后,我在开发自己的MUD小游戏《幻仙》。尘封许久的游戏梦又开始燃气念头…
等距(Isometric)指的是:将物体的三个维度(通常是X、Y、Z轴)以一种特定的比例被压缩到二维平面上,使得物体变得有立体感。在过去又被玩家称作2.5D游戏。像《模拟城市2000》、开罗系列游戏的界面就是等距(Isometric)画面游戏。
游戏案例
在Phaser3的官方Demo中,我也找到了相应的等距地图案例。(使用Phaser的原因是其相比Unity、Cocos Creator、laya等游戏引擎使用更简单,不需要下载专门的IDE工具,通过任何一个代码编辑器都能立即开始!)
官方案例
一、瓦片图收集
互联网上能收集的等距瓦片图资源相对较少,能在Phaser3中应用的2D资源又更少了。我主要在以下三个网站收集瓦片图资源:
- OpenGameArt.Org:https://opengameart.org
- Itch.io:https://itch.io/game-assets
- Kenney.nl:https://kenney.nl/assets
我在OpenGameArt上找到Kenney发布的大量写实风格的3D材质素材(Kenney其实就是Kenney.nl),看起来非常不错!我将用它们来进行本次示例。
需要注意的是:Phaser3游戏引擎**目前只能使用图片尺寸相同的素材。如果你在Tiled地图编辑器里用了图片尺寸不一的图片集(Collection of Images**类型),Phaser3会出错。虽然有人在Github上分享了解决方法(原帖链接),但我试过了,还是不行。
Collection of Images 类型无法在Phaser3里使用
二、处理瓦片图
下面是我找的一份图片尺寸相同的素材,首先我们需要调整图片尺寸,将其从256×512改为64×128。这样可以缩小图片的体积,也能减轻在移动端的表现压力(过大的尺寸会导致在移动端运行黑屏)。
图片是统一的256*512像素
如果手工批量处理图片就太慢了,我推荐使用一款简单又免费的工具:Caesium Image Compressor。
按照图中序号操作
打开输出目录,图片尺寸已经都被调整为64×128像素,现在需要将其合并起来。
合并工具一时还比较难找,又想到以前做HTML网页时,会经常用到雪碧图(雪碧图:把一堆小图标拼到一个大图上,然后使用css定位截取展示某一个图标内容,这样既可以减少HTTP的请求次数,也方便了素材资源的管理),于是就找了一下在线制作雪碧图的工具,还真找到一个好用的:sprite-generator。
雪碧图在线制作工具
三、制作等距地图
Phaser3官方推荐使用Tiled来制作地图,所以我们需要先下载Tiled软件(软件官网)。
3.1 添加地图
先在主界面上点击New Map
创建地图,需要注意两点:
- Orientation选择Isometric类型;
- 高度设置是图片宽度的50%;
创建地图
3.2 添加图片集
在右侧界面上点击New Tileset
创建图片集,需要注意两点:
- Embed in map需要勾选上,不然在Phaser3中会无法显示内容;
- 目前仅支持Based on Tileset Image类型的图片集;
- Tile width和Tile height需要设置成图片的宽度、高度;
创建图片集
3.3 制作地图
花了几分钟随便搭了一个Demo,操作比较简单,制作时需要注意图片的Layer层级会影响视觉效果的问题(上层遮挡下层,有些则需要两个Layer层互相配合才行)。
绘制demo地图
此处我们不过多描述Tiled的操作教程,有疑问参见官网文档。
3.4 导出地图
制作完成后点击File
-Export As
进行地图导出,需要将保存类型选择为JSON map files
,并且手动将文件名命名为xxx.json
。
导出地图
四、Phaser3编码
我摘取了第一章官方案例中的核心代码,并对内容进行了一定改动(下面也逐行对代码进行了解释)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class Example extends Phaser.Scene {
preload () { this.load.setPath('/')
this.load.image('tiles', 'assets/tilemaps/css_sprites.png');
this.load.tilemapTiledJSON('map', 'assets/tilemaps/map1.json') }
create () { const map = this.add.tilemap('map');
const tileset1 = map.addTilesetImage('css_sprites', 'tiles');
const layer1 = map.createLayer('Tile Layer 1', [ tileset1 ]); const layer2 = map.createLayer('Tile Layer 2', [ tileset1 ]); const layer3 = map.createLayer('Tile Layer 3', [ tileset1 ]); } }
|
主要有几个注意点:
map.addTilesetImage
的第一个参数,需要和Tiled中的Tileset名称保持一致;
map.createLayer
的第一个参数,需要和Tiled中的Layer列表名称一致(越底层越先注册);
五、最终效果