I have recently been trying out object pooling in unity to speed up the instantiation of several game objects at once.
However, since these are fairly complex objects I need to reset them when they go back in the pool.
I read that using ScriptableObject might be a good way to store the default values for an easy reset. But in order to do that I need to load a fresh ScriptableObject at runtime to store the actual values of the object.
So in pseudocode, i'd have a class with public MyScriptableData data
and public MyScriptableData defaults
1) create new pooled object with default ScriptableObject data = defaults;
;
2) do stuff that changes values of scriptable object during lifetime of object
3) deactivate, and then return pooled object to pool, resetting the scriptableObject to its default (data = defaults;
again).
I have 3 main questions:
A) I'm not sure how to actually implement this. It seems to me, in step 2, the default values would be changed. Therefore resetting to defaults would do nothing. I thought about creating a new instance of my scriptable object using
data = ScriptableObject.CreateInstance<MyScriptableData>();
but then how would I copy in the default values from defaults
, ensuring in never changing the defaults? I would like for the defaults to be editable in the unity editor as an asset.
B) If I use CreateInstance, will the performance be bad? The whole reason i'm doing this object pooling is to reduce the performance costs of object instantiation. I'd hate to reintroduce slow code by instantiating scriptable objects.
C) Is this approach alright? or is there a better way to reset objects before going back into the pool?
EDIT based on some answers: I already have a setup that has a long list of fields, and then stores the default values of theses fields in a dictionary. But I found every time I wanted to add/change/remove a field, I had to change code in several spots
ATTEMPTED SOLUTION (But wrong, see below): I created an extension method for 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;
}
}
FINAL SOLUTION:
The above script worked to clone scriptable objects, however, it appears I was going down the wrong path with that solution.
Several people below pointed out that pooling isn't that important in unity for most applications. I had originally tried to put pooling in because my framerate according to the profiler was around 30-15 fps, and I thought pooling would help improve that.
Based on the comments I dug a bit deeper and found that there was a process called LogStringToConsole. I said to myself, could this be as simple as my Debug.Log statements slowing things down!? I deleted them and the huge spikes went away. Apparently Debug.Log causes huge performance problems. Now I'm well above 60fps. Because of this I have decided not to pool those objects (But I still use pooling on simpler objects in another scenario that are being spawned several times per second). This means I don't need to worry about scriptable objects here at all. I now have and instantiate that loads a prefab and an Init method to set things up, and the objects are destroyed when they are used up.
When I went back to using instantiate/destroy, I didn't notice a significant change in performance. Thanks for all the responses!
Note!!
From about 2014, as a rule you do not need to pool in Unity. Unity drastically improved performance so that, for typical game scenarios, it is not necessary.
Note that the OP eliminated the problem simply by removing their hand-pooling attempt.
In recent years Unity drastically improved their
garbage collection
memory handling
pool-like handling triggering heuristics
prefab and instantiation processes
Object creation in video games is completely trivial on modern hardware; typical scenarios like "bullets" only amount to a dozen or so items perhaps in a second; and you have a vast amount of luxury time in future quiet frames to do gc, etc. In the early days of Unity, you would have to write pooling by hand to achieve typical game multiple object requirements such as bullets. Thanks to Unity's efforts, this is now totally unnecessary for typical game scenarios (bullets, multiple NPCs etc).
You can pool if for some reason you want to, but it is totally unnecessary performance-wise, for typical video game needs. 2D or 3D.
If you only ever need to reset the object's values whenever it deactivates, couldn't you simply use:
OnEnable()
{
default = data;
}
OnDisable()
{
data = default;
}
That would allow you to store / assign default data when it activates, and reset its data back to the default values when it deactivates.
How about when you created the object, in the Awake()
or Start()
save the default value that you want to have into bunch of variables or store it inside dictionary,
after that to reset the value just make a method maybe with name called Reset()
and assign all the variables with the default values that you have stored before.
e.g.
// 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;
}
The downside is every instance will store their own default variables occupying memory, you could change them into static or const if you want later on
The other way is you make 1 instance which is used for just storing default value (dont modify this in runtime) and use C# reflection to copy all members value
C# Using Reflection to copy base class properties
Hope this helps
来源:https://stackoverflow.com/questions/37847848/unity-need-to-reset-a-pooled-object-on-return-to-pool-perhaps-using-scriptable