如果你是跟着上一篇文章来的,那么你的屏幕上应该有两个角色,我们先把 player_0 (1) 删掉,直接点击 Delete 键即可。

现在,我要让角色动起来。从这里开始,我们就会涉及到编程了。

不会编程的小伙伴不用担心,我会把所有代码清晰的复制粘贴到这里来。你们只需要理解每一行代码,并且给代码写上注释就行。方便以后你复习项目的时候,可以清楚的知道当初自己写了什么。

回到我们的 Hierarchy 界面,我们选中 player_0。然后在 Inspector 界面点击下方的 Add Component 按钮,添加新的元件。Unity 已经为我们准备了一大堆的元件库,但是现在我们需要创建新的元件(代码)。所以我们需要在元件库搜索框里输入 Player。然后列表会弹出两个元件,直接无视它,点击 New Script,然后点击 Create and Add 按钮。

这时候你就会发现 Project 面板的 Asset 目录下出现了一个文件:Player;并且你的 player_0 的 Inspector 面板也会出现一个 Player 的元件。如果没有的话,将刚刚创建的 Player 文件拖曳到 player_0 的 Inspector 面板里。

另外,如果需要删除元件,在 Inspector 面板,右键你所需要删除的元件的名字,然后点击 Remove Component 即可。

我们将这个 Player 文件拖曳到 Scripts 文件夹里。这时候 Script 文件夹的样式将会变成实心的。然后双击打开 Script 文件夹,让后双击打开 Player 文件。系统会选择默认的代码编辑器打开这个文件(通常是 Visual Studio 2019)

修复 Unity 依赖

双击点开之后,初始界面应该如下:

注意看,上面的 MonoBehavior 是白色的字体,这说明他对于浏览器只是普通的文本。但是 MonoBehaviour 是 Unity 的库,这意味着我们的 Unity 源码没被 Visual Studio 关联。所以我们先关闭 Visual Studio,然后回到 Unity。

在 Unity 的右上角菜单里 选择:Edit / Preferences 这时候会弹出一个设置对话框,我们打开 External Tools 标签页,然后在 External Script Editor 的下拉框那里,选择 Visual Studio,然后自动的接下来的属性目录会改变。点击 Regenarate project files,等待 Unity 添加项目依赖就行。

接着再打开刚刚的 Player 源文件,这时就会发现 MonoBehaviour 由原先的白色变为绿色。

开始写代码

我会一并将代码复制到最下方,这里来说 Unity 的编程技巧。一切从简,我们将原先 Unity 生成给我们的函数 Start 和 Update 删除。

移动角色,我们就需要让其检测碰撞,当碰撞到物体时,得让他停下。这里我们就要用到 Unity 的 BoxCollider2D 元件。因为我们开发的是 2D 游戏,所以选的空间后面都带有 2D 后缀(2D 和 3D 计算公式不一样,所以需要区分)。

然后我们写上 Start 函数(同时按下 CTRL + SHIFT + M 就能快速实现 Unity 的函数,在弹出的对话框中选择所需的函数,然后点击确定即可),Start 函数的调用时机是在这个游戏物件(Game Object)被创建之后。也就是说,当我们点击开始游戏,因为场景上有玩家的 Game Object 就会被调用一次。我们通常用 Start 函数来初始化对象,所以这里先用来初始化 刚刚定义的 boxCollider 成员变量。

using UnityEngine;

public class Player : MonoBehaviour
{
private BoxCollider2D boxCollider;

public void Start()
{
boxCollider = GetComponent<BoxCollider2D>();
}
}

由此可见,我们需要给玩家设置一个 BoxCollider2D 元件。回到刚刚创建代码文件的步骤,在 Add Component 搜索框里搜索 BoxCollider2D 然后点击它。

接下来,我们回到 Scene 面板,这时候会发现角色周围多出了一个绿色的框,这就是我们的碰撞侦测盒子。我们可以点击 Inspector 面板里 BoxCollider2D 元件中的 Edit Collider 按钮,然后鼠标点击 Scene 面板中的绿色小方块进行微调。

我的调整参数为:Offset X: 0, Y: -0.01,Size X: 0.1, Y: 0.1

接着,回到 Visual Studio,我们通过上面创建 Start 函数一样的方法,增加一个 FixedUpdate 函数,该函数调用时机是每隔一个固定的帧进行刷新(比如一秒60帧,他就在每个 60 秒的第一帧调用一次)

FixedUpdate 函数通常用来修改有关物理操作。但是不建议将获取按键写在这里,因为他只是在某个固定的帧数调用,所以有一定的概率不会捕获到你所按下的键位影响用户体验。但是目前我们需要控制物理动作,所以用 FixedUpdate 函数。

在 FixedUpdate 函数里,我们需要接收用户输入。更改角色朝向,修改角色位置(移动)

要让他移动,我们就需要一个 3D 的方向变化(Unity 是一个 3D 游戏引擎,只是 2D 少了一个维度,在平面上移动)。我们定义一个 Vector3 类型的变量 moveDelta。然后在每个固定帧(Fixed Update)调用 Input.GetAxisRaw() 函数分别获得横轴和纵轴点击值。

GetAxisRaw 的参数字符串怎么决定?我们回到 Unity,右上角菜单点击 Edit / Project Setting,打开对话框,在对话框左侧列表选择 Input Manager,在右侧界面展开 Axes,然后选择对应的轴(这里有一大堆,我们先看 Horizontal 和 Vertical)。GetAxisRaw 的参数就是对应所需的 Name。

至于怎么决定按下的键位?是这么一回事,我们看上面的图,Name 决定我们怎么去明明这个轴(操作),这里 Horizontal 理解为横向移动。那么横向移动就肯定需要两个按钮嘛(左右键)然后我们还可以通过 a 和 d 键进行移动,那我们就需要设置一正一负(负 -1,正 1)的键位名,负(Negative Button)是 left 而正(Postitve Button)则是 right。那么 a 和 d 键我们就设置在替代按钮(Alt Xxx Button)其他的暂不解释。

所以 GetAxisRaw() 函数的工作原理:当玩家点击a,系统收到的就是 -1 的值,d 则是 1 的值。没有按下任何键位就是 0

我们分别将两个不同的轴保存在 x 和 y 两个不同的变量里。然后将其传递到 moveDelta 变量里。这里需要 new Vector3(),然后将我们获取到的 x 和 y 值传递进去。然后 3D 包括了 xyz 三个值,所以在平面中,z = 0。

接下来,我们需要知道玩家什么时候需要反面。也就是当玩家的横轴被切换之后,就需要根据它所移动的方向来切换。我们通过获取 moveDelta.x 来获取玩家横向移动的值,当他大于0,则朝向右边,小于0则朝向左边。

最后我们需要让角色动起来。完整代码如下:

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

public class Player : MonoBehaviour
{
private BoxCollider2D boxCollider;

private Vector3 moveDelta;

public void Start()
{
boxCollider = GetComponent<BoxCollider2D>();
}

public void FixedUpdate()
{
// 获取用户输入(左右上下)
float x = Input.GetAxisRaw("Horizontal");
float y = Input.GetAxisRaw("Vertical");

// 将 moveDelta 重置
moveDelta = new Vector3(x, y, 0);

// 根据行走的方向,切换行走图的朝向
if (moveDelta.x > 0)
{
transform.localScale = Vector3.one; // 节省资源,所以使用 Vector3 的静态变量
}
else if (moveDelta.x < 0) // 为了避免放开键位之后,moveDelta.x = 0 而导致角色回归原本的朝向
{
transform.localScale = new Vector3(-1, 1, 1);
}

// 让他动起来
transform.Translate(moveDelta * Time.deltaTime);
}
}

最后,我们回到 Unity,等待编译。点击上方的播放按钮 ▶。等待游戏开始,点击 wasd 和上下左右键即可操作角色移动。