顾 静

顾 静

1656763980

如何在 Unity 中使用可编写脚本的对象

假设您正在 Unity 中为您的游戏开发一个商家系统,商家在其中销售具有不同名称、价格、描述和图像的商品列表。

您团队中的游戏设计师已经编写了一份 100 页的文档,内容涉及各种项目,其中有一整章专门介绍了多种药水类型。作为首席程序员,您需要决定将这些项目中的每一项存储在系统中的哪个位置,那么您会在哪里存储呢?

在本文中,我们将探索 Unity 中的快速原型设计,以及如何在游戏开发中有效地使用可编写脚本的对象。

可序列化的结构

我相信解决这个系统的最初尝试是使用封装项目属性的可序列化结构。我们称它为 ItemStruct。然后,您将在商家中添加一个 ItemStruct 列表。

因此,通过检查器,游戏设计师可以手动输入这些项目中的每一项,同时让您为游戏的其他系统编程。这种系统的代码和检查器如下所示:

[Serializable]
public struct ItemStruct
{
   public string name;
   public int price;
   [TextArea]
   public string description;
   public Sprite image;
}

项目结构检查器示例

这种方法实际上非常通用,对于许多简单的系统来说,它无疑是最简单、更稳定的方法。

可序列化结构提供了一种通过检查器进行更改的简单方法,同时还利用了在 C# 中作为结构的优点,例如与类相比更适合较小的数据聚合,以及具有值语义。

但是,您的商家系统允许游戏世界中的不同商家。

其中一个,在起始区,应该是卖低级药水和弱临时增益的。为了支持速度较慢的玩家,游戏中更高级的区域仍然有商人出售价格低于更高级别替代品的起始药水。

 

ItemStruct 方法的问题

如果我们坚持使用 ItemStruct 方法,现在可能是商家的不同实例现在拥有相同 ItemStructs 的副本。

如果你已经挑了眉毛,那就准备挑另一只眉毛,因为你的游戏设计师很有可能会在某个时候尝试平衡游戏并对所有低级药水打折。由于 ItemStructs 没有相互连接,因此每个销售它们的商家的每个实例都需要更新。如果您已经在使用预制件和预制件变体,请向我鼓掌,但它们仍然需要更新。

购买是对这些物品进行编程的困难之一,但考虑到游戏也有战斗。击败敌人会获得战利品,例如药水。

应该清楚的是,我们正在寻找的解决方案围绕着将 ItemStruct 从商家(和其他机制)中分离出来,并让他们每个人都使用对它的引用。

因此,对 ItemStruct 所做的任何更改都会立即影响引用它的人。很高兴,Unity 提供了一种简单的方法:可编写脚本的对象。

使用可编写脚本的对象

可编写脚本的对象是 Unity 中的数据保存对象,不需要附加到游戏对象。

因此,我们可以在不将它们添加到场景中的对象或预制件的情况下创建它们。它们独立存在,任何使用它们的实体都使用引用。

可编写脚本的对象不继承 Unity 脚本的默认类 MonoBehavior,但它们确实具有一些类似的行为,并且可以以类似的方式使用。

例如,他们有自己的Awake call以及 OnEnable 和 OnDisable。他们可以拥有自己的方法和成员变量,也可以从使用属性中受益。

此外,它们与常规单声道行为脚本一样容易创建:

[CreateAssetMenu(fileName = "New Item", menuName = "Item", order = 0)]
public class ItemScriptableObject : ScriptableObject
{
   public string name;
   public int price;
   [TextArea]
   public string description;
   public Sprite image;
}

项目脚本对象

正如您在上面的示例中看到的那样,为了提高创建可脚本化对象实例的效率,我们可以使用CreateAssetMenuAttribute添加生成该类型文件的编辑器菜单。

从游戏对象中分离数据

回到我们之前讨论的将 ItemStructs 从游戏中的元素中分离出来的问题,现在一个简单的解决方案是将 ItemStructs 替换为 ItemScriptableObject。每个商家都持有一份其销售的 ItemScriptableObjects 列表。

物品价格的任何折扣或上涨现在都对物品本身进行。更改会立即反映在引用它的任何实体中。现在让游戏设计师为平衡游戏经济而欢欣鼓舞。

此外,其他机制,例如从怪物身上掉落物品或掠夺宝箱,也同样受益。您甚至可以更进一步,使用相同的可编写脚本的对象来设计库存系统或制作系统。

我们作为示例设计的可脚本化对象可以轻松扩展以包含其他数据,例如它在 UI 上显示的图像或制作类型。

在这方面,我们可以对可编写脚本的对象进行两个令人兴奋的扩展,值得一提:Inheritance 和 CustomEditorScripts。

扩展可编写脚本的对象

让我们从继承开始。可编写脚本的对象继承自Object 基类;因此,它们可以在任何可以使用常规 C# 类的上下文中使用。

这意味着我们可以拥有抽象的可编写脚本的对象并使用它们来建模行为,从而受益于所有那些良好的面向对象编程的优点,例如数据抽象和多态性。

例如,我们可以为游戏中的项目创建一个抽象的可编写脚本的对象,其中包含名称、成本和显示图像等标准信息。它还将包含一个抽象方法Use( )。然后每个项目类型可以指定它在使用时的作用。

然后,我们可以为 Potions 创建一个具体的可编写脚本的对象,它扩展了 Item。Potions 可脚本化对象从其父类继承成员变量并强制实现该Use方法。

从这里我们甚至可以通过创建 HealthPotion 扩展或 RejuvenationPotion 来进一步分支层次结构,以便该Use方法具有更精确的实现,并且我们添加更适合每种类型需求的成员变量。

您现在可能已经停下来想知道为什么我们以前不能这样做,因为这是标准的面向对象编程实践。

事实是我们总是可以,这就是使用可脚本化对象的美妙之处:我们不需要改变我们对系统建模的方式。现在的优势是我们可以为与系统本身分离的数据创建实例。

此外,正如我们所指出的,即使我们的游戏设计师现在正在为 LegendarySwordScriptableObject 类型添加数据,我们也可以将我们的系统实现为基于 Items。

public abstract class ItemScriptableObject : ScriptableObject
{
   public string name;
   public int price;
   [TextArea]
   public string description;
   public Sprite image;

   public abstract void Use();
}[CreateAssetMenu(fileName = "New Potion", menuName = "Items/Potion", order = 0)]
public class PotionScriptableObject : ItemScriptableObject
{
   public override void Use()
   {
       //Do Something
   }
}

使用自定义编辑器脚本

我之前提出的另一个方面是使用自定义编辑器脚本授权可编写脚本的对象。自定义编辑器脚本允许我们更改和添加特定实体在 Unity Inspector 中的显示方式。

它们通常在常规 MonoBehavior 脚本中用于添加按钮或显示标签,并且通常用于加快开发速度。

ScriptableObjects 也可以使用他们自己的自定义编辑器脚本进行扩展,这使得它们直接在检查器中成为强大的编辑工具,并且再次与其他实体分离。

例如,我们建议我们的 Item 脚本对象可以包含它的显示图像。使用自定义编辑器脚本,我们可以直接在检查器中显示选定的图像,以方便检查是否使用了正确的精灵。

特别是对于药水,我们可以添加一个按钮,打印给定属性的药水使用结果,以快速评估其结果是否合适。

[CustomEditor(typeof(PotionScriptableObject))]
public class PotionScriptableObjectEditor : UnityEditor.Editor
{
   public override void OnInspectorGUI()
   {
       base.OnInspectorGUI();

       var potionScriptableObject = (PotionScriptableObject) target;
       if (potionScriptableObject.image == null) return;
       GUILayout.Box(potionScriptableObject.image.texture);

       if (GUILayout.Button("Test Use"))
       {
           potionScriptableObject.Use();
       }
   }
}

Potion Scriptable 对象示例
(注:此处使用的精灵最初来自Brackeys的2D Mega Pack )

可编写脚本的对象的其他优点

与常规的 MonoBehavior 脚本相比,无论是否使用自定义编辑器脚本来调整可编写脚本对象的值都有另一个优势:它们在编辑器和播放模式之间是持久的。

这意味着,如果您在游戏模式下测试游戏时更改了可编写脚本的对象,则更改将保存到可编写脚本的对象并在编辑器模式下持续存在。

请注意,构建游戏实例之间的持久性并非如此,也就是说,您不应该使用可编写脚本的对象作为保存系统的替代品。

此外,对可编写脚本的对象所做的更改也会在场景之间持续存在。在场景和游戏状态之间传输信息时,这一优势可以更好地替代单例。

例如,您可以将玩家的信息(例如健康和经验)存储在可编写脚本的对象中,并让游戏中的多个系统引用它。

如果您的角色在战斗场景中失去生命值,然后返回世界地图,则对生命值的更改将保留在可编写脚本的对象中,并且不必通过其他机制共享,例如标记为在加载时不会被销毁PlayerManager新场景。

然而,可编写脚本的对象还有另一种用途,这听起来很违反直觉:无数据可编写脚本的对象。

转换可编写脚本的对象

默认情况下,可编写脚本的对象实例是唯一的,因为它们中的每一个都将拥有一个 GUID。

因此,对可编写脚本对象的实例的多个引用必然会使用正确的引用,尤其是因为它们很可能在检查器中链接到其相应的引用持有者。

另一方面,我们也可以从这个属性中受益,通过它们的内容(例如,重写 Equals 方法)或它们的标识符来比较可编写脚本的对象。

这也使它们成为字典和其他基于键的数据结构的优秀键。

可编写脚本的对象替代品

正如我之前所暗示的,我们可以仅将可脚本化对象用作键,但要保留它们的所有优点。例如,考虑到您游戏中的全球市场系统,您想要指定一个字典,将商家链接到他们对应的城市。

每个商家只能链接到一个城市,城市可以容纳多个商家。你会用什么来建立商人和城市之间的联系?一个字符串?一个枚举?

您可能已经猜到,例如,可编写脚本的对象 CityScriptableObject 可以轻松解决这个难题。

让我们先讨论替代方案。

细绳

字符串必然会出现拼写错误和其他错误,而且维护和更新效率非常低(例如,由于游戏编写者之间的争论,一个城市在开发过程中更改了名称)。比较字符串也不是很有效。

枚举

枚举工作得很好,但是每次新增都需要更改代码库。此外,如果您删除枚举中的一个条目并且必须修复引用它的所有代码(由于游戏作者之间的另一个争论——那些人!),现在一座城市被摧毁了,这可能是灾难性的。

为什么可编写脚本的对象是有益的

另一方面,可编写脚本的对象实例可以在项目视图中创建(无需代码),并且可以在代码中作为常规对象引用,而不是依赖于 switch 语句和其他控制流结构来指定可能的条目。

如前所述,只需添加新的字段和方法,就可以进一步扩展同一个可编写脚本的对象。如果我们需要一个键来引用一个城市,我们的脚本对象可以是空的,并使用资产的文件名本身作为键(无论如何它都有一个 GUID)。

但是,如果稍后我们希望商家在用户界面中使用其城市名称,我们可以轻松地将其作为属性添加到 CityScriptableObject 代码中,并且对代码的任何其他部分没有附带影响。

如果您现在正在考虑这似乎是对可脚本化对象的非正统用法,请记住,这种做法最初是由 Unity Technologies 本身的员工 Richard Fine 提出的,并且已经得到社区中许多其他开发人员的验证。

实现一个使用可脚本化对象而不是枚举的系统与实现一个使用自制密钥系统的系统没有太大区别。

实体将持有对可编写脚本的对象的引用,系统将使用这些可编写脚本的对象进行比较,甚至使用它们自己的成员方法。还记得我们的Use方法吗?下面的代码摘录应该可以帮助您设想一种可能的方法来实现此策略。

[UnityEngine.CreateAssetMenu(fileName = "New Magic Element", menuName = "Magic/Magic Element", order = 0)]

public class MagicElementScriptableObject : UnityEngine.ScriptableObject
{
//Nothing 
}public class MagicPotionScriptableObject : PotionScriptableObject
{
   public MagicElementScriptableObject magicElement;
   public int increaseBy;
  
   public override void Use()
   {   
character.IncreaseMagicElementPower(magicElement, increaseBy);
   }public class Character
{
   public Dictionary<MagicElementScriptableObject, int> magicElements;
  
   public void IncreaseMagicElementPower (MagicElementScriptableObject magicElement, int value)
   {
magicElements[magicElement] += value;
   }
}

判断可编写脚本的对象

凭借其极大的灵活性和简单性,可编写脚本的对象为系统、持久性、数据存储甚至作为单例和枚举的替代品提供了许多新方法。

如前所述,由于它们的继承性,它们的行为与我们已经习惯的许多其他组件相似,这使得开始使用它们的过程更加平易近人。

当然,所有好事都是有代价的——前面讨论的一些方法也可以用不同的技术来实现,这些技术可能会为代码库实现更高水平的性能或稳定性。

灵活性

使用可编写脚本的对象来替换枚举的灵活性可能过于非正统,并且在您的游戏已经很好建立之后产生的收益递减。然后是时候执行反向替换并获取枚举的优势了。

请注意,这可能只有在原型设计阶段完成了长时间的头脑风暴和游戏测试之后才会发生。

表现

在性能方面,可编写脚本的对象的行为就像 C# 对象。它们的性能影响可以在启动游戏时的序列化过程中以及在将它们保存到磁盘时的反序列化过程中感知到。

但是,由于它们本质上是作为 YAML 文件实现的,因此这两种操作的成本基本上与使用常规 JSON 文件执行该操作的成本相同。从某种意义上说,使用可脚本化对象的性能将与大多数其他数据驱动开发策略一样重。

值得一提的是,如果您一直将数据保存到可编写脚本的对象并希望立即将其保存到磁盘,则可能需要通过 call 调用 Unity AssetDatabase.SaveAssets API

结论

除此之外,还有大量的实验和设计空间。可编写脚本的对象可以是运行时集中的有用工具,它通过共享相同的可编写脚本的对象来帮助跟踪实体之间的信息,从而消除了对中间单例的需要。

在混合现实 (MR) 的背景下,开发了一个使用可编写脚本对象的 MR工具包,作为对序列、指令甚至训练工具的问卷进行编码的层。

增加游戏设计的自由度和多功能性,加快原型阶段。可能发生的最糟糕的事情是,在您的游戏稳定有趣之后,您可能需要重构系统的某些部分,这无疑比为无聊或无趣的游戏优化框架要好得多。

感谢阅读,如果您想要更多非正统的 Unity 策略来快速开发和原型设计,请告诉我。你可以在这里看到更多我的作品。 

来源:https ://blog.logrocket.com/fast-prototyping-unity-scriptable-objects/

#unity 

What is GEEK

Buddha Community

如何在 Unity 中使用可编写脚本的对象

Nora Joy

1607328200

Why unity 3D is best for game app development

We can see an exponential growth in the game development industry today and the market for game development will increase day by day ,thanks to the increasing number of smartphone users and the technological advancements.Unity 3D is the trending game app development framework to serve the best quality.This game development framework enables developers to conduct 2D or 3D rendering with more than 1 mobile game to assist them in ratcheting. Apart from this the great qualities like cross-platform integration with asset management, high-end visual quality, intuitive design, interface flexibility and gameplay can now be leveraged.India is the leading game development hub and now people are** hire dedicated unity 3D developers in India** to create a high performing game app with best quality at affordable price which you can spread your games to larger audience.Lets have a look at why unity a 3D is the best platform for game development.
**
Support cross-platform**

Cross platforms save time and money as a single script can be compiled and used for multiple platforms such as Android, iOS, PC, Web and even Mac etcFeatures such as agile methodology allow speedy prototyping and constant releases to speed up the process of game development.

Open source

The large open source community of Unity 3D with an easy-to-understand documentation allows developers to come up with the most accurate and precise code and it saves a lot of time.

Graphics

Unity 3D can support graphic rendering from engines that use OpenGL ES, OpenGL and Direct 3D, as well as applications like 3DS Max, Blender and Adobe Photoshop. It enables high-quality audio and visual effects to be adapted without any distortion or compromise with quality.
**
Play mode feature
**
This feature allows easy and hassle free testing by allowing developers to look and play within the game instantly, evaluate and even review it,and also the Play or Play Plus mode can also be used to achieve frame to frame referencing.

Debugging

With Unity game development, the analysis and modification is incredibly easier as all the game factors are seen during ongoing interaction, which helps the engineers to troubleshoot the process at runtime.

These advantages make unity as the best game development platform and people h**ire dedicated unity 3D developers** for the best output.With Unity, countless games have been made and some of them have become instant classics.Take a look at some of the all-time trending Unity games .

  • Kerbal Space Program

  • Firewatch

  • Subnautica

  • Hollow Knight

  • Arizona Sunshine

  • Cuphead

  • Ori And The Blind Forest

  • Hearthstone

  • Beat Saber

  • Cities Skylines

  • Getting Over It With Bennett Foddy
    In terms of graphics, gameplay, consistency and realism, technical advances and rise of new technologies like AR & VR and AI & ML make the game more ambitious day by day.Today the entire global game development is booming and mobile gaming business are hire unity 3D developers in India to meet this heavy market.**Hire dedicated unity 3D developers **will benefits the following,

  • International standard game app development at lower cost.

  • Skilled and experienced game developers

  • Faster time to market

  • Best infrastructure

Conclusion

Unity 3D has taken over the business and has altered the advancement of cross-platform app development paths. Unity 3D has already become the favourite of developers as they can import games created from iOS, PC, Play Store or other game consoles from other platforms and allow minimum game modifications to take full advantage of Unity 3D’s features. So if you have any game development hire unity 3D developers with great experience.

#hire unity 3 d developers in india #hire dedicated unity 3 d developers in india #hire unity 3 d programmers in india #hire unity 3 d developers #hire dedicated unity 3 d developers #hire unity 3 d programmers

The  NineHertz

The NineHertz

1612786985

Unity Game Development Company | Hire Unity Developers

Hire unity app developers of The NineHertz at nominal cost. We provide all the possible support and ease to make the client satisfied. From our in depth analysis, we are always able to deliver quality work at time. Not only the quality but we make sure the outcome or the gaming solution as per the guidelines of the client and has functionality which meets the needs of the market. The game software built by our experts is seamless and robust which increases the user popularity graph.

#unity game development #unity mobile app development #unity development services #unity game development company

The  NineHertz

The NineHertz

1613126944

Unity Game Development Company | Hire Unity Developers

Hire Unity developers who are highly experienced to develop fantastic games and impress the user with their outstanding graphics. With the great and innovative imagination, we create rich gaming applications for all types of categories. From 2D to 3D games, our team has numerous game developers who provide error-free and unbeatable game applications for our clients.

#unity game development #unity mobile app development #unity development services #unity game development company

顾 静

顾 静

1656763980

如何在 Unity 中使用可编写脚本的对象

假设您正在 Unity 中为您的游戏开发一个商家系统,商家在其中销售具有不同名称、价格、描述和图像的商品列表。

您团队中的游戏设计师已经编写了一份 100 页的文档,内容涉及各种项目,其中有一整章专门介绍了多种药水类型。作为首席程序员,您需要决定将这些项目中的每一项存储在系统中的哪个位置,那么您会在哪里存储呢?

在本文中,我们将探索 Unity 中的快速原型设计,以及如何在游戏开发中有效地使用可编写脚本的对象。

可序列化的结构

我相信解决这个系统的最初尝试是使用封装项目属性的可序列化结构。我们称它为 ItemStruct。然后,您将在商家中添加一个 ItemStruct 列表。

因此,通过检查器,游戏设计师可以手动输入这些项目中的每一项,同时让您为游戏的其他系统编程。这种系统的代码和检查器如下所示:

[Serializable]
public struct ItemStruct
{
   public string name;
   public int price;
   [TextArea]
   public string description;
   public Sprite image;
}

项目结构检查器示例

这种方法实际上非常通用,对于许多简单的系统来说,它无疑是最简单、更稳定的方法。

可序列化结构提供了一种通过检查器进行更改的简单方法,同时还利用了在 C# 中作为结构的优点,例如与类相比更适合较小的数据聚合,以及具有值语义。

但是,您的商家系统允许游戏世界中的不同商家。

其中一个,在起始区,应该是卖低级药水和弱临时增益的。为了支持速度较慢的玩家,游戏中更高级的区域仍然有商人出售价格低于更高级别替代品的起始药水。

 

ItemStruct 方法的问题

如果我们坚持使用 ItemStruct 方法,现在可能是商家的不同实例现在拥有相同 ItemStructs 的副本。

如果你已经挑了眉毛,那就准备挑另一只眉毛,因为你的游戏设计师很有可能会在某个时候尝试平衡游戏并对所有低级药水打折。由于 ItemStructs 没有相互连接,因此每个销售它们的商家的每个实例都需要更新。如果您已经在使用预制件和预制件变体,请向我鼓掌,但它们仍然需要更新。

购买是对这些物品进行编程的困难之一,但考虑到游戏也有战斗。击败敌人会获得战利品,例如药水。

应该清楚的是,我们正在寻找的解决方案围绕着将 ItemStruct 从商家(和其他机制)中分离出来,并让他们每个人都使用对它的引用。

因此,对 ItemStruct 所做的任何更改都会立即影响引用它的人。很高兴,Unity 提供了一种简单的方法:可编写脚本的对象。

使用可编写脚本的对象

可编写脚本的对象是 Unity 中的数据保存对象,不需要附加到游戏对象。

因此,我们可以在不将它们添加到场景中的对象或预制件的情况下创建它们。它们独立存在,任何使用它们的实体都使用引用。

可编写脚本的对象不继承 Unity 脚本的默认类 MonoBehavior,但它们确实具有一些类似的行为,并且可以以类似的方式使用。

例如,他们有自己的Awake call以及 OnEnable 和 OnDisable。他们可以拥有自己的方法和成员变量,也可以从使用属性中受益。

此外,它们与常规单声道行为脚本一样容易创建:

[CreateAssetMenu(fileName = "New Item", menuName = "Item", order = 0)]
public class ItemScriptableObject : ScriptableObject
{
   public string name;
   public int price;
   [TextArea]
   public string description;
   public Sprite image;
}

项目脚本对象

正如您在上面的示例中看到的那样,为了提高创建可脚本化对象实例的效率,我们可以使用CreateAssetMenuAttribute添加生成该类型文件的编辑器菜单。

从游戏对象中分离数据

回到我们之前讨论的将 ItemStructs 从游戏中的元素中分离出来的问题,现在一个简单的解决方案是将 ItemStructs 替换为 ItemScriptableObject。每个商家都持有一份其销售的 ItemScriptableObjects 列表。

物品价格的任何折扣或上涨现在都对物品本身进行。更改会立即反映在引用它的任何实体中。现在让游戏设计师为平衡游戏经济而欢欣鼓舞。

此外,其他机制,例如从怪物身上掉落物品或掠夺宝箱,也同样受益。您甚至可以更进一步,使用相同的可编写脚本的对象来设计库存系统或制作系统。

我们作为示例设计的可脚本化对象可以轻松扩展以包含其他数据,例如它在 UI 上显示的图像或制作类型。

在这方面,我们可以对可编写脚本的对象进行两个令人兴奋的扩展,值得一提:Inheritance 和 CustomEditorScripts。

扩展可编写脚本的对象

让我们从继承开始。可编写脚本的对象继承自Object 基类;因此,它们可以在任何可以使用常规 C# 类的上下文中使用。

这意味着我们可以拥有抽象的可编写脚本的对象并使用它们来建模行为,从而受益于所有那些良好的面向对象编程的优点,例如数据抽象和多态性。

例如,我们可以为游戏中的项目创建一个抽象的可编写脚本的对象,其中包含名称、成本和显示图像等标准信息。它还将包含一个抽象方法Use( )。然后每个项目类型可以指定它在使用时的作用。

然后,我们可以为 Potions 创建一个具体的可编写脚本的对象,它扩展了 Item。Potions 可脚本化对象从其父类继承成员变量并强制实现该Use方法。

从这里我们甚至可以通过创建 HealthPotion 扩展或 RejuvenationPotion 来进一步分支层次结构,以便该Use方法具有更精确的实现,并且我们添加更适合每种类型需求的成员变量。

您现在可能已经停下来想知道为什么我们以前不能这样做,因为这是标准的面向对象编程实践。

事实是我们总是可以,这就是使用可脚本化对象的美妙之处:我们不需要改变我们对系统建模的方式。现在的优势是我们可以为与系统本身分离的数据创建实例。

此外,正如我们所指出的,即使我们的游戏设计师现在正在为 LegendarySwordScriptableObject 类型添加数据,我们也可以将我们的系统实现为基于 Items。

public abstract class ItemScriptableObject : ScriptableObject
{
   public string name;
   public int price;
   [TextArea]
   public string description;
   public Sprite image;

   public abstract void Use();
}[CreateAssetMenu(fileName = "New Potion", menuName = "Items/Potion", order = 0)]
public class PotionScriptableObject : ItemScriptableObject
{
   public override void Use()
   {
       //Do Something
   }
}

使用自定义编辑器脚本

我之前提出的另一个方面是使用自定义编辑器脚本授权可编写脚本的对象。自定义编辑器脚本允许我们更改和添加特定实体在 Unity Inspector 中的显示方式。

它们通常在常规 MonoBehavior 脚本中用于添加按钮或显示标签,并且通常用于加快开发速度。

ScriptableObjects 也可以使用他们自己的自定义编辑器脚本进行扩展,这使得它们直接在检查器中成为强大的编辑工具,并且再次与其他实体分离。

例如,我们建议我们的 Item 脚本对象可以包含它的显示图像。使用自定义编辑器脚本,我们可以直接在检查器中显示选定的图像,以方便检查是否使用了正确的精灵。

特别是对于药水,我们可以添加一个按钮,打印给定属性的药水使用结果,以快速评估其结果是否合适。

[CustomEditor(typeof(PotionScriptableObject))]
public class PotionScriptableObjectEditor : UnityEditor.Editor
{
   public override void OnInspectorGUI()
   {
       base.OnInspectorGUI();

       var potionScriptableObject = (PotionScriptableObject) target;
       if (potionScriptableObject.image == null) return;
       GUILayout.Box(potionScriptableObject.image.texture);

       if (GUILayout.Button("Test Use"))
       {
           potionScriptableObject.Use();
       }
   }
}

Potion Scriptable 对象示例
(注:此处使用的精灵最初来自Brackeys的2D Mega Pack )

可编写脚本的对象的其他优点

与常规的 MonoBehavior 脚本相比,无论是否使用自定义编辑器脚本来调整可编写脚本对象的值都有另一个优势:它们在编辑器和播放模式之间是持久的。

这意味着,如果您在游戏模式下测试游戏时更改了可编写脚本的对象,则更改将保存到可编写脚本的对象并在编辑器模式下持续存在。

请注意,构建游戏实例之间的持久性并非如此,也就是说,您不应该使用可编写脚本的对象作为保存系统的替代品。

此外,对可编写脚本的对象所做的更改也会在场景之间持续存在。在场景和游戏状态之间传输信息时,这一优势可以更好地替代单例。

例如,您可以将玩家的信息(例如健康和经验)存储在可编写脚本的对象中,并让游戏中的多个系统引用它。

如果您的角色在战斗场景中失去生命值,然后返回世界地图,则对生命值的更改将保留在可编写脚本的对象中,并且不必通过其他机制共享,例如标记为在加载时不会被销毁PlayerManager新场景。

然而,可编写脚本的对象还有另一种用途,这听起来很违反直觉:无数据可编写脚本的对象。

转换可编写脚本的对象

默认情况下,可编写脚本的对象实例是唯一的,因为它们中的每一个都将拥有一个 GUID。

因此,对可编写脚本对象的实例的多个引用必然会使用正确的引用,尤其是因为它们很可能在检查器中链接到其相应的引用持有者。

另一方面,我们也可以从这个属性中受益,通过它们的内容(例如,重写 Equals 方法)或它们的标识符来比较可编写脚本的对象。

这也使它们成为字典和其他基于键的数据结构的优秀键。

可编写脚本的对象替代品

正如我之前所暗示的,我们可以仅将可脚本化对象用作键,但要保留它们的所有优点。例如,考虑到您游戏中的全球市场系统,您想要指定一个字典,将商家链接到他们对应的城市。

每个商家只能链接到一个城市,城市可以容纳多个商家。你会用什么来建立商人和城市之间的联系?一个字符串?一个枚举?

您可能已经猜到,例如,可编写脚本的对象 CityScriptableObject 可以轻松解决这个难题。

让我们先讨论替代方案。

细绳

字符串必然会出现拼写错误和其他错误,而且维护和更新效率非常低(例如,由于游戏编写者之间的争论,一个城市在开发过程中更改了名称)。比较字符串也不是很有效。

枚举

枚举工作得很好,但是每次新增都需要更改代码库。此外,如果您删除枚举中的一个条目并且必须修复引用它的所有代码(由于游戏作者之间的另一个争论——那些人!),现在一座城市被摧毁了,这可能是灾难性的。

为什么可编写脚本的对象是有益的

另一方面,可编写脚本的对象实例可以在项目视图中创建(无需代码),并且可以在代码中作为常规对象引用,而不是依赖于 switch 语句和其他控制流结构来指定可能的条目。

如前所述,只需添加新的字段和方法,就可以进一步扩展同一个可编写脚本的对象。如果我们需要一个键来引用一个城市,我们的脚本对象可以是空的,并使用资产的文件名本身作为键(无论如何它都有一个 GUID)。

但是,如果稍后我们希望商家在用户界面中使用其城市名称,我们可以轻松地将其作为属性添加到 CityScriptableObject 代码中,并且对代码的任何其他部分没有附带影响。

如果您现在正在考虑这似乎是对可脚本化对象的非正统用法,请记住,这种做法最初是由 Unity Technologies 本身的员工 Richard Fine 提出的,并且已经得到社区中许多其他开发人员的验证。

实现一个使用可脚本化对象而不是枚举的系统与实现一个使用自制密钥系统的系统没有太大区别。

实体将持有对可编写脚本的对象的引用,系统将使用这些可编写脚本的对象进行比较,甚至使用它们自己的成员方法。还记得我们的Use方法吗?下面的代码摘录应该可以帮助您设想一种可能的方法来实现此策略。

[UnityEngine.CreateAssetMenu(fileName = "New Magic Element", menuName = "Magic/Magic Element", order = 0)]

public class MagicElementScriptableObject : UnityEngine.ScriptableObject
{
//Nothing 
}public class MagicPotionScriptableObject : PotionScriptableObject
{
   public MagicElementScriptableObject magicElement;
   public int increaseBy;
  
   public override void Use()
   {   
character.IncreaseMagicElementPower(magicElement, increaseBy);
   }public class Character
{
   public Dictionary<MagicElementScriptableObject, int> magicElements;
  
   public void IncreaseMagicElementPower (MagicElementScriptableObject magicElement, int value)
   {
magicElements[magicElement] += value;
   }
}

判断可编写脚本的对象

凭借其极大的灵活性和简单性,可编写脚本的对象为系统、持久性、数据存储甚至作为单例和枚举的替代品提供了许多新方法。

如前所述,由于它们的继承性,它们的行为与我们已经习惯的许多其他组件相似,这使得开始使用它们的过程更加平易近人。

当然,所有好事都是有代价的——前面讨论的一些方法也可以用不同的技术来实现,这些技术可能会为代码库实现更高水平的性能或稳定性。

灵活性

使用可编写脚本的对象来替换枚举的灵活性可能过于非正统,并且在您的游戏已经很好建立之后产生的收益递减。然后是时候执行反向替换并获取枚举的优势了。

请注意,这可能只有在原型设计阶段完成了长时间的头脑风暴和游戏测试之后才会发生。

表现

在性能方面,可编写脚本的对象的行为就像 C# 对象。它们的性能影响可以在启动游戏时的序列化过程中以及在将它们保存到磁盘时的反序列化过程中感知到。

但是,由于它们本质上是作为 YAML 文件实现的,因此这两种操作的成本基本上与使用常规 JSON 文件执行该操作的成本相同。从某种意义上说,使用可脚本化对象的性能将与大多数其他数据驱动开发策略一样重。

值得一提的是,如果您一直将数据保存到可编写脚本的对象并希望立即将其保存到磁盘,则可能需要通过 call 调用 Unity AssetDatabase.SaveAssets API

结论

除此之外,还有大量的实验和设计空间。可编写脚本的对象可以是运行时集中的有用工具,它通过共享相同的可编写脚本的对象来帮助跟踪实体之间的信息,从而消除了对中间单例的需要。

在混合现实 (MR) 的背景下,开发了一个使用可编写脚本对象的 MR工具包,作为对序列、指令甚至训练工具的问卷进行编码的层。

增加游戏设计的自由度和多功能性,加快原型阶段。可能发生的最糟糕的事情是,在您的游戏稳定有趣之后,您可能需要重构系统的某些部分,这无疑比为无聊或无趣的游戏优化框架要好得多。

感谢阅读,如果您想要更多非正统的 Unity 策略来快速开发和原型设计,请告诉我。你可以在这里看到更多我的作品。 

来源:https ://blog.logrocket.com/fast-prototyping-unity-scriptable-objects/

#unity 

顾 静

顾 静

1661472000

如何在 Unity 中使用流媒体资源

资产可以是 Unity 中的任何资源;这些资源可以是图像、音频、视频、脚本、文本等。

在任何平台上构建 Unity 项目时,游戏中的所有资源都将“打包”在一个文件中(或更多取决于您的平台),构建的结果大小将取决于您决定的资源大小打包在您的游戏中。

创建具有多个高清纹理的游戏而不是使用地图集,或者使用 240p 视频而不是使用 1080p 是不一样的。预计资产规模越大,最终构建的规模就越大。

一般来说,保持构建尽可能轻量级是一个很好的做法,这样玩家就不会因为大小而放弃下载游戏,而且场景运行速度也会大大加快。

实现这一点有多种提示和技巧,在本文中,我们将讨论其中的一些,从流媒体资产开始。

什么是流媒体资产?

假设您的游戏中有一个不错的场景,所有动作都在其中发生。玩家是敌人的基地,在一个特定的房间里,你决定把一段视频作为彩蛋放在电脑上播放。

视频真的很大,超过 300MB,播放器可能会也可能不会进入那个特定的房间才能真正看到它。默认情况下将其加载到您的场景中可以吗?这可能会导致您的精彩场景出现不必要的缓慢。所以当我们真正需要它时加载它可能会很棒。

流媒体资产就是这样:放置在特定文件夹中的资产,需要时由 Unity Player 加载。该资产将被放置在目标平台内易于找到的地址中。

请注意,放置在流资产文件夹(在 Unity 中名为 StreamingAssets)的任何资产都将被复制到目标平台,如果用户在项目文件夹中搜索,它将能够看到它们。

这就是一些游戏的一些模组的制作方式。

改变角色的纹理?进入数据文件夹并找到使用的纹理,然后对其进行修改。改变强硬老板的台词?只需转到数据文件夹并找到音轨并将其替换为另一个具有相同名称的音轨。

流媒体资产让游戏运行顺畅,无需从内存中加载所有资产,并以易于访问的路径提供更大的资产。

让我们构建一个流媒体资产的简单示例

我们将通过在您的游戏中显示视频,通过一个简单的示例来展示流媒体资产的工作原理。

从 iStockPhoto下载了此示例的视频预览。

确保您要使用的视频具有以下扩展名之一:.mov、.mpeg、.mp4、.avi、.asf。

对于我们的初始示例,我们将创建一个名为Video的新文件夹并将我们的视频放在该文件夹中:

文件夹内的视频

然后,在我们的Hierarchy面板上,我们将设置我们的视频播放器。

右键单击您的 Hierarchy 并添加一个Empty Game Object。我打电话给我的 VideoObject

在其中,我创建了一个立方体,我们将在其中播放视频;它可以是你想要的任何形状,但为了方便我选择了一个立方体。我称之为Video_Canvas

在Video_Canvas的同一级别,在VideoObject中,我通过右键单击VideoObject并选择Video,然后选择 Video Player添加了一个新的Video Player对象。

视频对象文件夹结构

以下是我设置视频播放器对象的方法:

视频播放器设置

我们将Source设置为Video Clip,然后从我们创建的Video Folder 中选择视频剪辑,然后选择Loop以便视频可以无限播放。

然后,选择Render Mode作为Material Override并拖放要在其上播放视频的对象——在我们的例子中是Video_Canvas立方体。让Material 属性_MainTex

如果您在 Unity 编辑器中单击播放,您将看到类似这样的内容:在立方体的每一侧播放一个视频。

每个立方体侧面的视频

如果您想要一个更平坦的表面(如屏幕),您还可以创建一个平面或将立方体的比例缩小到您想要的形状。

该演示视频为 3.5MB。

如果我们为独立平台(Windows、Linux、MacOs)构建这个项目,我们将看到一个带有视频的空白项目,大小为 87.6MB。

黑色项目视频

现在,让我们看看使用相同的视频作为流媒体资产的效果。

为此,我们将在Assets文件夹中创建一个名为StreamingAssets的新文件夹;这是一个特殊命名的文件夹,它将我们的视频文件video_streamingAssets视为常规文件,在编辑器的属性窗口中没有任何选项。

流资产文件夹

然后在我们的Video Player对象中,我们可以将Video Player 游戏组件的Source更改为URL并保留实际的 URL。我们将创建一个名为LoadVideo的新脚本,并将其附加到我们的视频播放器游戏对象,就像在图像中一样。

该脚本将视频播放器作为参数,因此我们将拖放与该脚本连接的相同视频播放器。

视频播放器详细信息

这是LoadVideo 的脚本:

using UnityEngine;
using UnityEngine.Video;

public class LoadVideo : MonoBehaviour
{
   public VideoPlayer myVideoPlayer;
   void Start()
   {
       string videoUrl= Application.streamingAssetsPath +"/"+ "video_streamingAssets" + ".mp4";
       myVideoPlayer.url = videoUrl;
   }
}

基本上,在应用程序启动时,它将使用Application.streamingAssetsPath来获取已内置的任何目标平台中StreamingAssets文件夹的路径。然后它将引用该文件夹中我们视频的名称及其扩展名。

然后,它将采用myVideoPlayer(已经有我们场景视频播放器的引用),并将其url属性写入我们视频的路径。

这将产生与播放视频相同的立方体。

每个立方体侧面的视频

现在,如果我们构建这个项目,我们将看到它的大小与前面的示例相同,但有一个主要区别。

一个差异的空白项目

如果我们看到这个构建的包内容,我们可以看到在Contents/Resources/Data上有我们的StreamingAssets文件夹和我们的视频。

带有视频的 StreamingAssets 文件夹

如果我们将StreamingAssets文件夹中的视频替换为另一个同名的视频,则无需再次构建项目即可看到游戏中反映的更改。

有差异的立方体

现在我们知道了如何将我们的资产/内容从我们的构建中分离出来。我们还可以从视频播放器上的 URL 调用远程文件。为此,我们将使用此视频作为示例。它大约有 10 分钟长,大约 151MB。

如果我们将代码更改为:

using UnityEngine;
using UnityEngine.Video;

public class LoadVideo : MonoBehaviour
{
   public VideoPlayer myVideoPlayer;
   void Start()
   {
       string videoUrl= "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
       myVideoPlayer.url = videoUrl;
   }
}

…并删除StreamingAssets文件夹的原始视频,我们最终得到如下内容:

新视频立方体

相同的功能,但现在视频资产来自外部来源

现在,如果我们在StreamingAssets文件夹中构建没有本地视频的项目,我们会看到构建的大小有所减少。

流媒体资产减小了大小

请记住,这也可以通过异步调用或UnityWebRequest调用和协程来完成,但为简单起见,我们只添加视频的确切 URL 以展示流媒体资产的强大功能。

使用协程从外部源获取视频

我们可以LoadingScript像这样修改我们的代码,以便使用协程而不是将直接链接传递给我们的视频播放器:

using UnityEngine;
using UnityEngine.Video;
using UnityEngine.Networking;
using System.Collections;

public class LoadVideo : MonoBehaviour
{
   public VideoPlayer myVideoPlayer;
   void Start()
   {
       StartCoroutine(LoadExternalVideo("http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4"));
   }

     IEnumerator LoadExternalVideo(string url){
      using (UnityWebRequest request = UnityWebRequest.Get(url))
      {
          //Fetches a page and displays the number of characters of the response.
          yield return request.SendWebRequest();
          myVideoPlayer.url = request.url;
      }
  }
}

在此示例中,我们可以使用协程来使用 API 端点并检索视频的 URL,但为简单起见,这是一个关于如何使用外部链接进行操作的简单示例。

结果将非常相似;我们只需要确保我们的UnityWebRequest结果是从消费端点检索到的,并确保在将视频的 URL 设置为视频播放器的 URL 属性之前存在。

最后的想法

流媒体资源只是 Unity 为您的游戏处理资源的不同方式之一。当您通过允许他们修改您的游戏来尝试让您的玩家群参与进来​​时,或者如果您想拥有游戏的轻量级最终构建,然后在首次启动时,下载游戏的其余资产,它非常有用在玩家注意到之前。

有很多网络游戏都是这样做的:他们的游戏很轻,你可以完成第一关的小教程,然后你会得到一个加载屏幕,下载其余的资产。

您必须牢记最适合您的游戏的方法。您不能将所有资产都放在远程服务器上,因为如果玩家第一次玩您的游戏时,他们的设备没有良好的互联网连接会发生什么?如果没有资产,你的游戏会是什么样子?

最好的方法之一是默认使用非常轻量级的资源版本(如材质、纹理、图像)作为占位符,并且当您确定播放器会喜欢该内容并且设备已准备好下载该信息时,尽可能无缝地这样做。

我希望你喜欢这篇文章并觉得它很有用!快乐的游戏开发!

来源:https ://blog.logrocket.com/how-to-use-streaming-assets-unity/

#unity