这篇文章我们将继续完善箱子功能。但在此之前,我们需要在 Collidable 类和 Chest 类中增加一层 Collectible 类(原先是 Chest 类继承了 Collidable 类;现在是 Chest 类继承 Collectible 类,Collectible 类继承 Collidable 类)。因为之后会有一些物体是碰撞之后,被玩家捡起来的。

添加这层继承关系是因为,假如之后我们有武器了,当武器挥动的时候,应该是碰撞(Collidable)处理而非获取(Collectible)处理。而箱子则是获取(Collectable)处理。

所以,修改之后代码如下:

Chest.cs

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

public class Chest : Collectable
{
protected override void OnCollide(Collider2D collider)
{
Debug.Log(collider.name + "This is called from Chest.cs");
}
}

Collectable.cs

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

public class Collectable : Collidable
{

}

Collectable 类

接下来,我们要修改 Collectable.cs 的代码。因为是继承了 Collidible 类,所以我们覆盖重写 OnCollide 函数。它需要判断碰撞对象是否为玩家,如果是的话,则将 collected 设置为 true。

这里的 collected 的修饰符是 protected。该修饰符只能允许自己和自己的子类访问,别人是无法访问的。

所以现在知道的概念是:当这个物品被玩家触碰之后,它可以被拾取。至于怎么样被拾取(拾取之后获得什么,或者该物件有什么外观上的变化),则通过继承该类的子类进行定义。

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

public class Collectable : Collidable
{
protected bool collected; // protected 修饰符只能让自己和儿子访问

protected override void OnCollide(Collider2D collider)
{
// 当触碰的对象是玩家
if(collider.name == "Player")
{
OnCollect();
}
}

protected virtual void OnCollect()
{
collected = true;
}
}

上面的代码,我们通过判断碰撞对象的名字是否为 Player 来检测对方是否为玩家,所以回到我们的 Unity,在 Hierarchy 面板中选中 player_0 然后将其重命名为 Player。

Chest 类

所以现在我们知道,Chest 类继承了 Collectable 类,用人话来说,就是他是能够被拾取的。所以原先我们覆盖重写的是 OnCollide 函数,现在将其删掉,替换为 OnCollect 函数。意味着当被捡起来时该干嘛干嘛。

这次重写 OnCollect 函数,我们保留了 base.OnCollect() 函数的调用。这是因为父类(Collectable 类)里这个函数将 collected(判断是否被捡起来的布尔值)设置为 true。当然,如果你觉得代码可读性降低了的话,你也可以删除 base.OnCollect() 函数,然后替换为 collected = true 语句。

在这里的 OnCollect 函数,首先我们要判断这件物品是否没被获取过,如果没被获取过,则才可以被获取。那么对于箱子,我们原本的图片箱子是装满了金币的,当玩家触碰之后,金币将会被获取,而箱子变为空的。

所以我们声明一个 Sprite 对象,这个对象用于保存空箱子的图片。然后再声明一个 int 数值,记录宝箱中拥有的金币数量。这两个变量的修饰符都是 public,所以你可以再 Inspector 面板中直接进行修改。

在 OnCollect 函数里,我们获取 SpriteRenderer 组件(你打开 Inspector 面板就会看到其实图片就是被这个组件渲染上去的),然后将它的 sprite 属性改为上面定义的 Sprite 对象。

我们再用 Debug.Log 函数暂时输出“玩家获取了多少金币”这个信息。

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

public class Chest : Collectable
{
public Sprite emptyChest; // 空箱子的图片
public int goldAmount = 10; // 箱子里拥有的金币

protected override void OnCollect()
{
if (!collected) // 当还未被拾取
{
collected = true;
GetComponent<SpriteRenderer>().sprite = emptyChest;
Debug.Log("获得了" + goldAmount + "金币!");
}
}
}

回到 Unity 界面,我们从 Artwork 文件夹中的 Atlas 图集里找到 chest_1 图片(这是我们之前裁剪好的空箱子),在 Hierarchy 面板中选中 chest_0,然后把 chest_1 图片长按拖曳到 Inspector 面板中的 Empty Chest 栏里。或者点击 Inspector 面板中,Empty Chest 右侧的小圆圈,可以直接选择所需的图片。

所以按照上面的代码逻辑,当玩家碰撞到这个箱子,则会触发,图片将会替换为 emptyChest 我们赋值的图片,然后显示信息“获得了xx金币!”。

我们可以将 chest_0 命名为 Chest(规范命名),然后将其从 Hierarchy 面板中直接拖曳到 Project 面板中的 Prefabs 文件夹里。这样我们就能直接从 Prefabs 文件夹拖曳出来到游戏场景,从而生成多个箱子。Prefab 就是预制件,也就是已经提前设定好的游戏物件。

当然,拖出来的每个箱子也能有自己所拥有的金币。

那么本章就学到这里。今天就学会了怎么和地图上的物件进行交互。