学习Unity银河恶魔城游戏制作第二部分:瓦片、背景与摄像头

Tile Palette

Tile Palette,即“瓷砖调色板”,是2D关卡设计的核心工具,可以快速构建基于网格的2D场景。

其功能为:将预制的瓷砖Tile资源组织为调色板,后续可以使用画笔工具在网格上快速绘制关卡。

如果想将瓷砖资源放入调色板进行组织,需要讲下载的图像Sprite资源(首先切割好)拖入Tile Palette窗口。在拖入过程中,Unity会自动为拖入的Sprite子资源生成对应的.tile文件(作者需要自行选择这些.tile文件的存放位置)。后续即可使用这些调色板内的瓷砖资源为场景绘制相关背景。

Tile Palette窗口如图(在加入瓷砖资源过程中,总共需要选择两次保存位置:调色板资源的保存位置,与转换后的.tile瓷砖文件保存位置):

如果想对调色板上的这些瓷砖进行删除,移位等修改,可以点击右上角铅笔标志进入编辑模式(Edit),然后再使用上方的这些工具修改调色盘。

具体如何使用此调色板绘制游戏背景等场地信息,见场地绘制。

场地绘制

如果想要讲Tile Palette中的瓦片信息应用到场景中,首先需要为场景创建一个Tilemap。当初次创建时,会同时创建一个父对象Grid(用来规定网格形状)。

目前设置的场地信息包括两个部分(使用两个Tilemap进行实现):地面Ground及背景Background。其差别是:地面所属瓦片是需要有物理碰撞的,玩家可以站立于其上,而Background只起到背景绘制的功能,不产生任何物理效果。

在这个例子中,两个Tilemap对象的设置完全不同。对于地面Tilemap,需要为期添加Tilemap Collider 2D,Rigidbody 2D和Composite Collider 2D三个组件。同时需要进行一些特殊设置:

  • 设置Composite Collider 2D的Geometry Type为Polygon,这样可以让碰撞体为实心多边形。这种设置适用于封闭区域
  • 设置Rigidbody 2D的Body Type为Static:让对应物体永远不移动(无限质量、不受力),适用于静态地面
  • 设置Tilemap Collider 2D的Composite Operation为Merge:可以将所有瓦片的碰撞体合并为一个整体给Composite Collider 2D处理,各个瓦片不会保持独立碰撞体
  • 设置Tilemap Renderer的Order in Layer为0,保证其高于背景Tilemap的层次,可以保证其在上方被渲染(不被覆盖)
  • 设置整个对象的Layer为Ground,这样后续玩家可以判定其为地面,能够踩踏

部分设置如图:

背景Tilemap的设置就非常简单,因为不需要有任何物理模拟,只需要让它的Tilemap Renderer中的Order in Layer低于地面层即可。

然后只需要使用Tile Palette为场景添加瓷砖即可。Tile Palette窗口上方的工具均可用以编辑场景中的Tilemap。需要注意选择当前瓦片被添加到场景中的哪一个Tilemap(在Tile Palette窗口上方选单可选)。最终效果如下:

Camera

在unity中,摄像头的很多高级功能被集中保存在Cinematic Studio这一扩展包中。可以为场景新增Cinematic中的Virtual Camera对象,这个对象会覆盖本身场景中的Main Camera对象,对摄像头施加更加高级的控制。可以进行控制的参数包括:

  • Follow:目前这个摄像头默认跟随的对象
  • Ortho Size:选择摄像头的大小(可以看到的范围)
  • Screen X / Screen Y:跟随的角色在摄像机中的位置
  • X / Y / Z Dumping:摄像机跟随角色的延迟情况,会使摄像机在某一方向的移动速度低于玩家,造成摄像机移动的延迟
  • Lookahead time:会让摄像机在玩家之前移动
  • Lookahead Smoothing:会让摄像机的提前移动更加平滑
  • Dead Zone Width / Height:会在摄像头中心生成一块“死区”,当摄像机跟随的玩家在这片区域内移动时摄像机不会发生位移
  • Soft Zone Width / Height:可以限制摄像头中心的移动,不能超出Soft Zone设置的区域
  • Bias X / Y:可以设置Soft Zone相对于摄像头中心的偏移

目前的Virtual Camera对象的参数设置如下:

Background

首先,对于背景来说,重要的就是Sprite Renderer的堆叠,来确定不同背景的覆盖情况。用来管理的Sprite Renderer相关属性为:Sorting Layer和Order in Layer。

其中,Sorting Layer是大的层次划分,可以将不同图像分为Background,Ground,Player等,在较高层的图像就一定能够覆盖较低层的图像。

Order in Layer则是较低的层次划分,处于同一Sorting Layer的图像通过比较Order in Layer确定最终的覆盖情况,较高的Order in Layer对应图像能够覆盖较低的。

接下来希望实现的,是背景跟随玩家移动,也就是说,如果背景完全不动,那么角色的移动有可能超出背景的范围。若背景能够跟随角色移动,且若不同层次的背景能够以不同速度跟随,则会有很好的视觉效果。代码如下:

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
30
31
32
using UnityEngine;  

public class ParallexBackground : MonoBehaviour
{
private GameObject cam;

[SerializeField] private float parallaxEffect;

private float xPosition;
private float length;

void Start()
{
cam = GameObject.Find("Main Camera");

length = GetComponent<SpriteRenderer>().bounds.size.x;
xPosition = transform.position.x;
}

void Update()
{
float distanceMoved = cam.transform.position.x * (1 - parallaxEffect);
float distanceToMove = cam.transform.position.x * parallaxEffect;

transform.position = new Vector3(xPosition + distanceToMove, transform.position.y);

if (distanceMoved > xPosition + length)
xPosition = xPosition + length;
else if (distanceMoved < xPosition - length)
xPosition = xPosition - length;
}
}

其中,parallaxEffect决定了背景跟随玩家移动的统一程度。根据代码可知,xPosition通过transform得来,是代表当前背景的位置;cam.transform是摄像机位置,由于其跟随主角,也可以视作主角位置。

因此,通过float distanceToMove = cam.transform.position.x * parallaxEffect;可以知道当前背景应该移动的距离(玩家移动距离乘背景随玩家移动的统一程度),并且可以通过更改当前背景的transform.position进行背景位置的修改。

当然,这样还会引发一个问题:当parallaxEffect小于1时,会出现背景移动的比玩家慢的情况,这样久而久之,如果背景长度有限,玩家就有可能移动出背景的边界。

为了解决这个问题,首先可以获取背景的长度length = GetComponent<SpriteRenderer>().bounds.size.x;,然后可以通过float distanceMoved = cam.transform.position.x * (1 - parallaxEffect);知道背景比玩家少移动的距离,一旦背景比玩家少移动的距离超出了一个背景图像的距离,就让背景瞬间移动一个图像的距离(注意,背景一般由三幅同样的背景图像拼接而成,这也使得这种防止背景用完的方式能够实现)。

想要更清晰的了解此方法可见Unity对应摄像机设置。