在Phaser3中制作等距(Isometric)地图

前段时间迷上了一款微信MUD小游戏,复古风格一下回忆起2012年前后,我在开发自己的MUD小游戏《幻仙》。尘封许久的游戏梦又开始燃气念头…

等距(Isometric)指的是:将物体的三个维度(通常是X、Y、Z轴)以一种特定的比例被压缩到二维平面上,使得物体变得有立体感。在过去又被玩家称作2.5D游戏。像《模拟城市2000》、开罗系列游戏的界面就是等距(Isometric)画面游戏。

游戏案例游戏案例

在Phaser3的官方Demo中,我也找到了相应的等距地图案例。(使用Phaser的原因是其相比Unity、Cocos Creator、laya等游戏引擎使用更简单,不需要下载专门的IDE工具,通过任何一个代码编辑器都能立即开始!)

官方案例官方案例

一、瓦片图收集

互联网上能收集的等距瓦片图资源相对较少,能在Phaser3中应用的2D资源又更少了。我主要在以下三个网站收集瓦片图资源:

  1. OpenGameArt.Org:https://opengameart.org
  2. Itch.io:https://itch.io/game-assets
  3. Kenney.nl:https://kenney.nl/assets

我在OpenGameArt上找到Kenney发布的大量写实风格的3D材质素材(Kenney其实就是Kenney.nl),看起来非常不错!我将用它们来进行本次示例。

需要注意的是:Phaser3游戏引擎**目前只能使用图片尺寸相同的素材。如果你在Tiled地图编辑器里用了图片尺寸不一的图片集(Collection of Images**类型),Phaser3会出错。虽然有人在Github上分享了解决方法(原帖链接),但我试过了,还是不行。

Collection of Images 类型无法在Phaser3里使用Collection of Images 类型无法在Phaser3里使用

二、处理瓦片图

下面是我找的一份图片尺寸相同的素材,首先我们需要调整图片尺寸,将其从256×512改为64×128。这样可以缩小图片的体积,也能减轻在移动端的表现压力(过大的尺寸会导致在移动端运行黑屏)。

图片是统一的256*512像素图片是统一的256*512像素

如果手工批量处理图片就太慢了,我推荐使用一款简单又免费的工具:Caesium Image Compressor

按照图中序号操作按照图中序号操作

打开输出目录,图片尺寸已经都被调整为64×128像素,现在需要将其合并起来。

合并工具一时还比较难找,又想到以前做HTML网页时,会经常用到雪碧图(雪碧图:把一堆小图标拼到一个大图上,然后使用css定位截取展示某一个图标内容,这样既可以减少HTTP的请求次数,也方便了素材资源的管理),于是就找了一下在线制作雪碧图的工具,还真找到一个好用的:sprite-generator

雪碧图在线制作工具雪碧图在线制作工具

三、制作等距地图

Phaser3官方推荐使用Tiled来制作地图,所以我们需要先下载Tiled软件(软件官网)。

3.1 添加地图

先在主界面上点击New Map创建地图,需要注意两点:

  1. Orientation选择Isometric类型;
  2. 高度设置是图片宽度的50%;

创建地图创建地图

3.2 添加图片集

在右侧界面上点击New Tileset创建图片集,需要注意两点:

  1. Embed in map需要勾选上,不然在Phaser3中会无法显示内容;
  2. 目前仅支持Based on Tileset Image类型的图片集
  3. Tile width和Tile height需要设置成图片的宽度、高度;

创建图片集创建图片集

3.3 制作地图

花了几分钟随便搭了一个Demo,操作比较简单,制作时需要注意图片的Layer层级会影响视觉效果的问题(上层遮挡下层,有些则需要两个Layer层互相配合才行)。

绘制demo地图绘制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');

// 加载由Tiled生成的地图数据
this.load.tilemapTiledJSON('map', 'assets/tilemaps/map1.json')
}

create ()
{
// 添加地图到场景中
const map = this.add.tilemap('map');

// 将 瓦片图 添加地图里
const tileset1 = map.addTilesetImage('css_sprites', 'tiles');

// 创建 地图层(层级由先到后的顺序进行叠加渲染,所以layer1是最低层)
const layer1 = map.createLayer('Tile Layer 1', [ tileset1 ]);
const layer2 = map.createLayer('Tile Layer 2', [ tileset1 ]);
const layer3 = map.createLayer('Tile Layer 3', [ tileset1 ]);
}
}

主要有几个注意点:

  1. map.addTilesetImage的第一个参数,需要和Tiled中的Tileset名称保持一致;
  2. map.createLayer的第一个参数,需要和Tiled中的Layer列表名称一致(越底层越先注册);

五、最终效果


评论区