Unity:需要在返回池时重置池化对象。 也许使用ScriptableObject?

我最近一直在尝试统一对象池,以便一次加速几个游戏对象的实例化。

但是,由于这些是相当复杂的对象,我需要在它们返回池中时重置它们。

我读到使用ScriptableObject可能是存储默认值以便轻松重置的好方法。 但为了做到这一点,我需要在运行时加载一个新的ScriptableObject来存储对象的实际值。

所以在伪代码中,我有一个带有public MyScriptableData datapublic MyScriptableData defaults

1)使用默认的ScriptableObject data = defaults;创建新的池化对象data = defaults; ;

2)在对象的生命周期内做更改脚本对象值的东西

3)取消激活,然后将池化对象返回池,将scriptableObject重置为其默认值( data = defaults;再次)。

我有3个主要问题:

A)我不确定如何实际实现这一点。 在我看来,在步骤2中,默认值将被更改。 因此,重置为默认值将不起作用。 我考虑使用创建我的脚本对象的新实例

data = ScriptableObject.CreateInstance();

但是,我将如何从默认值中复制默认值,确保永远不会更改默认值? 我希望默认值可以在Unity编辑器中作为资产进行编辑。

B)如果我使用CreateInstance,性能会不好? 我正在做这个对象池的全部原因是为了降低对象实例化的性能成本。 我不想通过实例化脚本化对象来重新引入慢速代码。

C)这种方法好吗? 还是有更好的方法在返回池之前重置对象?

编辑基于一些答案:我已经有一个具有很长字段列表的设置,然后将这些字段的默认值存储在字典中。 但我发现每次我想添加/更改/删除字段时,我都必须在几个位置更改代码

ATTEMPTED SOLUTION(但错误,见下文):我为ScriptableObject创建了一个扩展方法:

 using UnityEngine; using System.Reflection; public static class ScriptableObjectExtension { public static T ShallowCopy (this T orig) where T : ScriptableObject { T copiedObject = ScriptableObject.CreateInstance (); FieldInfo[] myObjectFields = orig.GetType ().GetFields ( BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); foreach (FieldInfo fi in myObjectFields) { fi.SetValue (copiedObject, fi.GetValue (orig)); } return copiedObject; } } 

最终解决方案:

上面的脚本用于克隆可编写脚本的对象,但是,看起来我正在使用该解决方案走错路。

下面的几个人指出,对于大多数应用程序来说,池化并不重要。 我最初试图将游泳池放入,因为我根据剖面仪的帧速率大约是30-15 fps,我认为合并将有助于改善这一点。

根据评论我深入挖掘并发现有一个名为LogStringToConsole的进程。 我对自己说,这可能就像我的Debug.Log语句一样简单! 我删除了它们,巨大的尖峰消失了。 显然Debug.Log会导致巨大的性能问题。 现在我已经超过了60fps。 因此,我决定不对这些对象进行池化(但是我仍然在另一个场景中使用更简单的对象进行池化,这些场景每秒会产生几次)。 这意味着我根本不需要担心脚本化对象。 我现在已经并实例化了加载预制件和Init方法进行设置,并且对象在用完时会被销毁。

当我回到使用instantiate / destroy时,我没有注意到性能的重大变化。 感谢所有的回复!

注意!!

从大约2014年开始,您通常不会在Unity中使用。

通过这个阅读任何人:注意OP只是通过删除他的池化尝试解决了他的问题。

许多业余爱好者都没有学到这些新信息,这引起了相当大的困惑。

Unity完全重新设计了实例化和游戏对象,因此在典型的游戏项目中很少需要池化。

现在你非常非常非常需要在Unity中游泳。

在正常的爱好者编程中,你需要使用典型的子弹,弹丸等。

你可以说:在Unity的早期,你经常不得不集中这一事实,这对Unity来说是一个搞砸的事情。 他们完全解决了这个问题: Instantiate调用现在基本上“立即”工作 – 正如你所期望的那样,它是多么微不足道。

(这是video游戏工程如何在非常短的时间内 发生巨大变化的一个很好的例子。)

请注意,在互联网上找到可笑的过时示例代码是很常见的,特别是与Unity相关的代码。 到目前为止,我个人写了迄今为止最流行的关于“如何在Unity中汇集”的post,这些post几年前在古老的Unity答案网站上 – 现在完全没有意义。

(2)其次,Unity中的一个小的无关的gotchya。

在此处输入图像描述

带有Unity的gotchya是Debug.Log在某些情况下所有事情都会导致荒谬的性能问题,这可能会让项目混淆几天!

(在Unity中有很多有趣的小问题,这里有两个经典… https://stackoverflow.com/a/34713627/294884 https://stackoverflow.com/a/35166004/294884

如果您只需要在停用时重置对象的值,则不能简单地使用:

 OnEnable() { default = data; } OnDisable() { data = default; } 

这将允许您在激活时存储/分配默认数据,并在停用时将其数据重置为默认值。

如何创建对象,在Awake()Start()保存您想要拥有的一组变量的默认值或将其存储在字典中,

之后重置值只需使用名为Reset()的方法创建一个方法,并使用之前存储的默认值分配所有变量。

例如

 // method 1 Dictionary defaultValues = new Dictionary(); int speed = 10; List scores = new List() {1.5f, 3.4f}; // method 2 SomeClass something = new SomeClass(); SomeClass defaultSomething = new SomeClass(); // and if the type can use const string sth = "abc"; const string defaultSth = "abc"; void Awake() { defaultValues.Add("speed", speed); defaultValues.Add("scores", new List(scores)); // new list because it is reference type, //and you dont want to store reference to the list because it will be edited during runtime defaultSomething = something.Clone(); // edit, but you need to implement clone by yourself for that class or do something that will make other instance of the class with same value } void Reset() { speed = (int) defaultValues["speed"]; scores = (List) defaultValues["scores"]; something = defaultSomething.Clone(); sth = defaultSth; } 

缺点是每个实例都会存储占用内存的自己的默认变量,如果以后需要,可以将它们更改为static或const

另一种方法是你创建一个用于存储默认值的实例(不要在运行时修改它)并使用C#reflection复制所有成员值

C#使用Reflection来复制基类属性

希望这可以帮助