Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Update changes: AddComponent and strings

Discussion in 'Scripting' started by Armillary, Mar 8, 2015.

  1. Armillary

    Armillary

    Joined:
    Dec 16, 2009
    Posts:
    189
    I'm having an issue with a bit of code I was hoping to polish up for the new update and release. The idea of the whole system is that it adds components based on a day/night cycle and in order to do so I previously had variables of type object exposed to the unity editor so the user could add a script to it in the editor and see changes made according to said script when the cycle switched from day to night and/or dusk to dawn. I did this by getting the name of the script and using the string overload for addComponenet like so:

    public Object anEvent;
    gameobject.AddComponent(anEvent.getName());

    Now that the use of a string in addComponent is being phased out the automatic update added a different line of code which mainly serves as a stopgap and is not ideal. If anyone could provide some additional information concerning alternatives I'd be grateful. So far I've tried a number of approaches including changing the variable to a Type instead of an Object but this removes the field in the editor, using getType which gave the error "MonoScript is not derived from Component"
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    To reference 'types' I use this:
    https://code.google.com/p/spacepupp.../browse/trunk/SpacepuppyBase/TypeReference.cs

    And here is the inspector:
    https://code.google.com/p/spacepupp...tor/Inspectors/TypeReferencePropertyDrawer.cs

    The inspector references a call to TypeDropDown which can be found in here:
    https://code.google.com/p/spacepupp...wse/trunk/SpacepuppyBaseEditor/SPEditorGUI.cs

    Note it has an optional config attribute that allows defining a type to restrict to. For example in your case, you might want to restrict to types that only inherit from Component (because you're adding components). So you'd write something like:

    Code (csharp):
    1.  
    2. [TypeReference.Config(typeof(Component), dropDownStyle = TypeDropDownListingStyle.ComponentMenu)]
    3. public TypeReference ComponentType;
    4.  
    My type dropdown for the inspector isn't exactly pretty at this moment... It's only got 3 simple modes... nested by namespace, flat, or mimic the AddComponent menu (of course only for components, and it doesn't have the search functionality).

    I hope to update it in the future, but you know how it is... so much to do, so little time to do it.
     
    Last edited: Mar 8, 2015
  3. Armillary

    Armillary

    Joined:
    Dec 16, 2009
    Posts:
    189
    Thanks for the help, I'll see if I can't make this work.
     
  4. Fajlworks

    Fajlworks

    Joined:
    Sep 8, 2014
    Posts:
    344
    I had a similar issue where component names were saved in my database as a string. If you still haven't found an answer, please give this script a try.

    Basically this editor script scans your folder path you provide for scripts and it will generate an gameObject extension method that will allow you to simply write:

    Code (CSharp):
    1. string componentName = anEvent.getName();
    2. gameObject.AddComponentX(componentName);
    You can look for detailed usage and explanation on this forum thread:
    http://forum.unity3d.com/threads/custom-addcomponent-string-script-for-unity-5.307592/

    Please let me know if it helped you out. Best regards! :)
     

    Attached Files:

  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Now I'll admit up front my thing I posted is fairly complex. That's because I wanted a seamless and type safe system that didn't require you to type a string name into the inspector (and potentially mispell the name). I wanted a drop down that listed known types and what not.

    BUT

    If you just want a string, and to get the component based off that string name.

    You don't have to scan the folders. Mono/.Net already has a catalogue of all your scripts that you can query, it's what reflection is for.

    Furthermore, this can support types that aren't in your folders, but instead part of unity itself (BoxCollider, CharacterController, etc), as well as third party scripts.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class FindComponentByName : MonoBehaviour
    6. {
    7.  
    8.     public string ComponentName;
    9.  
    10.  
    11.     // Use this for initialization
    12.     void Start ()
    13.     {
    14.  
    15.         var tp = GetComponentTypeByName(this.ComponentName);
    16.         if(tp != null)
    17.         {
    18.             this.gameObject.AddComponent(tp);
    19.         }
    20.  
    21.     }
    22.  
    23.     public static System.Type GetComponentTypeByName(string name)
    24.     {
    25.         if (string.IsNullOrEmpty(name)) return null;
    26.  
    27.         var ctp = typeof(Component);
    28.         foreach (var assemb in System.AppDomain.CurrentDomain.GetAssemblies())
    29.         {
    30.             foreach(var tp in assemb.GetTypes())
    31.             {
    32.                 if (ctp.IsAssignableFrom(tp) && tp.Name == name) return tp;
    33.             }
    34.         }
    35.  
    36.         return null;
    37.     }
    38.  
    39. }
    40.  
    Honestly at the root of what I posted, this is ALL I'm really doing in my complicated thing in my initial post. I just wrapped it up with a pretty inspector and dropdown list and what not.

    You can see it in here as 'FindType':
    https://code.google.com/p/spacepupp...browse/trunk/SpacepuppyBase/Utils/TypeUtil.cs


    Furthermore, because you might use this a whole lot, you could catalogue the types into memory. Like so, broken into a static util class:

    Code (csharp):
    1.  
    2. public static class ComponentHelper
    3. {
    4.  
    5.     private static System.Collections.Generic.Dictionary<string, System.Type> _knownComponents;
    6.  
    7.     public static System.Type GetComponentTypeByName(string name)
    8.     {
    9.         if(string.IsNullOrEmpty(name)) return null;
    10.  
    11.         if(_knownComponents == null)
    12.         {
    13.             _knownComponents = new System.Collection.Generic.Dictionary<string, System.Type>();
    14.             var ctp = typeof(UnityEngine.Component);
    15.             foreach(var assemb in System.AppDomain.CurrentDomain.GetAssemblies())
    16.             {
    17.                 foreach(var tp in assemb.GetTypes())
    18.                 {
    19.                     if(ctp.IsAssignableFrom(tp)) _knownComponents.Add(tp.Name, tp);
    20.                 }
    21.             }
    22.         }
    23.  
    24.         return (_knownComponents.ContainsKey(name)) ? _knownComponents[name] : null;
    25.     }
    26.  
    27.     public static Component AddComponent(this UnityEngine.GameObject go, string name)
    28.     {
    29.         if(go == null) throw new System.ArgumentNullException("go");
    30.  
    31.         var tp = GetComponentTypeByName(name);
    32.         return (tp != null) ? go.AddComponent(tp) : null;
    33.     }
    34.  
    35. }
    36.  
     
    Last edited: Mar 13, 2015
  6. Armillary

    Armillary

    Joined:
    Dec 16, 2009
    Posts:
    189
    I do appreciate the time you've spent getting all this information up and available, and I hope I can find a way to apply this to my future work (I'm doing a bit of work on a concept now that might benefit from something of this nature down the line a bit). I apologize if I'm responding a bit slow but I'm juggling quite a bit at once so it's taking me a while to shift through everything. All three solutions are interesting but I think I'll try to dive a bit deeper into the code lordodduct posted (since I can propably learn a thing or two from it in the process of implementing it) but I am grateful for the info provided by Fajlworks and I'll give it a go if I can't decipher all else heh.
     
  7. Fajlworks

    Fajlworks

    Joined:
    Sep 8, 2014
    Posts:
    344
    Yeah, lordodduct's solution is hands down better. I'll give it a try!
     
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Note, I just edited my last post, because I noticed that I was still comparing the name when caching the types. Shouldn't have been doing that.