Unity:需要将 return 上的池对象重置为池。也许使用 ScriptableObject?
Unity: Need to reset a pooled object on return to pool. Perhaps using ScriptableObject?
我最近一直在尝试在 unity 中使用对象池来加速多个游戏对象的实例化。
但是,由于这些对象相当复杂,我需要在它们返回池中时重置它们。
我读到使用 ScriptableObject 可能是存储默认值以便轻松重置的好方法。但是为了做到这一点,我需要在运行时加载一个新的 ScriptableObject 来存储对象的实际值。
所以在伪代码中,我有一个 class 和 public MyScriptableData data
和 public MyScriptableData defaults
1) 使用默认的 ScriptableObject 创建新的池化对象 data = defaults;
;
2) 在对象的生命周期内做一些改变脚本化对象值的事情
3) 停用,然后 return 池对象到池,将 scriptableObject 重置为其默认值(再次 data = defaults;
)。
我有 3 个主要问题:
A) 我不确定如何实际实现它。在我看来,在第 2 步中,默认值将被更改。因此,重置为默认值将无济于事。我考虑过使用
创建我的可编写脚本对象的新实例
data = ScriptableObject.CreateInstance<MyScriptableData>();
但是我如何从 defaults
中复制默认值,确保从不更改默认值?我希望默认值可以在统一编辑器中作为资产进行编辑。
B) 如果我使用CreateInstance,性能会不会很差?我做这个对象池的全部原因是为了降低对象实例化的性能成本。我不想通过实例化可编写脚本的对象来重新引入缓慢的代码。
C) 这个方法可以吗?还是有更好的方法在返回池之前重置对象?
根据一些答案进行编辑:我已经有一个包含一长串字段的设置,然后将这些字段的默认值存储在字典中。但是我发现每次我想要 add/change/remove 一个字段时,我都必须在几个地方更改代码
尝试的解决方案(但错误,见下文):我为 ScriptableObject 创建了一个扩展方法:
using UnityEngine;
using System.Reflection;
public static class ScriptableObjectExtension {
public static T ShallowCopy<T> (this T orig) where T : ScriptableObject {
T copiedObject = ScriptableObject.CreateInstance<T> ();
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 时,我没有注意到性能有明显变化。感谢大家的回复!
创建对象时如何,在Awake()
或Start()
中将您想要的默认值保存到一堆变量中或将其存储在字典中,
之后要重置该值,只需创建一个名为 Reset()
的方法,然后为所有变量分配您之前存储的默认值。
例如
// method 1
Dictionary<string, object> defaultValues = new Dictionary<string, object>();
int speed = 10;
List<float> scores = new List<float>() {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<float>(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<float>) defaultValues["scores"];
something = defaultSomething.Clone();
sth = defaultSth;
}
缺点是每个实例都会存储自己的默认变量占用内存,以后可以改成static或者const
另一种方法是创建一个仅用于存储默认值的实例(不要在运行时修改它)并使用 C# 反射复制所有成员值
C# Using Reflection to copy base class properties
希望对您有所帮助
如果您只需要在对象停用时重置对象的值,难道您不能简单地使用:
OnEnable()
{
default = data;
}
OnDisable()
{
data = default;
}
这将允许您在它激活时存储/分配默认数据,并在它停用时将其数据重置回默认值。
注意!!
从大约 2014 年开始,您通常不需要在 Unity 中池化。 Unity 显着提高了性能,因此对于典型的游戏场景,没有必要。
请注意,OP 只是通过取消他们的手拼尝试来解决问题。
近年来,Unity 极大地改进了他们的
垃圾收集
内存处理
池式处理触发启发式
预制和实例化过程
视频游戏中的对象创建在现代硬件上完全微不足道; 像“子弹”这样的典型场景可能在一秒钟内只有十几个项目;并且你在未来的安静帧中有大量的奢侈时间来做 gc 等。在 Unity 的早期,你必须手动编写池来实现典型的游戏多对象需求,例如子弹。由于 Unity 的努力,现在对于典型的游戏场景(子弹、多个 NPC 等)来说完全没有必要。
如果出于某种原因,您可以合并,但就性能而言,这完全没有必要,对于典型的视频游戏需求。 2D 或 3D。
我最近一直在尝试在 unity 中使用对象池来加速多个游戏对象的实例化。
但是,由于这些对象相当复杂,我需要在它们返回池中时重置它们。
我读到使用 ScriptableObject 可能是存储默认值以便轻松重置的好方法。但是为了做到这一点,我需要在运行时加载一个新的 ScriptableObject 来存储对象的实际值。
所以在伪代码中,我有一个 class 和 public MyScriptableData data
和 public MyScriptableData defaults
1) 使用默认的 ScriptableObject 创建新的池化对象 data = defaults;
;
2) 在对象的生命周期内做一些改变脚本化对象值的事情
3) 停用,然后 return 池对象到池,将 scriptableObject 重置为其默认值(再次 data = defaults;
)。
我有 3 个主要问题:
A) 我不确定如何实际实现它。在我看来,在第 2 步中,默认值将被更改。因此,重置为默认值将无济于事。我考虑过使用
创建我的可编写脚本对象的新实例data = ScriptableObject.CreateInstance<MyScriptableData>();
但是我如何从 defaults
中复制默认值,确保从不更改默认值?我希望默认值可以在统一编辑器中作为资产进行编辑。
B) 如果我使用CreateInstance,性能会不会很差?我做这个对象池的全部原因是为了降低对象实例化的性能成本。我不想通过实例化可编写脚本的对象来重新引入缓慢的代码。
C) 这个方法可以吗?还是有更好的方法在返回池之前重置对象?
根据一些答案进行编辑:我已经有一个包含一长串字段的设置,然后将这些字段的默认值存储在字典中。但是我发现每次我想要 add/change/remove 一个字段时,我都必须在几个地方更改代码
尝试的解决方案(但错误,见下文):我为 ScriptableObject 创建了一个扩展方法:
using UnityEngine;
using System.Reflection;
public static class ScriptableObjectExtension {
public static T ShallowCopy<T> (this T orig) where T : ScriptableObject {
T copiedObject = ScriptableObject.CreateInstance<T> ();
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 时,我没有注意到性能有明显变化。感谢大家的回复!
创建对象时如何,在Awake()
或Start()
中将您想要的默认值保存到一堆变量中或将其存储在字典中,
之后要重置该值,只需创建一个名为 Reset()
的方法,然后为所有变量分配您之前存储的默认值。
例如
// method 1
Dictionary<string, object> defaultValues = new Dictionary<string, object>();
int speed = 10;
List<float> scores = new List<float>() {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<float>(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<float>) defaultValues["scores"];
something = defaultSomething.Clone();
sth = defaultSth;
}
缺点是每个实例都会存储自己的默认变量占用内存,以后可以改成static或者const
另一种方法是创建一个仅用于存储默认值的实例(不要在运行时修改它)并使用 C# 反射复制所有成员值
C# Using Reflection to copy base class properties
希望对您有所帮助
如果您只需要在对象停用时重置对象的值,难道您不能简单地使用:
OnEnable()
{
default = data;
}
OnDisable()
{
data = default;
}
这将允许您在它激活时存储/分配默认数据,并在它停用时将其数据重置回默认值。
注意!!
从大约 2014 年开始,您通常不需要在 Unity 中池化。 Unity 显着提高了性能,因此对于典型的游戏场景,没有必要。
请注意,OP 只是通过取消他们的手拼尝试来解决问题。
近年来,Unity 极大地改进了他们的
垃圾收集
内存处理
池式处理触发启发式
预制和实例化过程
视频游戏中的对象创建在现代硬件上完全微不足道; 像“子弹”这样的典型场景可能在一秒钟内只有十几个项目;并且你在未来的安静帧中有大量的奢侈时间来做 gc 等。在 Unity 的早期,你必须手动编写池来实现典型的游戏多对象需求,例如子弹。由于 Unity 的努力,现在对于典型的游戏场景(子弹、多个 NPC 等)来说完全没有必要。
如果出于某种原因,您可以合并,但就性能而言,这完全没有必要,对于典型的视频游戏需求。 2D 或 3D。