Unity:认识脚本对象

图片

在本教程中,您将学习如何在Unity中创建和使用脚本对象。 可编写脚本的对象可以帮助改善您的工作流程,减少内存占用,甚至允许您拆分代码体系结构。

根据Unity文档,ScriptableObject是一个类代码,允许您在游戏中创建Scriptable Objects,以存储独立于脚本实例的大量共享数据。

在Unity中使用脚本对象的原因很多。 它们可以减少用于每个其他预制件的内存量,因为Scriptable Object本质上遵循Flyweight设计模式。

可脚本化对象的另一个优点(将成为本教程的主题)是它们可用于方便的数据传输。 我们将以此属性为例,创建一个剑商人商店,该商店将显示各种剑的参数,价格和说明。

注意:本教程假定您熟悉Unity编辑器。 您必须了解如何在代码编辑器中编辑代码,并且具有基本的C#知识。 如果您需要提高Unity技能,请查看其他Unity教程

开始工作


让我们开始下载所需的资料

将下载的文件解压缩到方便的位置,然后在Unity中打开Scriptable Object Tutorial-Starter项目。

您应该看到在项目采购中创建了以下文件夹:


  • _Setup :对于本教程而言,不需要此文件夹。
  • 场景 :包含Sword Merchant场景,我们将在整个教程中进行介绍。 打开这个场景。
  • 脚本 :到目前为止,只有一个脚本,但是在教程中,我们将创建一个新脚本。
  • 剑图标 :包含各个剑的静止图像。
  • Sword Prefabs :包含Sword Merchant场景中所有剑的预制件。

创建可编写脚本的对象


首先,请转到Sword Merchant场景。 它看起来应该像这样:


准备脚本对象


现在该创建第一个可编写脚本的对象了!

在“ 脚本”文件夹中,创建一个名为SwordData的新脚本。 此类将用作剑商人商店中显示的所有剑数据的容器。

在此类内部,首先从ScriptableObject继承而不是MonoBehaviour

 public class SwordData : ScriptableObject { } 

该动作告诉Unity,我们仍然希望像常规MonoBehaviour一样使用Unity的功能和方法,但是我们不再需要将此脚本附加到GameObject。 取而代之的是,它将像处理任何常规资产一样进行处理,可以像创建预制件,场景或材料一样进行创建。

用序列化字段填充脚本,其中将包含与Sword Merchant UI中显示的信息相对应的所有数据。

 public class SwordData : ScriptableObject { [SerializeField] private string swordName; [SerializeField] private string description; [SerializeField] private Sprite icon; [SerializeField] private int goldCost; [SerializeField] private int attackDamage; } 

  • swordNamestring ,其中将存储剑的名称。
  • descriptionstring ,将在其中存储剑的描述。
  • icon :将包含剑图标的精灵。
  • goldCost将剑的价值存储在黄金中的int
  • AttackDamage :用于存储用剑攻击时的伤害的int

注意:SerializeField

在Unity中,SerializeField属性使您可以在Inspector中使用私有脚本变量。 它将允许您在编辑器中设置值,而无需提供其他脚本对变量的访问。

每把剑都将需要其自己的Scriptable Object SwordData实现 。 但是在创建这些实现之前,我们需要向“资产”菜单添加一个可脚本编写的对象。

通过将以下属性添加到SwordData类,将我们的脚本对象添加到资产菜单:

 [CreateAssetMenu(fileName = "New SwordData", menuName = "Sword Data", order = 51)] public class SwordData : ScriptableObject 

  • fileName :创建资产时的默认名称。
  • menuName :在资产菜单中显示的资产名称。
  • order :资产菜单中的资产放置。 Unity将资产分为因子为50的子组。即,值51将新资产放置在Asset Menu的第二组中。

如果一切正确完成,则可以转到Assets >> Create ,然后在菜单中查看新的Sword Data资产。 它应该位于Folder资产下的第二组中:


您还可以在“项目”窗口中右键单击,还可以看到新的Sword Data资产:


新增资料


我们通过在Scripts文件夹中创建一个名为Scriptable Objects的文件夹,并在该文件夹内创建一个名为Sword Data的文件夹,来组织项目。

在新创建的Sword Data文件夹中,创建我们的第一个Sword Data资产。

新的Sword Data资产仍应具有先前指定的默认名称fileName 。 选择一种资产,然后将其复制六次( Ctrl / Cmd + D ),以创建七个Sword Data资产,每个剑一个。 现在根据预制件重命名每个资产:


单击“ Sword数据”文件夹中的第一个Sword数据资产,然后查看“ 检查器”窗口:


在这里,我们看到一种资产,其中将存储有关特定剑的信息。 填写每把剑的信息。 尝试给他们一个独特的描述,攻击时的金钱价值和伤害。 在“图标精灵”字段中,使用“ 剑图标”文件夹中的相应精灵:


恭喜你! 您创建了一个脚本对象,并使用此脚本对象配置了多个资产。

使用脚本对象


现在,我们将开始从这些可编写脚本的对象中获取数据。

首先,我们需要添加一些公共的getter方法,以便其他脚本可以访问Scriptable Object内部的私有字段。 打开SwordData.cs并在之前添加的字段下添加以下内容:

  public string SwordName { get { return swordName; } } public string Description { get { return description; } } public Sprite Icon { get { return icon; } } public int GoldCost { get { return goldCost; } } public int AttackDamage { get { return attackDamage; } } 

打开Sword.cs并添加以下代码:

  [SerializeField] private SwordData swordData; // 1 private void OnMouseDown() // 2 { Debug.Log(swordData.name); // 3 Debug.Log(swordData.Description); // 3 Debug.Log(swordData.Icon.name); // 3 Debug.Log(swordData.GoldCost); // 3 Debug.Log(swordData.AttackDamage); // 3 } 

这是我们使用此代码添加的内容:

  1. 此剑数据的数据容器。
  2. OnMouseDown是内置的MonoBehaviour函数,当用户按下鼠标左键时会调用该函数。
  3. 如何从可脚本化对象资产中获取数据的示例

返回到Unity并转到“ 层次结构”窗口。 在剑的预制中选择1_Longsword游戏对象。 将适当的1_Longsword Data资产添加到Inspector窗口中Sword.cs脚本的Sword Data变量中:


在Unity编辑器中单击“播放”( Ctrl / Cmd + P ),然后单击最左边的剑:


控制台应显示类似于从Sword Data资产传输的数据的信息。

可编写脚本的对象使替换此数据变得容易。 尝试将不同的Sword Data可编写脚本的对象插入到剑的Sword Data字段中。

事件脚本对象


因此,我们创建了一个可脚本编写的对象,您了解了如何在游戏中访问其数据。 但是我们仍然需要将Sword Data与UI集成在一起!

您可以为此使用快速脏的Singleton模式。 但是,我们现在还有其他可能性...

...即可编写脚本的对象! 我们将使用它们来创建干净整洁的代码。

在本节中,您将学习如何使用UnityEvent类创建游戏事件。

游戏活动和听众


在“脚本”文件夹中,创建两个脚本: GameEvent.csGameEventListener.cs 。 它们彼此依赖,因此要消除错误,您需要同时创建两者。

 using System.Collections.Generic; using UnityEngine; [CreateAssetMenu(fileName = "New Game Event", menuName = "Game Event", order = 52)] // 1 public class GameEvent : ScriptableObject // 2 { private List<GameEventListener> listeners = new List<GameEventListener>(); // 3 public void Raise() // 4 { for (int i = listeners.Count - 1; i >= 0; i--) // 5 { listeners[i].OnEventRaised(); // 6 } } public void RegisterListener(GameEventListener listener) // 7 { listeners.Add(listener); } public void UnregisterListener(GameEventListener listener) // 8 { listeners.Remove(listener); } } 

这是上面的代码的作用:

  1. 将GameEvent作为资产添加到“资产”菜单。
  2. GameEvent是一个可编写脚本的对象,因此应从ScriptableObject继承。
  3. 要订阅GameEvent的GameEventListener的列表。
  4. 调用所有GameEvent订阅者的方法。
  5. 最后签名的GameEventListener将是第一个被调用的(后进先出)。
  6. 调用每个UnityEvent GameEventListeners。
  7. 一种允许GameEventListeners订阅此GameEvent的方法。
  8. 一种允许GameEventListener取消订阅此GameEvent的方法。


 using UnityEngine; using UnityEngine.Events; // 1 public class GameEventListener : MonoBehaviour { [SerializeField] private GameEvent gameEvent; // 2 [SerializeField] private UnityEvent response; // 3 private void OnEnable() // 4 { gameEvent.RegisterListener(this); } private void OnDisable() // 5 { gameEvent.UnregisterListener(this); } public void OnEventRaised() // 6 { response.Invoke(); } } 

在上面显示的代码中,项目将继续开发:

  1. 使用UnityEvent类的要求。
  2. 此GameEventListener将订阅的GameEvent。
  3. GameEvent事件引发此GameEventListener时将引发的UnityEvent响应。
  4. 启用此GameObject后,将GameEvent绑定到GameEventListener。
  5. 禁用此GameObject时,从GameEventListener绑定GameEvent。
  6. 当生成GameEvent导致GameEventListener调用UnityEvent事件时调用。

有困难吗? 没什么,随着时间的推移您会发现!

编辑培训


返回Unity编辑器,并在脚本>> ScriptableObjects中创建一个新的Game Events文件夹。 然后,像我们对每个Sword Data资产所做的那样,从Asset菜单创建七个Game Events。 将它们放在新的“游戏事件”文件夹中。


用以下几行替换Sword.cs脚本中的代码:

  [SerializeField] private GameEvent OnSwordSelected; // 1 private void OnMouseDown() { OnSwordSelected.Raise(); // 2 } 

此代码为剑商人的商店增加了两种可能性:

  1. 选择剑时产生游戏事件。
  2. 单击剑时发生事件。

保存脚本。 现在,在剑的每个Hierarchy GameObject中,连接相应的OnSwordSelected事件。


现在,每把剑都有指向该事件的链接,该事件在单击该剑时触发。

UI整合


现在,您需要使UI工作。 我们的目标是在单击每把剑时显示相应的剑数据。

UI链接


在更新UI之前,您必须获得指向UI的每个元素的链接。 首先,创建一个名为SwordMerchant.cs的新脚本,并将以下代码添加到该新脚本中:

 using UnityEngine; using UnityEngine.UI; public class SwordMerchant : MonoBehaviour { [SerializeField] private Text swordName; // 1 [SerializeField] private Text description; // 2 [SerializeField] private Image icon; // 3 [SerializeField] private Text goldCost; // 4 [SerializeField] private Text attackDamage; // 5 } 

使用此代码,我们添加了以下内容:

  1. NameText游戏对象的Text组件的引用。
  2. 链接到DescriptionText游戏对象的Text组件。
  3. 链接到Sword_Icon游戏对象的“图像”组件。
  4. 链接到GoldText游戏对象的Text组件。
  5. 指向AttackText游戏对象的Text组件的链接。

上述游戏对象位于“层次结构”窗口的SwordMerchantCanvas >> SwordMerchantPanel中 。 将脚本添加到GameObject SwordMerchantCanvas ,然后配置所有链接:


侦听器和UI响应


所有剑都有一个事件,UI可以使用GameEventListener脚本进行订阅。 为每个OnSwordSelected事件添加一个GameEventListener到GameObject SwordMerchantCanvas


如您所见,我们的Game Event Listener有两个字段:它侦听的Game Event事件,以及在生成Game Event时引发的响应。

在我们的情况下,响应将由UI更新。 将以下方法添加到SwordMerchant.cs脚本中:

  public void UpdateDisplayUI(SwordData swordData) { swordName.text = swordData.SwordName; description.text = swordData.Description; icon.sprite = swordData.Icon; goldCost.text = swordData.GoldCost.ToString(); attackDamage.text = swordData.AttackDamage.ToString(); } 

此方法接收Sword Data资产,然后使用相应Sword Data字段的值更新每个UI字段。 请注意, GoldCostAttackDamage返回一个int ,因此您需要将其转换为文本字符串。

使用我们的新方法,我们可以向每个GameEventListener添加响应。

对于您添加的每个响应,您都需要一个指向SwordMerchantCanvas游戏对象的链接,作为None(对象) 字段的值 。 之后,从“ 仅运行时”下拉列表右侧的下拉菜单中选择SwordMerchant.UpdateDisplayUI

请小心,并为每个OnSwordSelected事件使用正确的Sword Data资源。


现在,我们可以开始游戏,单击剑,然后查看相应的UI更新!


由于我们使用游戏事件,因此您可以简单地使用SwordMerchantCanvas ,并且即使没有UI也可以正常运行。 这意味着剑的预制件与SwordMerchantCanvas是分开的。

接下来要去哪里?


如果您在故事中错过任何内容,则可以下载完成的项目 ,该项目位于教程的材料中。

如果要继续前进,请尝试使每把剑重现自己的声音。 尝试扩展可编写脚本的对象剑数据并侦听OnSwordSelected事件。

想更多地了解Unity? 观看我们的Unity 视频系列或阅读Unity教程

Source: https://habr.com/ru/post/zh-CN421523/


All Articles