Unity–碰撞交互

本博重在回顾之前的知识点,不具有普适性,大神绕道。


我们来实验Unity很重要的碰撞检测系统,实现门的开和关,说白了就是碰撞交互产生动画,动画,动画,并且是有声音的动画。


我们首先来打开之前创建的Project,就是拥有地形和FPSController的那个项目,我们进入到工程目录发现:

点击Assets | test.unity,可打开工程。注意:后缀是.unity的文件其实表示当前工程中的一个scene。

(YG: 还可以在Unity中open scene或open project来打开。)

 

然后我们导入资源包:

在主菜单栏中选择Assets | Import Package | Custom Package,然后通过路径选择packages文件夹中的vr_experiments.unitypackage文件导入,在弹出对话框中点击Import确认导入。

有人问了,这个后缀名为unitypackage的文件是啥子用p>

其实就是可以理解为,别人写了一个Unity项目,里面有好多东西,然后你需要用到它的图片啊、模型啊、动画啊、之类的,然后你就可以导入package的形式得到别人写的那个项目。


下面我们要添加一个屋子,这个屋子可以实现,当人碰到它门的时候,它就开门;当人离开它的时候,它就关门。

完成导入后,在工程面板打开Book Assets文件夹,然后在Models文件夹中选择outPost。我们接下来将在检视面板中调整哨岗模型的设置,在默认基础上做出如下修改。

很显然,这是一个3D模型,大概是别人用3DMax或者其他建模软件搞出来的屋子。

在其检视面板上部,你会看到三个按钮:Model, Rig和Animations。

◆点击Model按钮,出现Model相关的参数面板。把Scale Factor(缩放系数)设为1.5,勾选 Generate Lightmap UVs产生光照贴图UV)。下拉滑动控制条,点击Apply确认设置。(YG:注意这里须明确指出是否保存。按apply保存,按revert表示不保存。另外,注意目前这个outPost还没有加入到场景中。)

点击Animations按钮,出现Animations相关的参数。我们要添加3个动画剪辑(分别代表闲置状态、开门动画、关门动画),我们点击Clips菜单栏下的“+”按钮,在默认剪辑的基础上再添加2个动画剪辑,分别按下图命名动画剪辑,并设置其起始、结束的关键帧数。

这个Default Take就是整个的开门关门的动画,我们只是判断了一下,然后把动作分解了。动画是fbx文件中自带的。

完成设置后,点击下方的Apply按钮应用设置。现在,模型已经做好了放入到场景中使用的准备工作。

回到工程面板,选中outPost,把它拖到场景视图中,由于Unity地形自带碰撞器,我们的哨岗会自动对齐到地面。模型放入场景后,它的名字也出现在了层次面板中。把鼠标放在场景视图中,按下F键聚焦该模型。

在检视面板中修改模型的Transform组件到合适位置(一个比较平坦的地面):

调整好Transform属性后,在下面的Animation菜单栏中取消勾选Play Automatically(自动播放)因为我们需要玩家在靠近门的时候才播放相应动画,所以取消自动播放。

你看,因为outpost.fbx自带了动画,因此我们把它放在场景中实例化的时候,也会有Animation的组件。


添加碰撞器:

为了打开门,我们的门就需要感应到有物体碰到了它,这样它就能做出正确的反应。要门具备感应能力,我们需要为它添加碰撞器。在层次面板中展开outPost,选择其子物体door,双击该物体聚焦,这时我们看到检视面板中有一个叫做Mesh Collider( 格碰撞器)的组件。这是我们在模型导入设置时,勾选了Generate Collider(生成碰撞器)的结果。这个 格碰撞器是根据模型来自动生成的,它很精确,但却是一个“昂贵”的碰撞器,我们的门只需要一个简单的盒碰撞器就行。意思就是这个比较 格太精密了,拖低Unity的计算效率。

就这个,我们不用它,然后在主菜单栏中选择Component | Physics | Box Collider,添加一个盒碰撞器。添加了BoxCollider 之后我们可以把之前的Mesh Collider 勾选掉了


设置标签:

标签在Unity中,可以用来表示游戏对象,特别是作用在脚本中。使用过Flash的同学,可以把它类比于影片剪辑的实例名,可以用在脚本中引用物体对象。确保door在选中的状态下,在检视面板中点击Tag下拉菜单,然后选择Add Tag(添加标签)

(YG: Tag是为了在脚本中引用某个物体或某类物体而设的。只要给某个物体或某类物体打上Tag,那么在脚本中就可以通过Tag来引用了。如果不用Tag,你在脚本中也可以通过物体的名字来引用,但是一次只能访问一个物体。有了Tag,是你有可能一次访问多个物体)

然后在弹出的Tag Manager菜单栏中添加playerDoor标签,点击Element 0右边输入标签名,如下图。这时Size(标签数)自动增加1,并出现Element 1来为下一个标签做好准备。

添加好标签名后,这时我们完成创建了这个新标签,但是并没有把它和我们的门对象关联上。让我们回到层次面板,再次选中door对象,然后再下拉Tag菜单,选择playerDoor标签,完成关联操作。

这是Unity的常见操作。


添加刚体组件:

现在我们具备了更具效率的碰撞器,我们还需要为door对象添加一个刚体组件。否则我们的物理引擎无法知道我们的door对象中有个碰撞器,且会把它当作一个不能移动的物体看待。总之,我们如果想让一个物体受到物理效果的影响,我们就要为它添加刚体组件。

现在我们在选中door物体的状态下,作如下操作:

选择主菜单栏Component | Physics | Rigidbody添加刚体。然后在检视面板的Rigidbody组件中勾选Is Kinematic(是否动力学),和取消Use Gravity(使用重力)(YG:如果不取消gravity,门会消失。Is Kinematic如果不选中门可能会倒掉。)

(YG:注意box collider和rigidbody组件的区别。这两个组件都属于Physics类中的组件,即物理引擎所考虑的组件。Box collider会探测到有东西和物体发生碰撞了,但并没有影响碰撞后怎么反应,而rigidbody就是用来设置碰撞后怎么反应的)


为门对象添加声源组件:

为了让门对象在打开、关闭的时候能够发出声音,我们需要为它添加Audio Source组件。确保door对象处于选中状态,再从主菜单栏中选择Component | Audio | Audio Source添加声源组件。不加Audio Source这个component后面就不会发声,即使你用audio.PlayOneShot播放声音文件也不行。而且更奇怪的是门也不会自动关上!什么原因呢span>


触发碰撞器:

添加完声源组件,接下来就是我们的核心步骤了,那便是添加触发型碰撞器。顾名思义,触发型碰撞器,在本例中就是玩家控制的角色进入碰撞器区域内,然后就会触发播放开门动画、开门声音等。

并且我们一旦把碰撞器设置为触发型,它便不再形成物理障碍,也就不会阻止玩家通过其区域。这一点很重要,因为需要玩家在进入碰撞区域时 触发碰撞器、并顺利通过它,而不被碰撞器阻拦,同时岗哨的门被触发而自动打开,这和我们日常生活中的“感应门”的概念有些类似。因此我们的触发碰撞器最好环绕在门的周围,但是我们又不想让它跟随门一起移动,所以我们不能把触发碰撞器添加在门上面,而是要把它设置在其父对象outPost上,这样我们就解决了碰撞区域会移动的问题。

把思路理清后,我们接下来就来添加碰撞器。在层次面板中选择door物体的父级对象outPost,然后在主菜单栏中选择Component | Physics | Box Collider,添加一个盒碰撞器。这时你会发现它被创建在了outPost的中心位置,在检视面板中调整它的中心位置坐标Center(0, 1.8, 3.8),和大小Size(3.8, 3.8, 3.8)

(注意:大家在设置碰撞器的位置坐标和大小参数时,不一定是和上面给出的数值一样,需要大家参考实际,一切以能让碰撞器环绕包裹住door物体为准,请参考下图。)

上面的步骤只是为我们创建了一个简单的盒碰撞器(Box Collider)那如何把它变成触发型碰撞器呢有些同学已经注意到,在检视面板中的Box Collider组件下,有一个Is Trigger选框。对,就是它,把它勾选上,我们就可以把简单的盒碰撞器转化为触发器碰撞器了。


ok,这些在Unity界面上的操作基本都已经完成,接下来就是写逻辑了,添脚本,把这些东西串起来了。

首先我们需要对door进行控制,我们创建DoorManager.cs:

我们定义了两个私有变量三个公有变量、并初始化前三个变量,私有变量不会在检视面板中出现,公有变量则会。

◆我们定义了一个私有变量doorIsOpen,让它保存门的状态(开启,还是关闭);

◆然后接着定义了另一个私有变量doorTimer,用它来做计时器,当门打开时我们用它来计算门开启的时间;

◆还有一个公有变量doorOpenTime,我们用它来设置门处于开启状态的时间;

◆最后两个公有变量,我们用来引用开门/关门的音频素材。

上面我们定义了一个名为:DoorCheck的函数。首先它会检测门的状态,如果门是关着的(if(!doorIsOpen)),那么我们就把门打开。

接着我们又定义了执行开门动作的函数:DoorOpen。在函数中有三条语句:

●告诉door的父级对象,播放开门动画;

●播放开门声音文件;(…PlayOneShot(doorOpenSound);)

●把门的状态改为“开启”;

到此,我们完成了开门/关门的所有脚本,记住保存当前脚本。接下来,我们还有最后一个步骤要完成。

当玩家进入触发器碰撞器的区域内,Unity会调用一个叫做OnTriggerEnter的函数。而我们需要通过这个函数,来控制我们的door对象开门。否则,即便我们写好了开门的代码,但那些代码永远不会被调用。

大家注意,在OnTriggerEnter函数中有一个叫做col的参数,它保存了碰撞事件的所有信息。而在函数内部我们首先检测与触发器碰撞的游戏对象是不是玩家(“Player”),这里我们检测的是标签。(注意:Player标签是Unity自带的,无需我们手动添加.但是需要我们把标签“贴到”我们的玩家对象上。大家在层次面板中选择First Person Controller,然后在检视面板中下拉Tag右侧的菜单,选择Player即可。)

transform.FindChild().SendMessage:那就找到outPost的子物体door,然后在其脚本组件中寻找并调用DoorCheck函数,而DoorCheck函数我们已经写好,这样一来我们就完成了本次实验中的所有脚本编写

ok。总而言之就是这个样子,outPost自带了一个box Collider,然后这个碰撞器是触发式的,一旦判断它碰到了tag为player的东西,就调用door的check函数,由于door这个游戏物体带的脚本有Update,就会进行门开的时间计算,一旦超过某个阈值,就会调用关门的动画,并且播放关门的声音。注意动画是在outPost身上的, 但是声音却是在door身上到。


下面来让我们总结一下,补充知识:

勾选 Generate Lightmap UVs(产生光照贴图UV)

这是为了输出纹理贴图,然后允许你在ps中自己改贴图而设的选项。但在我们这个实验中,其实是用不到这个的,可以不勾选。

 

为了实现碰撞检测,可以有两种方法,一种是component–physics—mesh collider,这个比较精细;另一种是component—physics—box collider,这个比较粗,但效率高。这两种都有“Is Trigger”项可以让你来选。

如果是box collider的话,你可设此box的位置和长宽高。

如果是mesh collider的话,你可设其mesh。

生发开去,打开component—physica你会发现一共有6种collider: mesh, box, sphere, capsule, wheel, terrain collider。当你创建了某个物体,他会根据你这个物体的形状来自动默认弄个collider。如你造了个sphere,那么默认就是sphere collider;如果你引入的是个一般模型,那么会是mesh collider。mesh collider中有个mesh项,专门让你来设mesh。不设是没用的。

可见实际游戏引擎中的碰撞检测比理论上讲得其实更灵活更实际。

 

但实验4中的那个outPost是个prefab,你会发现其Inspector面板比较特殊,有个outPost Import Settings,下面有三个按钮:Model, Rig, Animations。Model中有个Generate Colliders,如果你勾选了的话,那么当你从此prefab生成一个outPost实例的话,就会发现此outPost下的每一个子物体都会自动设了一个mesh collider的component,而且其mesh collider的mesh就是此物体。但此outPost本身并没有一个统一的mesh collider,显然,我估计因为此outPost本身就是一个empty object,所以也无所谓几何了。只要其所包含的子物体有collider就可以了。

 

关于audio

不加Audio Source这个component后面就不会发声,即使你用audio.PlayOneShot播放声音文件也不行。而且更奇怪的是门也不会自动关上!什么原因呢span>

答:如果你不加audio组件,那么脚本中DoorOpen中的audio就无所指(压根就没audio这个变量了)。当然你注释掉那两条audio语句就可以了,只是没有了声音。

你如果没加AudioSource组件,而调用了audio,那么运行到开门时下方会出现警告的。它其实停在了那儿,所以你才不会进入到关门的程序中。

你可在脚本底部添加:

@script RequireComponent(AudioSource)

这样一旦你加了audioSource组件,想删除时就会出警告。这是为了让你误删除组件而设的。

注意这个audio是类Component的变量!就是attach在物体上作为AudioSource的。

 

关于动画:

那三段动画:idle, doorOpen, doorShut是哪儿来的呢p>

其实这整个outPost都是在3ds max中建好的模型一块儿导入到unity中的,动画也是在3ds max中做好的,所以就自动带入到了unity中了。

当你在3ds max中设置了动画,并通过fbx导入到unity中后,你会发现在unity的project面板中会出现一个default take之类的东西,这其实就是那整段的动画,你需要在前面提到的animation面板中将其分割开成三段,并点击apply,这时project面板中的new clip就消失了,取代之的是三个动画段的名称。

 

你会发现在outPost的inspector面板的animations栏中的那三段动画的时间。idle为1-2;doorOpen为1-15; doorShut为16-29。这是因为其将三段动画都统一排在了一块儿,你可通过时间段来区分之。

3ds max文件需要导成fbx文件才可以导入到unity中,你可以在AssetsBook AssetsModels下发现outPost.fbx文件。

 

关于发送消息

transform.FindChild(“door”).SendMessage(“DoorCheck”);

其中transform.FindChild(“door”)表示想把消息发送给的物体(接受消息的对象)。而这个物体中有脚本程序中有个叫“DoorCheck”的函数。故而可以发送以这个函数为名字的消息。

需要注意的是“接受消息的对象”不能直接引用其名称来标定,而是还是要调用FindChild()或GameObject.Find()或GameObject.FindWithTag()来找到。

在下一个指导书中,会出现:

textHints.SendMessage(“ShowHint”);

此中的textHints是一个GUI text对象,之所以可以直接引用之,是因为这段程序中已将这个对象定义成了一个变量!

 

 

 

 

 

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2019年6月11日
下一篇 2019年6月12日

相关推荐