Unity3D学习-Day03-键盘输入控制角色及刚体碰撞

放入场景物体

1.首先将下面图片找到,这是一个精灵类型的文件
在这里插入图片描述
2.将此文件拖放到 Art > Sprites 下,以便 Unity 将此文件复制到 Project 文件夹并采用默认设置将文件导入。
使用精灵来创建游戏对象
要使用 Ruby 精灵来创建游戏对象,请执行以下操作:
1.单击 Project 文件夹中 Ruby 图像旁的小箭头。你会发现,该图“包含”似乎相同的图像,实际上就是 Unity 为你创建的精灵。

2.将此精灵从 Project 窗口拖入 Scene 视图,即可将此精灵作为新游戏对象添加到你的场景中。请使用 2D 视图。
如果你查看 Inspector,会发现 Unity 已自动将 Sprite Renderer 组件添加到新游戏对象。该组件在游戏对象所在的位置绘制一个精灵(在 Sprite 字段中指定)。
3.让我们尝试运行一下。使用移动工具在 Scene 视图中移动精灵(可以使用键盘上的快捷键 W),然后查看 Inspector 中的 Transform 组件的 Position 属性值如何变化。务必将精灵保持在白色矩形内,因为该矩形显示的是摄像机视图的边界。
如果切换到 Game 视图,你将在游戏中看到你的精灵。如果精灵在 Scene 视图中的白色矩形边界之外,则精灵不在摄像机的视野中,因此你不会在 Game 视图中看到此精灵。
为 Ruby 设置坐标
场景中的一切对象都具有 3 个坐标。坐标是游戏对象距场景中心(如果对象没有父项)或其父项(如果有父项)的距离。这三个坐标为:
水平 (x)
垂直 (y)
深度 (z)
因为此游戏是 2D 游戏,所以当前可以忽略深度。
要为 Ruby 设置坐标,请执行以下操作:

  1. 在 Hierarchy 中,选择 Main Camera 游戏对象。在 Inspector 中,你将看到 Ruby 的位置是 0,0,这也是 Unity 的 Main Camera 游戏对象的默认位置。(暂时忽略 z 坐标。)
    2.将 x 和 y 值更改为 0。现在,Main Camera 游戏对象将位于场景的中心。
  2. 在 Hierarchy 中,选择新的 Ruby 游戏对象。
    4.在 Inspector 中,将 x 值设置为 -2 并将 y 值设置为 0。现在,Ruby 将与 Main Camera 垂直对齐,但是在水平方向上,位于 Main Camera 的左侧。(一种有用的思考方式是考虑图形的轴:负值会在水平轴上将游戏对象向左移动,在垂直轴上向下移动)。
    5.选择 File > Save,保存你对场景的更改。或者,可以使用 Ctrl + S (Windows/Linux) 或 Cmd + S (macOS) 快捷键。请记住,在这些教程中,要经常保存更改!

“Ruby’s Adventure:2D 初学者”项目中的距离单位
在设置游戏对象位置时,经常使用“单位”一词。你可能会读到如下内容:“Ruby 游戏对象的位置为 2 个单位(x 轴)和 -4 个单位(y 轴)。”
这些单位的含义会变化。例如:
如果你要创建有关蚂蚁的游戏,可能需要以厘米为单位,并使用大小为 1 个单位 (1 cm) 的蚂蚁精灵。
如果你要创建有关太空的游戏,可能需要将每个单位设置为 10 米,并创建长度为 10 个单位 (100 m) 的太空飞船。
这些教程在提及距离时仅使用“单位”;如果觉得有帮助,你可以将每个单位视为 1 米。

创建新脚本
现在,你可以编写自己的第一个脚本,并向 Ruby 添加运动!
在 Unity 中,脚本是一种特定类型的组件。你可以将脚本添加到游戏对象,你在脚本中编码的任何行为都将应用于此游戏对象。
要创建新脚本,请执行以下操作:
1.在 Project 窗口中,找到 Assets 根文件夹。
2.在一个空的空间中右键单击,然后选择 Create > Folder。
3.将新文件夹命名为“Scripts”。这样将有助于使项目保持井然有序。
4.双击 Scripts 以打开文件夹。
5.右键单击,然后选择 Create > C# Script。
6.将新脚本命名为 RubyController。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RubyController : MonoBehaviour
{
    
    
   public class RubyController : MonoBehaviour
{
    
    
   // 在第一次帧更新之前调用 Start
   void Start()
   {
    
     
       //QualitySettings.vSyncCount = 0;
       //Application.targetFrameRate = 10;
   }
   // 每帧调用一次 Update
   void Update()
   {
    
    
       float horizontal = Input.GetAxis("Horizontal");
       float vertical = Input.GetAxis("Vertical");
       Vector2 position = transform.position;
       position.x = position.x + 0.1f * horizontal * Time.deltaTime;
       position.y = position.y + 0.1f * vertical * Time.deltaTime;
       transform.position = position;
   }
}

什么是物理系统?

学习物理时,你会发现所有的对象移动都是作用力组合的结果。例如,如果你尝试推动箱子,作用在箱子上的力包括:
-重力(将箱子往下拉)
-作用力(你施加来推动箱子的力)
-地面摩擦力(阻碍推动)
如果你要模拟移动和碰撞,需要使用所有数学公式来计算对象上的接触和力。但这需要编写很多复杂的代码。由于物理定律是相同的,因此可以抽象出此代码并在所有游戏中进行共享:这就是物理系统的功能。Unity 有一个内置的物理系统,可以为你计算对象的移动和碰撞。
为避免对我们游戏中的每个对象进行成本高昂的数学运算,Unity 仅对附有 Rigidbody 2D 组件的游戏对象执行这些计算。
添加 Rigidbody 2D 组件
让我们首先向 Ruby 添加 Rigidbody 2D 组件:
1.在 Hierarchy 中,选择 Ruby 游戏对象。
2.将 Ruby 游戏对象从 Hierarchy 拖入 Project 窗口中的 Prefab 文件夹。
3.双击 Ruby 预制件以打开预制件模式。
4.在 Inspector 中,单击 Add Component 按钮。
5.搜索“Rigidbody 2D”,然后选择该组件。
注意:确保你选择的是 2D 组件(还有一个组件名为 Rigidbody,是用于 3D 游戏的组件)。
6.使用 Save 按钮来保存预制件,然后返回到场景。
7.单击 Play 以进入运行模式。糟糕,你的角色刚刚掉出了屏幕!
8. 再次单击 Play 以退出运行模式。
9. 禁用重力
Ruby 掉落的原因是刚体会将重力施加到游戏对象(默认情况下设置为沿 y 轴向下),所以游戏对象会掉落。但在此示例中,这不是你想要的效果。你的游戏具有“自上而下”视图,这意味着一切都在地面上,屏幕的底部不是你的世界的“下方”。
幸好,你可以禁用重力:
1.在 Hierarchy 中,选择 Ruby 游戏对象。
2.在 Inspector 中,找到 Rigidbody 2D 组件。
3.找到 Gravity Scale 属性并将其设置为 0。
在这里插入图片描述
为 Ruby 预制件禁用重力
请注意 Gravity Scale 属性现在是如何显示的:粗体,旁边有一条小蓝线。这是因为你在场景中的 Ruby 上(而不是在预制件上)更改了这个属性!(你没有进入预制件模式)。
如果你将另一个 Ruby 添加到场景中,新 Ruby 仍然会将 Gravity Scale 设置为 1。这样的行为称为覆盖,让你可以对场景中的单个预制件实例(而不是对预制件的所有实例)进行修改。
“覆盖”还始终优先于预制件值,所以如果你编辑预制件并将 Gravity Scale 更改为 0.5,则所有现存的 Ruby 都会将自己的 Gravity Scale 设置为 0.5(但这个 Ruby 除外,因为它的覆盖已将该属性设置为 0)。
但是,在此示例中,你希望对预制件进行此修改:
1.在 Inspector 中,查看 Ruby 游戏对象标题。
2.单击 Overrides 下拉菜单。随即将出现一个对话窗口,其中列出与预制件相比已进行更改的组件(在本例中为 Rigidbody 2D)。
3.单击 Apply All 以便将更改应用于预制件。
现在,如果你要将新 Ruby 添加到场景中,则该游戏对象的 Gravity Scale 会设置为 0。
什么是碰撞体?
现在,物理系统已经识别你的游戏对象(由于刚体),接下来你需要告诉物理系统,该游戏对象的哪一部分是“实心的”。此操作是通过碰撞体完成的。
碰撞体是简单的形状(例如正方形或圆形),物理系统将这样的形状作为游戏对象的近似形状来进行碰撞计算。
向游戏对象添加碰撞体
让我们从 Ruby 游戏对象开始:
1.在预制件模式下打开 Ruby 预制件。
2.在 Inspector 中,单击 Add Component。
3.搜索“Box Collider 2D”,然后添加此组件。
注意:再次提醒,务必使用此组件的 2D 版本!
4.现在,你会在 Scene 视图中看到 Ruby 周围有绿色的轮廓线:

这就是碰撞体的形状,现在物理系统会将这个形状作为 Ruby 的形状。
5.保存预制件。
6.现在,为 MetalBox 预制件执行同样的操作:
双击预制件以将其打开。
添加 Box Collider 2D 组件。
保存预制件。
退出预制件模式。
注意:你尚未将刚体添加到箱子;这是正确的。这是因为不需要通过物理来移动箱子,只需要一个碰撞体即可,无论有没有刚体,游戏对象都将与箱子交互。7.现在,按 Play,然后尝试让 Ruby 穿过箱子以及在箱子周围移动。
解决 Ruby 的旋转问题
首先,让我们处理旋转问题。为此,你需要告知 物理系统不要旋转游戏对象。这在“真实”的物理中也许是可行的,但在此 2D 游戏中不起作用。
幸好,Rigidbody 2D 组件具有与此相关的设置:
1.确认 Ruby 已在预制件模式下打开。
2.在 Inspector 中,找到 Rigidbody 2D 组件。
3.单击 Constraints 旁边的小箭头以展开该部分。
4.启用 Freeze Rotation 复选框以确保刚体不会向 Ruby 添加任何旋转。
在这里插入图片描述
为什么 Ruby 会抖动?
发生抖动是因为 物理系统使用的是场景的简化副本,这个副本中仅包含碰撞体。
此物理场景可以使物理系统的计算更简化,但是物理系统需要:
每当带有刚体的游戏对象在场景中移动时,在物理场景中移动自己的游戏对象副本。
施加作用力并计算碰撞。
将场景中的游戏对象移动到物理场景中计算出的新位置。
在此示例中,这会导致以下事件:
你在帧更新过程中移动角色。
物理系统将自己的游戏对象副本移到相应的新位置。
物理系统发现角色碰撞体现在位于另一个碰撞体(此处为箱子)内,然后将角色碰撞体移回以便不再位于箱子内。
物理系统将 Ruby 游戏对象与该新位置同步。
你不断在箱子内移动 Ruby,而物理系统则将她移回。你要求代码执行的操作与物理系统执行的操作之间的这种冲突就会导致发生抖动。
解决 Ruby 的抖动问题
要解决 Ruby 的抖动问题,你需要移动刚体本身而不是游戏对象变换组件,并让物理系统将游戏对象位置同步到刚体位置。这样,物理系统就可以在进入箱子之前停止移动,而不必在 Ruby 已经进入箱子之后再移动 Ruby。
为此,需要修改 Ruby Controller 代码:
1.双击 RubyController 脚本以将其打开。你的脚本应如下所示:

public class RubyController: MonoBehaviour
{
    
    
    // 在第一次帧更新之前调用 Start
    void Start()
    {
    
     
          
    }

    // 每帧调用一次 Update
    void Update()
    {
    
    
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        
        Vector2 position = transform.position;
        position.x = position.x + 3.0f* horizontal * Time.deltaTime;
        position.y = position.y + 3.0f * vertical * Time.deltaTime;
        transform.position = position;
    }

}

2.因为默认情况下,所有游戏对象都具有一个 Transform 组件,所以 Unity 使 transform 变量在所有脚本中都可用。但是,必须将 Rigidbody 组件手动添加到游戏对象,因此 Unity 并没有将这个变量作为内置变量。
3.除此之外,物理系统的更新速度与游戏不同。每次游戏计算新图像时都会调用 Update,问题是调用速度不确定。在一台速度较慢的计算机上,调用速度可能是每秒 20 张图像,而在一台非常快的计算机上,调用速度可能是每秒 3000 张图像。
为了使物理计算保持稳定,需要定期进行更新(例如,每隔 16ms)。Unity 还有另一个名为 FixedUpdate 的函数,只要你想直接影响物理组件或对象(例如刚体),就需要使用该函数。
但是,你不应该读取 Fixedupdate 函数中的输入。FixedUpdate 不会持续运行,因此有可能会错过用户输入。你需要在类中添加两个浮点变量,以便在 Update 函数内存储当前的水平和垂直输入数据。
如下所示调整 RubyController 脚本:

public class RubyController : MonoBehaviour
{
    
    
    Rigidbody2D rigidbody2d;
    float horizontal; 
    float vertical;
    
    // 在第一次帧更新之前调用 Start
    void Start()
    {
    
    
        rigidbody2d = GetComponent<Rigidbody2D>();
    }

    // 每帧调用一次 Update
    void Update()
    {
    
    
        horizontal = Input.GetAxis("Horizontal");
        vertical = Input.GetAxis("Vertical");
    }

    void FixedUpdate()
    {
    
    
        Vector2 position = rigidbody2d.position;
        position.x = position.x + 3.0f * horizontal * Time.deltaTime;
        position.y = position.y + 3.0f * vertical * Time.deltaTime;

        rigidbody2d.MovePosition(position);
    }
}

让我们来看看新的和调整的每行代码:
Rigidbody2D rigidbody2d; 此行代码将创建一个新变量(名为 rigidbody2d)来存储刚体并从脚本中的任何位置访问刚体。
float horizontal; float vertical; 这两行代码将创建两个新变量来存储输入数据。这些变量曾在 Update 函数中声明过,但是由于你现在需要从另一个函数 (FixedUpdate) 访问这些变量,因此在此处又声明了这些变量。
rigidbody2d = GetComponent();
此代码位于 Start 函数内,因此在游戏开始时仅执行一次。此代码要求 Unity 向你提供与脚本附加到同一游戏对象(即你的角色)上的 Rigidbody2D。
在 Update 函数中,你删除了与移动相关的所有代码。你保留了用于读取输入的代码,这次是位于先前声明的两个变量中。
Vector2 position = rigidbody2d.position;
在 FixedUpdate 函数中,你添加了曾位于 Update 函数中的代码行,并调整了此代码以使用刚体位置。
rigidbody2d.MovePosition(position);
同样,你现在使用刚体位置,而不是使用 transform.position = position; 来设置新位置。这行代码会将刚体移动到你想要的位置,但是如果刚体在移动中与另一个碰撞体碰撞,则会中途停止刚体。
现在选择 Play时,你会看到你的角色已停止抖动!但是 Ruby 仍然停得太早,而且看起来一点也不合理。这是因为碰撞体的大小未正确适应图像
调整碰撞体的大小
我们在本教程前面探讨过,物理系统通过游戏对象的碰撞体来估算游戏对象形状。
如果在 Scene 视图中查看,则会看到箱子和 Ruby 的碰撞体远远超出你认为的游戏对象“实体”部分范围。
选择此箱子或 Ruby 后,你将看到周围出现绿色矩形。
例如,箱子的阴影在碰撞体内部,因此物理系统将此处视为实心。

要调整碰撞体大小,请执行以下操作:
1.双击 MetalBox 预制件(或者在 Hierarchy 中单击 MetalBox 右侧的箭头)以进入预制件模式。
2.在 Inspector 中,找到 Box Collider 组件。
3. 单击 Edit Collider 按钮。
4.单击 Edit Collider 时,碰撞体在 Scene 视图中会变化为在侧面显示四个小方块。可以单击并拖动小方块来调整碰撞体的大小。
如果因误单击而导致小点消失,则只需返回到 Inspector 中并重新单击 Edit Collider 按钮。

添加瓦片地图碰撞

现在,你的角色会与我们具有碰撞体的所有游戏对象碰撞。但是她仍然可以在水上行走,因此你应该让她与水面瓦片碰撞,使她不能在水上行走。但如何才能做到呢?
你可以在空游戏对象上添加一个碰撞体,然后调整碰撞体大小以涵盖水面。但这个过程容易出错,如果你想将水面重新绘制得更大、更小或绘成其他形状,则必须手动更改碰撞体。请注意,这种情况下通过瓦片地图可以轻松快速地更改世界。
幸好,瓦片地图也可以具有碰撞体。无论是否应该碰撞,每个瓦片都可以存储起来,并且瓦片地图碰撞体将为所有要设置以进行碰撞的瓦片创建碰撞体。
要设置瓦片地图碰撞体,请执行以下操作:
1.在 Hierarchy 中,选择 Tilemap 游戏对象。
2.在 Inspector 中,单击 Add Component 按钮。
3.搜索“Tilemap Collider 2D”,然后选择此组件。你会看到在 Scene 视图中为所有瓦片添加绿色碰撞体方块:

这是因为现在所有的瓦片都已设置为进行碰撞。
4.在 Project 窗口中,找到 Tile 文件夹。选择所有不是水的瓦片。(你可以单击一个瓦片,然后按住 Shift 并单击列表中的最后一个瓦片来全部选中。)
5.在 Inspector 中,找到 Collider Type 属性,然后将该属性从 Sprite(目前值)更改为 None。
现在,你选择的瓦片不再被视为碰撞体。如果在 Hierarchy 中选择 Tilemap,你将在 Scene 视图中看到只有水面瓦片具有绿色方块。
6.保存所做的更改。
7. 单击 Play 以进入运行模式,并尝试让 Ruby 在水上行走。她现在应该会与边界发生碰撞。
优化瓦片地图碰撞体
最后一小步是设置瓦片地图碰撞体。目前,正如在 Scene 视图中看到的,每个瓦片都是一个单独的碰撞体。 这种方法效果良好,但会产生两个问题:
物理系统的计算量更大;如果你的世界很大,可能会减慢你的游戏速度。
在瓦片之间的边界上会产生小问题。由于瓦片是两个并排的碰撞体,并且两者之间存在微小间隙,因此有时计算上的微小误差也可能导致仍会发生碰撞的罕见情况。
为了解决这些问题,Unity 提供了一个名为 Composite Collider 2D 的组件。此组件可以获取对象(或对象的子对象)上的所有碰撞体,并由此创建一个大碰撞体。
让我们来添加并配置此组件:
1.在 Hierarchy 中,选择 Tilemap 游戏对象。
2.在 Inspector 中,单击 Add Component 按钮。
3.搜索“Composite Collider 2D”,然后选择此组件。
你会看到自动添加 Rigidbody 2D 组件,因为复合碰撞体需要 Rigidbody 2D 才能正常运行。
4.在 Tilemap Collider 2D 组件中,启用 Used By Composite 复选框。
5.在 Rigidbody 2D 组件中,将 Rigidbody Body Type 属性设置为 Static。 将此属性设置为 Static 将阻止你的世界移动。此外还有助于物理系统优化计算,因为它现在知道刚体不能移动。

现在,围绕水面瓦片的碰撞体是一个大矩形(你可以在瓦片地图碰撞体 (Tilemap Collider) 上启用和禁用 Used by Composite 以便在 Scene 视图中进行比较)。
如果你绘制新的水面瓦片,Unity 会自动更新瓦片地图的复合碰撞体 (Composite Collider) 以便包含新的碰撞体。

猜你喜欢

转载自blog.csdn.net/qq_46061085/article/details/129150942