Unity2.5D Sprite层级显示遮挡问题处理

代码源自游戏《A Place for the Unwilling》

开发《A Place for the Unwilling》游戏第一部要解决的问题就是让精灵可以围绕其它精灵前后移动,呈现出真实的深度感觉。SpriteRenderer组件有两个属性,可以改变场景中Sprite的渲染顺序。

  • Sorting Layer  用于设置不同层的Sprite渲染顺序
  • Order in Layer  用于设置在同一层中的Sprite渲染顺序

如果想实时改变多个Sprite的渲染顺序,就需要修改一些属性以便无论精灵在场景中如何移动,均以正确的顺序渲染。由于“Oder in Layer”属性仅接受整型参数,所以利用Z轴似乎是个更好的选择。

Unity中Sprite的渲染优先级如下图,从高到低:

<ignore_js_op>

如果两个Sprite的“Sorting Layer”和“Order in Layer”均相同,那么在3D世界坐标中离相机更近的Sprite会被先渲染。

在明白了Sprite的渲染顺序后,接下来之要写个简单的脚本更改Sprite坐标的Z值为与其Y值成固定比例即可。但在此之前,先来解释一个重要的小概念,即如何设置精灵位于地面的底部。这里“底部”就是指3D世界中对象与地面接触的部分,示例如下:

<ignore_js_op>

我们要做的是在改变Sprite坐标Y值的同时改变其Z值,上图在3D环境的效果如下图:

<ignore_js_op>

理解了以上内容,就可以写脚本了,代码如下:

 1 using UnityEngine;
 2
 3 [ExecuteInEditMode]
 4 public class IsometricStaticObject : MonoBehaviour {
 5
 6     [SerializeField]
 7     private float m_floorHeight;
 8     private float m_spriteLowerBound;
 9     private float m_spriteHalfWidth;
10     private readonly float m_tan30 = Mathf.Tan(Mathf.PI / 5);
11
12     void Start()
13     {
14         SpriteRenderer spriteRenderer = GetComponent<SpriteRenderer>();
15         m_spriteLowerBound = spriteRenderer.bounds.size.y * 0.5f;
16         m_spriteHalfWidth = spriteRenderer.bounds.size.x * 0.5f;
17     }
18
19     // Use this condition for objects that don’t move in the scene.
20 #if UNITY_EDITOR
21     void LateUpdate()
22     {
23         // Use this condition for objects that don’t move in the scene.
24         if (!Application.isPlaying)
25         {
26             // Update the position in the Z axis:
27             transform.position = new Vector3
28                  (
29                      transform.position.x,
30                      transform.position.y,
31                      (transform.position.y - m_spriteLowerBound + m_floorHeight) * m_tan30
32                  );
33         }
34     }
35 #endif
36
37     void OnDrawGizmos()
38     {
39         Vector3 floorHeightPos = new Vector3
40                 (
41                     transform.position.x,
42                     transform.position.y - m_spriteLowerBound + m_floorHeight,
43                     transform.position.z
44                 );
45
46         Gizmos.color = Color.magenta;
47         Gizmos.DrawLine(floorHeightPos + Vector3.left * m_spriteHalfWidth, floorHeightPos + Vector3.right * m_spriteHalfWidth);
48     }
49 }

首先需要设置的是“Floor Height”,该属性决定Sprite的下边界在Y方向的偏移。 在3D世界坐标中,它用于设置Sprite在场景中的Z深度。 如果一个Sprite的底部比其它Sprite更高,它将被渲染在其它Sprite后面。

<ignore_js_op>

然后存储Sprite高度与宽度的半值,以便对Z坐标进行一些简单的数学运算。在《A Place for the Unwilling》游戏中使用了30度的等距切角,但您也可以将Z坐标设为与Y坐标一致,不影响游戏效果。

这里使用OnDrawGizmos方法在当前的地面高度绘制一条线,以便可以在编辑器中设置为最终的精确位置。另外,对于有些游戏运行后永远不会移动的对象,可以使用“if(!Application.isPlaying)”和“#if UNITY_EDITOR”条件在运行时保存计算结果,因为可能会有上百个Sprite同时绑定该脚本。

以上设置完成后,就可以在场景中移动Sprite并保证渲染顺序正常了,但还有两种情况需要更多的设置。

在处理中心不在中间位置的Sprite时,需要将其分为几部分。以下面的建筑为例,由于它的底部是矩形,如果整个建筑仅设置一个Floor Height值,那角色将只能沿着它前方的那面行走,并且会遮挡角色!为了解决这个问题,就需要将建筑Sprite分为两个部分,并为每一边设置不同的地面,如下图:

<ignore_js_op>

另一种情况是将某个Sprite作为另一个的子对象时。仍然以建筑为例,如果想为建筑增加窗户或招牌,这些附加物就不能使用与建筑相同的脚本,因为有些窗户可能位于建筑后面或顶部。这个问题很容易解决,只需创建建筑的子对象重置其坐标,并将Z坐标值设为-0.001,然后将所有需要附着在建筑上物体放置于该子对象下,将这些物体的Z坐标设为0,这样就可以与实际建筑保持0.001的距离,并且它们离相机更近。

<ignore_js_op>

最终3D环境下的完整场景如下:

<ignore_js_op>

Unity引擎本身就已经提供了非常灵活的工具来实现这样的功能,下面来看看这种实现方式存在的一些限制,以及一些有助于改进工作流程的扩展方法。

这种实现方式最大的限制就是制作很薄的墙壁时,因为使用该方法必须将Sprite切割为多个与墙壁厚度一致的部分,以便场景中的物体可以在墙壁前后移动。示例如下:

<ignore_js_op>

对于飞行物来说可能也比较麻烦,但如果注意其摆放的位置就可以避免出现问题。还可以通过修改Sorting Layer的值让它们永远位于场景主要对象的前方或后方。

最后分享一下如何扩展这种方式以适用更多的场景。

Isometric Colliders: 根据角色在游戏中的移动方式,实现一个小脚本为角色创建一个与游戏场景的图片摆放角度一致的碰撞体。

<ignore_js_op>

IsoVector类:该类包含一些常用的方向向量(N,W,E,S,NE,NW,SE,SW),以及从自定义方向获取向量(反之亦然)的方法,或者获取给定方向的反向向量(例如输入南获取北)等。

本文介绍的内容不一定是最佳的解决方案,但也展现出了很好的学习思路,从最开始想到编写脚本调整Sprite的Z值来正确渲染一切对象,解决了一开始构建游戏场景的问题。随着继续扩展代码库,也丰富了一些自定义类来加入新功能,同时维护好项目结构。希望这篇文章对正在使用Unity开发这种等距游戏的开发者有帮助!

原文连接:https://madewith.unity.com/en/stories/what-i-learned-from-trying-to-make-an-isometric-game-in-unity
原文作者:Martín Pane

时间: 2024-10-13 22:23:16

Unity2.5D Sprite层级显示遮挡问题处理的相关文章

三维模型2.5D轮廓提取及遮挡部分的剔除

轮廓提取相对容易,只需在2.5D渲染视角下,导出模型的顶点坐标以及基于视角的消隐后的三角形面,将三角面投影后合并就可得到轮廓,轮廓坐标基于2.5d图的基准坐标换算就得到.提取轮廓的在我另外一篇文章中有试用的插件,基于3dmax2013的. 轮廓遮挡部分的剔除,如果自动完成的话,效率特别低. 要处理一个模型,原则上要对模型可见面上的点沿视线方向引出直线,与相关模型的可见面做相交运算(在目前2.5D的视角下,模型包围盒的最小y值小于待处理模型都要计算),有交点的模型就用该模型的轮廓来裁剪待处理模型的

使用swift在sprite中显示圆角UIButton按钮

我对objective-C不是很熟,UIKit以前没有用过,SpriteKit也只看了遍教程,然后看了一遍swift语言教程,此时开始编写这个程序,所以遇到的问题比较小儿科,解决方法也是曲线救国,希望有高人指点解决这些问题的简单方法,有好的解决方法后,我会随时进行本日志的修改,以免误导他人. 程序界面很是简单 然后点击中间的游戏区域,会roll色子,随机产生1~6的数字,飞机前进若干步,遇到梯子向前跳若干步,而遇到蛇则后退若干步,指导抵达25格,游戏结束. 上面有一个标签,显示游戏状态和Roll

解决vs解决方案中的类文件没有正常层级显示的问题

VS2013在使用svn进行文件的下载时,因为某些原因,更新完后,功能节点类文件在解决方案列表中并没有正常的按层级显示,而是显示在了同一层次上.造成这样的原因,无可追溯,但是解决这个问题的办法则简单可循. 在VS2013中建立项目时,VS会自动产生 .csproj 文件,这是C#的工程文件,其中记录了与工程有关的相关信息,例如包含的文件,程序的版本,所生成的文件的类型和位置的信息等. 打开工程文件,发现没有正常显示的类文件信息有缺失,于是按照正常显示的类的文件信息,比照补充后,保存.重新加载项目

IE7绝对定位层级显示问题

问题: 当父级元素为相对定位position:relative;,子元素为绝对定位position:absolute:并且层级较高时,一般浏览器显示为子元素在父元素之上,但IE7子元素却被父级兄弟元素遮盖在下面, 解决: 1. 可设置当前父级元素层级高于父级兄弟元素, 2. 若子元素有下拉时,可设当前父级元素鼠标滑过时为相对定位position:relative; 层级设置高于其他父级兄弟元素z-index:1;

object层级显示问题

项目有一个双击监控视频全屏的需求,视频播放使用的是IE下的ActiveX控件,web页面中使用HTML嵌入对象元素object.预期方案如下: 1.在开发ActiveX控件时加入双击事件. 2.通过div包裹object,控制div的大小从而间接实现全屏效果. 在第二种方案中,首先经过测试添加单击或双击事件均无响应. (疑问1:object不支持单双击事件?但是这种方式可以响应: document.getElementById("objectid").onclick=fullScree

Query Designer:Hierarchy层级显示

声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4297530.html     引入外部的Hierarchy: 每个特征都可以有Hierarchy(特征可以有三张表:属性+文本+Hierarchy),这个就是外部的Hierarchy(相对于上面

几个数据库的小案例(三):用递归实现TreeView层级显示

从这个小案例我学到了不少知识.这些无论如何无法从书里得来.正所谓实践出真知,学习编程需要大量实践这句话永不过时. 首先:好的代码和坏的代码带来的性能上的差异很明显.好的策略可以让你的程序运行速度大大加快.而差的代码则能让你的程序直接卡死. 这是我写的递归填充TreeView: private void GetNode1(TreeNode tNod, int id) { using (SqlConnection conn = new SqlConnection("server=.\\sqlexpr

unity销毁层级物体及 NGUI 深度理解总结

http://www.2cto.com/kf/201311/258811.html 1.想找到层级面板中某个物体,并销毁,利用下面的代码: GameObject  obj = GameObject.Find("所要找的名字"); Destroy(obj): 如果要找的物体比较多,可以将找到的物体放在一个数组中,对于物体的名字如果是按照顺序或者一定规律命名的话,也可以通过for循环统一找并赋值,例如:物体的名字依次为house1,house2,……house10的话,定义一个string

记录在ios系统上,自研app,灰度环境遇到的一个vue页面dom节点已渲染,但是显示部分空白的情况

一.问题产生背景: 在ios系统上,自研app,灰度环境,进入到前端页面后,从不同入口进入到适配车型页面,部分页面显示正常,部分页面显示异常,而适配车型页面的代码逻辑仅仅是请求接口,获取到数据后进行for循环渲染而已 1.vue页面代码如下: 2.js代码如下(请求数据,有缓存先取缓存,没有取接口请求返回数据,直接赋值): 3.接口返回数据如下: 4.除了ios.自研app.灰度环境的某个入口外,其他各端,包括ios.自研app.灰度环境的大部分入口(进到适配车型页)正常如下: 5.ios.自研