- Home /
Fastest way to get all components on GameObject?
I'm developing a serialization tool on top of Unity. Every time I create an object and want to deserialize data to it, I have to retrieve all components to provide them with sensible data. The soonest I can do this is on Awake and via GetComponents()
. However, this isn't as fast as I would like it to, when components per GO are high or there are too much GOs to deserialize.
Is there a faster way to get every component a GO have? I imagine you cannot cache them at editor (I presume the instances are generated once instantiated.. makes sense..), and I don't see any property on GameObject exposing the Component[] I need.
Note that I want to retrieve every component on my GameObject just after its instantiation (and wisely, in the same tick) – the objects won't be residing on the scene at build time, but will be instantiated at some point of the execution
Thanks
When you said
the objects won't be residing on the scene at build time, but will be instantiated at some point of the execution
Are you talking about full dynamic objects created by script (new GameObject("name"))? And not instantiations from prefabs (GameObject.Instantiate(prefab))?
Answer by whydoidoit · Oct 10, 2013 at 02:53 PM
Try this:
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class CacheComponents : MonoBehaviour
{
public List<Component> components;
public bool cacheNow;
void Awake()
{
if(!Application.isPlaying)
Cache();
}
void Update()
{
if(cacheNow)
{
cacheNow = false;
Cache();
}
}
void Cache()
{
components = new List<Component>();
foreach(var component in GetComponents<Component>())
{
if(component != this) components.Add(component);
}
}
}
You also be wise to cache the GetGet$$anonymous$$ethod and GetSet$$anonymous$$ethods of the properties or create delegates to read and write field values for each individual type and cache those so you only do it once. It would be very nice if you could do that in the editor, but I can't think of a way.
Note for best performance use two scripts - one to hold the list and the one to cache it. You can then destroy the caching one when the app is running to avoid it's pointless Update function from running.
Another choice is to write a custom editor for this class that calls Cache in the Editor rather than the Update mode used here (which I hacked out for brevity to demonstrate the principle).
But I'm talking of instances that I haven't even created yet on editor. They will be created once the deserialized decided they need to. For already created instances, but my problem right now is the new ones. The already existent ones can be cached the way you explain, probably. I don't get the GetGet$$anonymous$$ethod part btw.
I feel a bit lost on that = are you talking about components that are added to the game object at runtime?
Normally you want to get the existing components and deserialize data into them from you storage. If there are components on the game object that aren't there in the editor before the game is played then you have no choice but to look for them at runtime. If you have components that you want to fill with values that are defined before playing then this is the right approach.
GetGet$$anonymous$$ethod is a way of getting the specific method that returns a value from a property without needing to use Invoke (which is stupidly slow). You can create a specific instance of a delegate using GetGet$$anonymous$$ethod for a property that will be as fast as calling it natively.
It was just a tip, I have no idea how you are intending to fill in these values...
Yes so long as those things are components on or beneath the instantiated object - this has to work or no prefabs that self referenced would ever fly :)
Answer by Sisso · Oct 10, 2013 at 01:13 PM
A partial and non so generic solution.
Before build you iterate between all Prefabs and objects in scene, then, attach a script that cache all information that you will need. For version 2, if you don't want to dirt your objects you should catalog in a unique prefab, but you have a problem to identify wich object it is or for wich prefab (you can add a script with the id... ops, failed again :P).
If you need to interate between prefabas and scenes you can use this. It is a incrible usefull stuff to make dirt scripts and prepare your build.
https://github.com/sisso/ngui-hdsd/blob/master/Assets/Scripts/Editor/EditorUtils.cs
Edited: Try to explain with code
Lets try, I will explain in code (not compiled :P)
This is your utility class that cache the components names
class ComponentsCache extends MonoBehaviour {
static function GenareteCache(obj: GameObject) {
var gc = obj.GetComponent(ComponentsCache);
if (!gc) gc = obj.AddComponent(ComponentsCache);
var names: String[] = [c.name for (c in obj.GetComponents())];
gc.componentNames = names;
}
static function GetComponentNames(obj: GameObject) {
// if we have build time cached
var gc = obj.GetComponent(ComponentsCache);
if (gc) return gc.componentsNames;
// if we don't have a cache, like in the editor or in objects created in runtime not derived from prefabs, we use the old fashion
return [c.name for (c in obj.GetComponents())];
}
var componentsNames: String[];
}
This is the code that you run on build time to cache all objects. Generally, for release only.
var ForAll = function() {
// for all objects generate cache
for (var obj in GameObject.FindObjectOfType(GameObject)) {
ComponentsCache.GenerateCache(obj);
}
}
EditorUtils.ForEachScene(function (sceneName: String) {
// we are in a scene, execute for all
ForAll();
return true;
});
EditorUtils.ForEachPrefab(function (obj: GameObject) {
// we are in a scene with prefab obj instantiated, execute for all
ForAll();
return true;
});
Now when you need at runtime the components names of any object you simple do
components = ComponentsCache.GetComponentNames(anyRuntimeObject)
So you mean just accessing a component (created at build time) that contains all the information previously gathered at build time? I don't understand your solution very well, but sounds interesting. Could you explain? I need to access each instance's components, so I presume I need to access them after instantiation.
Ok.... $$anonymous$$y answer sucks. I solve the problem to get information from components without need to access it, but what you want is to update.
Have you try dig into Reflection?
Your answer
Follow this Question
Related Questions
Get if any component in gameObject has changed 1 Answer
Way to scale all assets in a scene 1 Answer
[RESOLVED]How to get all available parameters from target Component 3 Answers
Script component in Editor vs. runtime-dependent inheritance 0 Answers
MetaData class can't be loaded and failed to load window layout 1 Answer