利用晚上下班后的业余事件,我参照官方 UGUI Example 中的拖拽 Demo 实现了RPG游戏中的基础背包功能。
本来我打算实现读取 Json 文件中的物品配置表来实现动态生成物品的,但是发现没这必要,毕竟只是个Demo。读取游戏配置我还得继续学习其他人的方案,也是我下一个Demo实现的目的。怎么才能以最unity的方式从文件中读取游戏配置,并在游戏内动态生成,以实现数据驱动。这样以后修改游戏内容只需要修改不同的配置文件就行了。
废话少说,接下来就直接说说在这个Demo制作过程中学到的东西吧。
0. UGUI 中 UI 元素分辨率的设定
在Unity2D中导入一个2d texture 有个关键的参数:Pixels Per Unit
,这个参数表示Unity中的1 Unit 由多少个图片像素组成。比如你导入了一个像素为 100px*100px 的图片,Pixels Per Unit
=100时,生成的 Sprite 在 Unity 的坐标系内就是 1Unit*1Unit的方片。
但是在UGUI中,每个UI元素的大小还由另外两个参数决定,那就是 Canvas Scaler 组件中的Scale Factor
和Reference Pixels Per Unit
。其中Scale Factor
控制整个UGUI的缩放大小。这个参数十分好理解,0.5代表整个UI 缩小一半,1代表本来的大小,2代表放大一倍。Reference Pixels Per Unit
参数的话就不怎么好理解了。这个参数是用来调节 UI 元素相对大小,用来区分普通游戏元素例如 Sprite 之类的。UI 元素最终在 Unity 坐标系中的大小: *GameObject Size = (Texture Pixels / Pixels Per Unity) Reference Pixels Per Unit*,举个例子:100px*100px的图片,Pixels Per Unit
=100,Reference Pixels Per Unit
=100,那么根据上面的公式 GameObject size = 100,那么相当于这个UI元素在游戏里面的大小是100 unit*100 unit。
UGUI 默认设置 UI 的大小就是1像素=1 unit,所以 UI 元素在 unity 编辑器中会显得十分的庞大。但是这样做有个好处,那么就是对于像素的处理会十分的容易理解,免去了图片大小和游戏内大小的转换。同时可以获得更好的渲染效果,因为纯 2D 游戏主要还是像素化的,在 3D 世界中会经常遇到一个问题那就是 tile bleeding,这是因为相机(camera)在移动的时候,移动值的类型是 float,精度十分高,所以当恰好移动到某一小精度时,可能会导致某两个相邻图块的渲染出现了1象素的位置偏差(比如左边的图块x坐标舍了点,右边的图块x坐标入了点,于是他俩之间出现了一个1象素的纵向的缝)。
解决 tile bleeding 的方法由很多种,我的方法基本步骤如下:
1.设置游戏内的 texture 的大小为1px = 1unit。
2.设置 Camera Viewport 的大小,让1px屏幕像素=1unit。
3.设置 Camera 的最小移动间隔为 1unit。
这样texture的每个像素和屏幕的像素都是一一对应的关系。在 Unity 2018.2 版本中官方也给出了解决方案:Pixel Perfect Camera,其解决方案和我的这个思路类似。
1. UGUI 的渲染顺序
无论 Canvas 的Render Mode
处于何种模式,UI 元素的渲染顺序都是根据 Hierarchy 面板的顺序决定的,根据实验应该是深度优先遍历。
所以,如果你要将一个UI元素显示在所有UI元素的上面,你最好是将这个元素放在 Canvas 根节点的子节点的最后面。
// UI 元素在所有UI元素的上面
var canvas = GameObject.Find("Canvas");
transform.SetParent(canvas); // unity 默认会将最近加入的子节点排到最后
// UI 元素在兄弟节点的上面
var children = transform.parent.GetComponentsInChildren<Transform>(); // 这会包括parent的Transform
transform.SetSiblingIndex(children.length - 2);
2. UGUI 的输入检测和事件传递
UGUI 的输入事件回调需要你实现不同的回调接口的。比如实现拖拽功能的话,需要IDragHandler
、IBeginDragHandler
等等。
UGUI 的事件传递是往父节点一层层的往上传递的。当父节点实现了一个接口,而子节点没有的话,那么即使事件由子节点捕获到了,但是由于子节点处理不了,那么就会传给父节点来处理。
UGUI 实现检测输入事件主要是靠 Canvas 上的 Graphic Raycaster
组件。这个组件负责发射一个射线,当由UI元素射中的话,就可以进行相应的处理了。你可以将UI元素里面Raycast Target
关掉,就可以不检测射线碰撞了。输入的主要数据都在 PointerEventData
类中。运用好这几个东西就可以做出各种 UI 操作了。
UGUI 的 EventSystem 具体的是如何实现的,我并不太关心,毕竟要我看所有的源代码太让人头大。
最后附上Github地址:https://github.com/dougen/Unity-Backpack
dalao好久没发日志了