Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Unity基础教程-物体运动(十)——环境交互(Movement with Consequences)

Unity基础教程-物体运动(十)——环境交互(Movement with Consequences)

作者头像
放牛的星星
发布于 2020-11-25 03:34:32
发布于 2020-11-25 03:34:32
3.5K0
举报
文章被收录于专栏:壹种念头壹种念头

本文重点内容: 1、通过加速区域创建跳板和浮空 2、制作一个多功能区域 3、不同材质的交互以及关闭或者激活对象 4、通过事件触发简单对象插值运动

这是关于控制角色移动的教程系列的第十期。它让环境可以以各种方式和对象运动产生交互。

本教程是CatLikeCoding系列的一部分,原文地址见文章底部。

本教程使用Unity 2019.4.4f1制作。它还使用ProBuilder软件包。

(和环境交互)

1 加速区域

一个活跃的环境比一个静态的环境更有意思,特别是它们还能对正在发生的行为做出反应的时候。这个行为表示可以对任何事情做出反应,也可以做任何事情,但是一个简单的例子是类似于跳板的东西:每当有东西落在跳板上时,它就会向上弹起。这可以是我们运动的球体,也可以是其他掉落或被推到跳板上的物体。因此,该行为在逻辑上属于跳板。其他物体不需要意识到它的存在,它们只是突然被弹飞起来了。

1.1 Zone 组件

描述跳板行为的最通用方法是,它是一个区域,可加速进入区域的任何物体。因此,我们将创建AccelerationZone组件类型,其可配置的速度不能为负。

区域可以通过添加一个带有触发器碰撞器的对象到场景中来创建,然后将 zone behavior 附加到它上。你也可以添加可视化的跳板对象,但是我只是用半透明的黄色材质使区域可见。

(Acceleration zone 组件)

当具有刚体的物体进入区域时,我们应该对其进行加速。为此添加一个OnTriggerEnter方法,该方法将触发并调用新的Accelerate方法。进入该区域的所有物体都被执行,但是如果需要的话,可以使用Layer来防止检测到不需要的处理的物体。

(在区域中的物体被推开)

1.2 阻止检测地面

这种简单的方法在发射常规物体时效果很好,但是我们的球体却没有正确发射。相反,它进入该区域时似乎获得了很大的前进速度。发生这种情况是因为我们将其压在了地面上。在这种情况下,可以通过降低“Max Snap Speed ”来解决,但这种方法不适用于设置为低速的加速区域。通常,为了防止被地面捕捉,我们必须指示MovingSphere暂时不要执行捕捉。为此,我们可以向其添加一个公共的PreventSnapToGround方法,该方法将stepsSinceLastJump设置为-1。

现在,如果物体具有MovingSphere组件,则AccelerationZone.Accelerate可以调用此方法,我们可以通过使用Sphere作为输出参数调用TryGetComponent来进行检查和检索。

(发射)

请注意,这种方法不会重置跳跃阶段,因此在没有着陆的情况下弹跳跳板不会刷新空气跳跃。

1.3 持续加速

瞬时速度变化对于跳板很合适,但是我们也可以使用该区域创建其他连续的加速度现象,例如悬浮区域。我们可以通过简单地添加一个与OnTriggerEnter相同的OnTriggerStay方法来支持这个特性。

如果效果持续时间较长,那么通过适当的加速度来实现速度变化会更好一些,因此让我们向该区域添加一个可配置的加速度,最小还是为零。如果将其设置为零,我们将立即进行更改,否则将应用加速

(升空区域 air加速度为1)

也可以施加力,这样质量较大的物体最终加速得较慢,但是固定的加速度使关卡设计变得更容易,因此我使用这个方式。

1.4 任意方向

最后,为了使其可以在任何方向上加速,请在“Accelerate”开始时将体速度转换为区域的局部空间,并在应用时将其转换回世界空间。使用InverseTransformDirection和TransformDirection进行此操作,以便区域的比例不会对其产生影响。现在可以通过旋转区域来控制加速度方向。

(跳跃区域之间的弹跳)

2 意识到存在

加速区域只是如何创建具有特定行为的触发区域的一个示例。如果你需要一个做其他事情的区域,你将不得不为它编写新的代码。但是,检测和响应某个地方出现的某些东西的简单行为是如此普遍,我们理想情况下只想编写一次。有很多行为非常简单,比如只是激活一个对象,就为它创建一个专用的组件类型可能就有些设计过渡了。更复杂的行为通常只是几个简单动作的组合。如果关卡设计师可以通过简单的对象来创建它,那会是非常方便的。

2.1 检测区域

让我们首先创建一个DetectionZone组件,该组件检测其区域中是否存在某些东西,并在有物体进入或退出时通知感兴趣的模块。我们通过从UnityEngine.Events命名空间为它提供类型为UnityEvent的onEnter和onExit字段进行配置来实现。

只需让它在OnTriggerEnter和OnTriggerExit中的适当事件上调用Invoke方法。这将触发对事件注册的所有内容的方法调用。

检查器会将组件的事件作为名为On Enter()和On Exit()的列表公开,这些列表最初是空的。名称后面的括号中没有任何内容,表示这些事件没有参数。

(没有事件)

2.2 材质选择

为了演示其工作原理,我们将创建一个简单的MaterialSelector组件类型,该组件类型具有可配置的材质数组和MeshRenderer参考。它具有一个带有索引参数的公共Select方法,该方法将有效的材质分配给渲染器(如果有效的话)。

创建一个带有红色非活动区域和绿色活动区域的材质选择器组件,这将用于更改检测区域的可视化。虽然不需要将其添加到受影响的游戏对象中,但这仍然是有意义的。

(材质选择器)

现在,通过按项目的+按钮将其添加到检测区域组件的输入事件列表中。通过材质选择器的左下角字段将游戏对象链接到该项目。之后,可以选择MaterialSelector.Select方法。由于此方法具有整数参数,因此其值将显示在方法名称下方。默认情况下,它设置为零,表示不活动状态,因此将其设置为1。然后对退出事件执行相同的操作,这次将参数保留为零。

(设置材质)

区域对象默认使用不活动的红色材质。只要有物体进入区域,将切换材质到绿色。当有东西离开这个区域时,它又会变成红色。

(和检测区域的交互)

2.3 最开始进入和最后退出

该检测区域可以工作,并确实可以完成其编程的目的,即每次进入时调用一次进入,每次离开时调用一次退出。因此,我们可以混合使用enter和exit事件(例如enter,enter,exit,enter,exit,exit),并且当其中仍然有东西时,最终会出现视觉上无效的区域。在区域中保持活动状态时,使区域保持活动状态更加直观。使用保证进入和退出事件将严格交替的区域进行设计也更加容易。因此,它仅应在第一件东西进入时和最后一件东西离开时发出信号。重构事件重命名为onFirstEnter和onLastExit可以使这一点变得清晰,这将需要再次连接事件。

(重命名事件)

为了使这种行为成为可能,我们必须跟踪区域中当前的碰撞体。通过为DetectionZone提供一个List  字段(从System.Collections.Generic命名空间初始化为新列表)来完成此操作。

该列表如何工作? 请参阅“对象管理”系列的“持久对象”教程。

在OnTriggerEnter中,只有在列表为空时才调用enter事件,然后始终将碰撞器添加到列表中以跟踪它。

在OnTriggerExit中,我们从列表中移除碰撞器,并且只有在列表为空时才调用退出事件 列表的Remove方法返回删除是否成功 这应该总是这样的,因为否则我们就无法追踪碰撞器。

(只要有物体在区域就保持激活状态)

2.4 检测突然出现和消失的物体

不幸的是,OnTriggerExit不可靠,因为在停用,禁用或销毁游戏对象或其碰撞器时便不会再调用它。不应该单独禁用碰撞器,因为那样会导致物体掉落到几何体中,因此我们将不支持这种方法。但是我们应该能够处理整个游戏对象在区域内时被禁用或销毁的情况。

在每一个物理步长中,我们都要检查区域内的碰撞器是否仍然有效。添加一个在碰撞器列表中循环的FixedUpdate方法。如果一个碰撞器计算为false,这意味着它或它的游戏对象已经被销毁。如果不是的话,我们就需要检查它的游戏对象是否被禁用了,这一点我们可以通过它的游戏对象的active属性来发现。如果碰撞器不再有效,则将其从列表中删除并递减循环迭代器。如果列表为空,则调用退出事件。

大多数情况下,检测区域中没有物体。为了避免不必要地连续调用FixedUpdate,我们可以在组件唤醒时和最后一个碰撞器退出后禁用该组件。然后我们只有在有东西进入后才启用它。之所以这样有效,是因为无论是否启用行为,总是会触发触发器方法。

接下来,我们还应该处理区域对象自身被停用或销毁的情况,因为当事件仍在区域中时发生时,调用退出事件是有意义的。我们都可以通过添加一个OnDisable方法来完成这两项工作,该方法清除列表并在列表不为空时调用exit事件。

请注意,检测区的组件不应由其他代码禁用,因为它可以管理自己的状态。一般规则是不要禁用检测区域组件,也不要禁用任何可能影响该区域的碰撞器。这些游戏对象应全部停用或销毁。

2.5 热重载

因为热重载(在编辑器播放模式下重新编译)将调用OnDisable,所以它违反了我们刚刚声明的规则。这将导致退出事件被调用以响应热重载,此后已经在区域中的对象会被忽略。幸运的是,我们可以在OnDisable中检测到热重载。如果同时启用了该组件并且游戏对象处于活动状态,则我们将进行热重载,并且什么也不做。当游戏对象没有被销毁而组件被销毁时,情况也是如此,但是我们仍然什么都不做。

我们只需要在编辑器中播放时进行检查,就可以将代码包装在#if UNITY_EDITOR和#endif中。

OnDisable中有哪些相关状态组合? 如果禁用了该组件,仅仅是禁用或反激活游戏对象,则应该继续进行。否则,如果游戏对象未处于活动状态,则该游戏对象将被停用或销毁,应该继续。否则,要么是热重载,要么是仅组件被销毁,则将其忽略。

2.6 更复杂的行为

这只是通过事件可以完成的简单演示。你可以通过将更多条目添加到事件列表来创建更复杂的行为。甚至不必为此创建新方法,直接使用现有方法。而限制则是它必须是与事件的参数列表匹配的无效方法或属性设置器,或者最多具有一个可序列化的参数。例如,我进行了一些设置,以便在更改检测区域本身的可视化效果的同时,在检测区域内有东西时关闭悬浮区域。

(切换悬浮区域)

您必总是对所有事件都响应。有时候可能只有在进入或退出时才触发某些事件。例如,在进入区域时激活某些内容。然后退出并不会取消激活它,而重新进入则会再次激活它,虽然二级激活实际上没有任何用处。

这种基于事件的方法可以用于整个游戏吗? 从理论上讲,是的,它对于快速原型制作非常有用,但是却很麻烦。一旦发现自己重复了复杂的模式,便有必要为其创建专用的方法或行为,这种方法或方法应该更容易使用,并在以后必要时进行优化。

3 简单运动

我们将在本教程中介绍的最后一种情况是移动环境对象。复杂的运动可以通过动画来完成,可以通过检测区域触发。但是通常两点之间的简单线性插值就足够了,例如,对于门,电梯或浮动平台。现在,让我们添加对此的支持。

3.1 自动滑动条

无论插值什么,它在概念上都由从0到1的滑块控制。如何更改值是与插值本身不同的问题。保持滑块分离还可以将其用于多个插值。因此,我们将创建一个专用于该值的AutomaticSlider组件。它的可配置持续时间必须为正。当我们使用它为物理对象设置动画时,我们将使其在FixedUpdate方法中增加其值,并确保它不会溢出。一旦值达到1,我们就可以完成并可以禁用滑块。

再一次,我们将使用Unity事件使它能够附加行为到滑动条。在本例中,我们需要一个随值变化的事件,我们将使用它来传递滑块的当前值。所以我们的事件需要一个浮点参数,可以使用UnityEvent类型。在FixedUpdate结束时调用事件。

但是,Unity无法序列化通用事件类型,因此该事件不会显示在检查器中。我们必须创建自己的具体可序列化事件类型,该事件类型只是扩展UnityEvent。此类型特定于我们的滑块,因此可以通过在类内部以及事件字段本身进行声明来使其成为嵌套类型。

进入播放模式时,滑块将立即开始增加。如果你不希望这样做,请在默认情况下将其禁用。然后,你可以将其连接到检??测区域,以在以后启用它。

(禁用具有值更改事件的滑块)

请注意,在这种情况下,事件的名称后跟(Single),表示它具有一个参数。单精度是指浮点类型,它是单精度浮点数。

3.2 位置插值

接下来,创建一个PositionInterpolator组件类型,该类型通过带有float参数的公共Interpolate方法在两个可配置位置之间插值可配置刚体的位置。使用Vector3.LerpUnclamped,以使提供的值不会被钳位,而是由调用者决定。我们需要通过其MovePosition方法更改身体的位置,以便将其解释为运动,否则将成为闪现。

(位置插值和滑块相连接)

通过将sider和interpolator都添加到同一平台对象,我创建了一个简单的移动平台。插值器的Interpolate方法的动态版本绑定到滑块的事件,这就是为什么其值没有字段的原因。然后,我将滑块连接到检测区域,以便在有物体进入该区域时激活平台。请注意,插值点在世界空间中。

(激活移动的平台)

3.3 自动倒置

我们可以通过向AutomaticSlider添加可配置的自动反向切换来使插值来回移动。这需要我们跟踪它是否反转,并在FixedUpdate中加倍代码,同时必须支持双向。同样,当自动反转激活时,我们必须跳动而不是钳制该值。在持续时间极短的情况下,这可能会导致溢出,因此反弹后我们仍然会钳住。

(自动升降的平台)

3.4 平滑步长

线性插值的运动是刚性的,反转时速度会突然变化。通过将值的平滑变体传递给事件,可以使其加速和减速。通过对其应用smoothstep函数来实现。并使它成为可配置的选项。

(线性VS平滑)

(开启了平滑步长的平台)

3.5 更多控制

可以通过检测区域事件,并禁用滑块组件来暂停动画,但让我们也可以控制其方向。最简单的方法是通过公共属性提供其反转状态。将反向字段替换为自动反向属性,调整其他代码的大小写以使其匹配。

让我们对自动反转选项执行相同的操作。在这种情况下,我们必须保留序列化字段,因此添加一个显式属性。

(更复杂的平台控制)

请注意,方向反转是突然的,因为它仍然是简单的插值。如果要在任何时候平稳停止和反转,则需要创建使用加速度和速度的更复杂的逻辑。

3.6 压碎的碰撞体

移动场景的危险在于,物体最终可能会陷入两个接近的碰撞器之间。当碰撞器之间的缝隙关闭时,身体要么被弹出,要么最终被压入碰撞器或穿过碰撞器。如果碰撞表面成一定角度,则存在清晰的逃生路径,物体将朝该方向被推动。如果不是这样,或者如果没有足够的时间逃脱,则物体最终会被压碎,穿透碰撞体。如果一个物体卡在两个足够厚的简单碰撞器之间,那么它可以留在它们内部,一旦有一条清晰的道路就弹出。否则会掉下去。

(物体被压入地表内了)

如果碰撞表面成一定角度,则物体会被推到一边,并且很有可能逃脱。因此,通过在表面之间留出足够的空间或通过引入倾斜的碰撞器(无论是否可见)来设计这样的配置是一个好主意。此外,将box碰撞器隐藏在地板上可以使它更牢固,以免物体被推入。或者,添加一个区域,在适当的时候触发该区域的销毁,表示它被压碎了。

(带有角度的碰撞器,并且地表下面隐藏了盒碰撞器)

3.7 局部插值

世界空间中的配置可能会带来不便,因为它无法在多个位置用于同一动画。因此,让我们通过在PositionInterpolator中添加一个局部空间选项进行总结。为此,我们添加了一个可选的可配置的Transform,该插值相对于应该发生的插值。通常用插值器引用对象,但这不是必需的。

(相对插值让复用成为可能)

下一章节,滚动。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 壹种念头 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Unity基础教程-物体运动(九)——游泳(Moving through and Floating in Water)
很多游戏都有水,并且大都是可以游泳的。然而,对于交互式水没有现成的解决方案。PhysX并不直接支持它,所以我们必须自己创造一个水的近似值。
放牛的星星
2020/11/25
2K0
Unity基础教程-物体运动(九)——游泳(Moving through and Floating in Water)
Unity基础教程-物体运动(八)——攀爬(Sticking to Walls)
本教程使用Unity 2019.2.21f1编写。它还使用了ProBuilder包。
放牛的星星
2020/11/25
3K0
Unity基础教程-物体运动(八)——攀爬(Sticking to Walls)
Unity基础教程系列(十二)——更复杂的关卡(Spawn,Kill,and Life Zones)
这是关于对象管理系列的第12篇也是最后一篇教程。它涵盖了kill区域的增加和更严格的关卡对象管理。
放牛的星星
2020/10/30
1.8K0
Unity基础教程系列(十二)——更复杂的关卡(Spawn,Kill,and Life Zones)
Unity基础教程-物体运动(十一)——滚动(Animated Sphere)
这是有关控制角色移动的教程系列的第11部分,也是最后一部分。它把我们毫无特色的球变成了滚动的球。
放牛的星星
2020/11/25
3.6K0
Unity基础教程-物体运动(十一)——滚动(Animated Sphere)
Unity基础教程系列(七)——可配置形状(Variety of Randomness)
这是有关 对象管理 的系列教程中的第七篇。它为形状增加了一些行为,并可以针对每个生成区域配置它们。
放牛的星星
2020/10/21
3K0
Unity基础教程系列(七)——可配置形状(Variety of Randomness)
Unity基础教程-物体运动(七)——移动地面(Going for a Ride)
这是有关控制角色移动的教程系列的第七部分。它解决了在运动中的地形上站立和导航的问题。
放牛的星星
2020/11/25
2.3K0
Unity基础教程-物体运动(七)——移动地面(Going for a Ride)
【Unity面试篇】Unity 面试题总结甄选 |Unity基础篇 | ❤️持续更新❤️
答:Awake —> OnEnable —> Start —> FixedUpdate —>Update —> LateUpdate—> OnGUl —> OnDisable —> OnDestroy
呆呆敲代码的小Y
2023/07/24
3.4K0
【Unity面试篇】Unity 面试题总结甄选 |Unity基础篇 | ❤️持续更新❤️
Unity基础教程系列(六)——更多的游戏状态(Saving All That Matters)
这是关于对象管理的系列教程中的第六篇。除了生成形状和关卡索引之外,它还包括保存更多游戏状态。
放牛的星星
2020/09/28
1.3K0
Unity基础教程系列(六)——更多的游戏状态(Saving All That Matters)
[学习笔记]unity3d-物理引擎(一)
刚体 简介 带有刚体组件的游戏物体。 add Compoment-physics-Rigidbody 刚体组件可使游戏对象受物理引擎控制,在受到外力时产生真实世界中的运动。 物理引擎:模拟真实世界中物体物理特性的引擎。 属性 质量 Mass:物体的质量。 阻力 Drag:当受力移动时物体受到的空气阻力。 0表示没有空气阻力。极大时可使物体停止运动,通常砖头0.001,羽毛设置为10。 角阻力 Angular Drag:当受扭力旋转时物体受到的空气阻力。 0表示没有空气阻力,极大时使物体停止旋转。
六月丶
2022/12/26
1.7K0
[学习笔记]unity3d-物理引擎(一)
Unity3D之MonoBehaviour
Unity3D中的MonoBehaviour是一种基于组件的编程架构,用于开发游戏和应用程序。它是Unity引擎中用于创建行为的基类之一,并且可以让你通过重写其方法来定义对象在游戏运行时的行为。
叶茂林
2023/07/30
4670
Unity基础(14)-事件系统
1.Awake:用于在游戏开始之前初始化变量或游戏状态。在脚本整个生命周期内它仅被调用一次.Awake在所有对象被初始化之后调用,所以你可以安全的与其他对象对话或用诸如GameObject.FindWithTag()这样的函数搜索它们。每个游戏物体上的Awake以随机的顺序被调用。因此,你应该用Awake来设置脚本间的引用,并用Start来传递信息Awake总是在Start之前被调用。它不能用来执行协同程序。
孙寅
2020/06/02
1.8K0
Unity基础教程系列(三)——复用对象(Object Pools)
如果我们只能创造形状,那么它们的数量只会增加,直到我们开始一个新的游戏为止。但大部分的时候,当一些物体在游戏中被创建时,它也应该可以被销毁。现在让我们让销毁形状变为可能。
放牛的星星
2020/09/14
3.1K0
Unity基础教程系列(三)——复用对象(Object Pools)
Unity开发入门-环境安装及素材导入
https://docs.unity3d.com/cn/current/Manual/Input.html
码客说
2023/07/24
5960
Unity开发入门-环境安装及素材导入
Unity面试刷题库
答:在构造函数如果有public修饰的静态构造函数时会报:“静态构造函数中不允许出现访问修饰符”,如果什么修饰符都不加的话不会报错,静态构造函数一般是起初始化作用。
孙寅
2020/06/02
4.3K0
Unity中MonoBehaviour的生命周期详解
在Unity开发的世界里,MonoBehaviour是构建游戏逻辑的核心基石。几乎所有的交互、动态效果和游戏行为都是通过MonoBehaviour的脚本实现的。深入理解MonoBehaviour的生命周期,不仅能让开发者更好地掌控游戏的运行,还能有效避免潜在的问题,优化游戏性能。本文将带您系统地了解MonoBehaviour的生命周期,从初始化到销毁,每一个关键阶段都将深入剖析,助您在游戏开发之路上更进一步。
Front_Yue
2025/03/19
2610
Unity中MonoBehaviour的生命周期详解
Unity 基础 - 刚体和 Collider
Unity 中的 物理引擎能够真实的模拟现实世界的物理效果,在 Unity 中使用的是 NVIDIA 的 PhysX 物理引擎,在 Unity 中使用 Rigidbody 让游戏对象受物理引擎控制。
hrscy
2018/08/30
1.9K0
Unity 基础 - 刚体和 Collider
[Unity面试] 2022年Unity面试题分享
【重点面试题】代表面试的时候问到的题目 光背答案是没有用的,一定要动手操作一下,才能知道答案为什么是这个。
全栈程序员站长
2022/11/09
4.5K0
[Unity面试] 2022年Unity面试题分享
Unity2D开发入门-Collider 碰撞体与碰撞检测
在Unity2D中,有多个Collider组件可用于进行碰撞检测和物体交互。以下是一些常用的Collider组件及其功能介绍:
码客说
2023/07/24
3.5K0
Unity面试题(包含答案)
在主线程运行的同时开启另一段逻辑处理,来协助当前程序的执行,协程很像多线程,但是不是多线程,Unity的协程实在每帧结束之后去检测yield的条件是否满足。
bering
2019/12/02
3.4K0
2022年Unity 面试题 |五萬字 二佰道| Unity面试题大全,面试题总结【全网最全,收藏一篇足够面试】
本文将整理的面试题大致分为以下几个模块,方便针对性学习和背题! 由于大部分常用的面试题在网上基本上已经有比较标准的答案了,所以说面试题类的文章基本上大同小异。 所以本篇文章中的部分内容也是直接从网上摘选来的 如果有不对的地方也欢迎指正(尽力不会出现这种情况),某个模块的内容不够也欢迎在评论区指出,我去重新添加上。
呆呆敲代码的小Y
2022/03/13
24.5K0
2022年Unity 面试题 |五萬字 二佰道| Unity面试题大全,面试题总结【全网最全,收藏一篇足够面试】
推荐阅读
相关推荐
Unity基础教程-物体运动(九)——游泳(Moving through and Floating in Water)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档