纳金网

标题: Unity3d中的碰撞检测(二) [打印本页]

作者: wyb314    时间: 2012-11-2 19:04
标题: Unity3d中的碰撞检测(二)
          上一篇我们了解了Unity里面的碰撞检测的两种方式,都能够很好的解决特定情形下的相关问题。这一话我紧接着介绍LayerMask(层蒙版)的应用,主要用在镜头穿墙上面。但是前提是读者对Unity里面的射线的应用有一个清楚的认识。

        Ray!射线,这并不是Unity里面独有的工具,其应用非常之广,而且此工具及其易用。利用它,你能极其快速的解决一些棘手的问题,比方说:鼠标点击屏幕,主角朝向鼠标点击的世界方向运动。此时,如果不利用射线的话,实现方式不会简单,特别是主角的运动方向的确定。好了,不费话了,看API:
    Ray : a ray is infinite line starting at origin and going in some direction.
我来用我自己理解的方式翻译一下:
一个射线是无限长的。它靠一个原点( origin ,Vector3类型)和一个方向( direciton,Vector3类型)决定。这是当然的,只需这两点,就能在一个三维空间确定一条射线。我们来看一下它的构造方法:



官方给出的实例化一条射线的代码:



看了这个之后,我们实例化一条射线就没问题了吧,注意:上面代码中的:transform.forward指的是该脚本绑定的GameObject自身坐标系下的Z轴在世界坐标系下的单位方向向量。

我们来看一下它的两个变量:



这两个东西我们从名字上面就能看出是什么,单词不认识的可以开有道。但是作为一个文化人,这种基本的单词应该记下来,我说的对吧!
好了,该看看函数了:


这个函数的功能是取得该射线从原点到距离为distance的一个点的坐标。



这个函数用的不多,功能比较简单:打印一条射线的基本信息,读者可以试着打印一下,就像java里面直接用
System.out.println(Object) 打印一个对象得出一个哈希码一样。
接下来我们该看看一个非常重要的类了:Physics,这个类里面的方法很多( 说实话,我只用过一个 ),我们重点看下面一个:


由于这个函数有好几个重载方法,我们今天仅介绍下面这个重载方法:





我们可以清楚地看出:
这个方法里面的前两个和Ray里的构造函数一模一样,这两者之间好像有着某种联系。这是必然的,因为还有下面这个重载函数:




看到没,很坑吧。不过,这也是API的一个亮点,多样化。两个方法的底层实现是一样的。
下面我来着重介绍这几个参数的意义:
orign与direction就不用介绍了吧,一个是原点,一个是方向。第三个从字面上理解是距离,第四个是射线的碰撞信息类,里面包含很多我们可以得到的信息,第五个就是我们今天必须理解的一个很实用的工具:LayerMask( 层蒙版 )。
这个函数的功能按我的理解是这样的:以点origin为原点,direction为方向,在世界空间中发射射线,如果在distance距离内与其他在kDefaultRaycastLayers层级之中的GameObject发生碰撞,那么此函数就返回ture,并且记录碰撞信息。
那么什么是LayerMask呢?我们打开Unity,随便找一个GameObject,哪怕是空的GameObject,你都会发现这样一个显示图标:


看到没?有个Layer,我们试着单击一下这个下拉框:



不好意思,我截图的技术真的很烂,但是我们还是可以隐隐约约看到有好几个选项可供选择,如:Default,TransparentFX等,那么这到底有什么用呢?

原来,Unity再设计GameObject时,对GameObject的做了一种分层的处理。举个例子吧:我们选中任意一个摄像机:可以在Inspector面板中找到这样一项:


Culling Mask,又出现了一个Mask,这和LayerMask又有什么联系呢?算了,我直接说吧:默认情况下摄像机可以渲染任何层级中的GameObject,但当我们在CullingMask中有层没选,那么摄像机在渲染时,就不会渲染这一层的物体,Game视图也就不会出现处在这一层的GameObject。此时Culling Mask选项会出现:Mixed...这种结果。当我们选择Nothing,那么Game视图里面必定不会出现任何GameObject。这个层级我们可以在Unity编辑器的菜单下:Edit->roject setting->Tags下找到:



我们可以看到我们刚才看到的那几个层级名。那么我们现在该了解一下LayerMask这个类了。
    由于文档上讲的我自己都不大明白,后来差了些资料,终于算是知道了这个东西的用法了。LayerMask在程序中我们可以用一个int型的变量对其进行赋值,但是这种赋值可不是随便胡乱赋的。我们看上图,一共有32层,有7个内建层,但是只有4个层是有名字的,分别为:Default  , TransparentFX , Ignore Raycast,Water。而且这四个层并不是连续的( 为什么要这样设计那是开发Unity技术部门决定的,不必太在意这个)。然后,我们可以从第9层:User Layer 8这一层开始,我们可以自定一个层的名字。好了,我们来看LayerMask这个类里面的用法吧:


这是我在文档上截的图,LayerMask有一个变量value,就是这个层级的层级值。让我们来看看下面的三个方法吧,我把这三个方法翻译如下:
1.LayerMask.operatorLayerMask( int intVal ) :LayerMask

给定一个int型的层级号,我们可以得到一个LayerMask对象。

2.LayerMask.LayerToName( int layer ) : string
给定一个int型的层级号,我们将其层级名返回。

3.LayerMask.NameToLayer( string layerName ) : int
给定一个层级名,我们将此层级的层级号返回。

那么我们来打印几个层级的层级好吧,代码比较简单,如下:

using UnityEngine;
using System.Collections;



public class TestLayerMask : MonoBehaviour {

// Use this for initialization
void Start () {

        for (int i = 0; i < 32;i++ )
        {
            Debug.Log(LayerMask.LayerToName(i));
        }
    }

}
代码的功能很简单,就是打印第0层到第31层的层级名,部分结果如下:


可以清楚地发现,当i=0时,我们的层级名是:Default。然后后面有的没有打印出层级名的,原因我就不多说了,你也明白。这样我们就可以得出一个结论:如果一个层属于第i(i介于0到31之间,可以等于)层,那么这个层级的层级序号就为i。对于那些只读的层,我们无法修改其名字。比如内建层。
那么能不能将一个层级序号为5的层级转化为一个LayerMask?答案是不行的,因为这个层在面板里面不能访问。你可以试一下。
        估计大伙现在应该对LayerMask这个类有一定的理解了吧。那好,接下来我会在第三篇文章中重点讲解摄像头的一个穿墙问题,就用到了这个LayerMask,敬请期待。



作者: may    时间: 2012-11-2 19:06
SF!呵呵
作者: 艾西格亚    时间: 2012-11-2 20:13
感谢分享精彩的教程!

作者: 狂风大尉    时间: 2012-11-6 14:27
很不错的教程分享啊。谢谢

作者: .    时间: 2012-11-8 21:15
这是一个好帖子,大家快来围观!!

作者: 狂风大尉    时间: 2012-11-11 00:45
顶起,楼主的教程很不错啊,学习学习!

作者: wyb314    时间: 2012-11-11 07:37
这是我自己的一些心得,希望圈友多支持一下,多来踩踩。
作者: 王者再临    时间: 2012-11-11 12:22
希望能介绍一下light probe这个东西的用法,貌似很多论坛都没看到相关的教程。

作者: 崇尚现在    时间: 2013-5-29 11:45
谢谢了 很好的教程
作者: jobaba    时间: 2013-5-30 15:55
thaks for shares
作者: jobaba    时间: 2013-5-30 15:55
thaks for shares
作者: 宁唯是宁唯    时间: 2013-12-26 15:42
thaks for shar
作者: 宁唯是宁唯    时间: 2013-12-26 16:27
111111111111111111111111111
作者: qwer4650987    时间: 2013-12-27 16:02
支持楼主




欢迎光临 纳金网 (http://wwww.narkii.com/club/) Powered by Discuz! X2.5