Scripting: custom user fields in GameObject (request)

It would be really helpful if the GameObject would have few (or just one) custom user fields of various type, e.g. user_int, user_float, etc.

While the “tag” field is helpful to identify objects on collisions/triggers, it’s a String and gets expensive on iPhone to compare on every collision/trigger. The workaround is to have a MonoBehaviour derived script (gives more options to identify an object), but type operations are also expensive (extract script component from a contact Collider/GameObject).

If only I had an integer field like user_int in addition to “tag”, I could assign integer object type to identify it during the collisions which will greatly improve performance on iPhone platform, it will also reduce GC calls.

If people think it’s a great performance tweak, please vote for it: http://feedback.unity3d.com/pages/15792-unity/suggestions/425030-scripting-custom-user-fields-in-gameobject

You can use a class, as I described here.

–Eric

Sorry, Eric, that’s exactly what I’m against and the whole reason for requesting an integer custom field. I’m using the same approach and it gives quite bad performance on iPhone due to the GetComponent() and typeof() calls which are very slow. BTW, I’m using C# only.

Um…what I posted doesn’t use GetComponent calls. Whether you’re using C# or not doesn’t make any difference; the concept is the same. If Unity had custom user fields in the inspector, they’d just use this technique internally anyway. The performance of using a property of a class and a plain float (or whatever) is virtually identical. This is what classes are for; the functionality you want already exists, so you might as well use it; the only drawback is that it can’t be easily implemented in the Unity IDE and must be created through scripting.

–Eric

Eric5h5, I think what he is trying to avoid is casting and having to extract the script from the GO. If the user values were native to the GO then there would be no need to get the script and cast.

I know…that’s why I said to use a class. Then you are never casting anything or getting anything from a script; the GO and the custom variable(s) are native to the same object. Look at the script I posted in the linked topic again.

There are certainly drawbacks to this approach because you can’t access properties in the same way as you would GameObject.name for example, but I’m pointing out that the desired speed and functionality does already exist–you just have to approach it slightly differently.

Also I should point out that even if you do use the GetComponent() technique, you only have to do that once. If you maintain the reference (i.e., cache it), then there is no speed penalty.

–Eric

Well, I might be missing something, but this is how I understand the issue; everything below is in C# and strongly typed.

GO has a list of attached components, containing various items including MonoBehavior and derived scripts from it.
To get a script of a certain class, I have to do

ActorBase actor = this.gameObject.GetComponent(typeof(ActorBase)) as ActorBase;

This works fine but has double penalty because of GetComponent() and typeof().

Now, as Eric suggested, you can cache bunch of references and I use it in all our games. It works fine if you cache it for the same GO for re-usage later. Things get complicated during trigger and collision checks.
To get a trigger check, I override a method in a MonoBehaviour script:

class Player : MonoBehaviour
{
...
void OnTriggerEnter(Collider other) 
{
     GameObject whoCollided = other.gameObject;
     ActorBase actor = whoCollided.GetComponent(typeof(ActorBase)) as ActorBase;
     if (actor != null)
     {
          switch(actor.Kind)
          {
              case ItemType.Bullet:
              ...               
          }
     }
}
...
}

Things are getting interesting, aren’t they? Now I have to use GetComponent() and typeof() to extract a base class ActorBase to find out what kind of object collided with the given one. I could use Tag property, but it will force GC kick in quite soon and does not worth it, plus string comparing is also slow. I can cache typeof(ActorBase), but it still not a perfect solution.

If we had a custom native int field in GameObject, things will be easier and faster, so we can avoid comparing strings with GameObject.Tag:

class Player : MonoBehaviour
{
...
void OnTriggerEnter (Collider other) 
{
     GameObject whoCollided = other.gameObject;
     switch(whoCollided.User_Int)
     {
          case ItemType.Bullet:
          ...               
     }
}
...
}

Not using GetComponent() and typeof() will save about 5-10ms, which is a huge amount of time and gives about 10-30fps increase.

Note: dont think that while you can write less code in JavaScript, the final compiled code will be smaller - no, the JS will add typeof() behind the scenes. :frowning:

I believe that single integer field will solve a lot of trouble for iPhone.

@Eric: beware of the circle references in your example, you need to set all the cached variables to “null” when that class is destroyed/disposed, otherwise you’ll be keeping the GO from finalizing by GC.

Now that is, indeed, true.

Of course, but I’ve generally been using pools of reusable objects, so actually destroying things doesn’t come up much.

–Eric

Why cant you do smthing like this

Derive you own custom game object

class myObj : GameObject {
   int m_nID;

   public int ID
   {
        get { return m_nID;}
        set { m_nID = value;}
   }
}

and during collision or to access from any script all u do is

void OnTriggerEnter (Collider other)
{
     myObj whoCollided = other.gameObject as myObj; 

     switch(whoCollided.ID)
     {
          case ItemType.Bullet:
          ...               
     } 
}

thats avoid casting and type of… :smile:

Well, that’s what first came to my mind, and you fell into the same trap :slight_smile: When you call Instantiate(), it cannot instantiate a prefab with your custom class derived from GameObject… nor can you replace gameObject property in a script object on a instantiated object as so many other objects in the scene and hierarchy have references to that GO… OOP is really in our way here, which is not helping…

It could work as you describe if Unity had something like Instantiate(), so we can instantiate using generics in C#. Then, Unity iPhone should support generics, which is another cry from developers…