Search Unity

Get component type as string

Discussion in 'Scripting' started by AnalogUniverse, Mar 13, 2019.

  1. AnalogUniverse

    AnalogUniverse

    Joined:
    Aug 10, 2018
    Posts:
    64
    Hi all Im how do I get my component scripts name as a string, so I can store them in a separate dictionary and access them by name, for example Im trying to get all scripts which extend a Tween class Ive created, and place them in a dictionary for quick access, the scripts which extend tween are as follows
    TweenPossition
    TweenAlpha
    TweenScale
    TweenRotation

    So I then want to access them via name

    Code (CSharp):
    1. TweenValue myTween;
    2.  
    3.         _transforms.TryGetValue("MyObjectNameTweenPossition", out myTween);
    At the moment Ive got this, but I cant figure out how to get the type of component as a string

    Code (CSharp):
    1.                
    2.  
    3.      foreach (Transform child in transform)
    4.         {
    5.             if (child.GetComponent<MyUIElement>() != null)
    6.             {
    7.                 foreach (Tween component in child.GetComponents<Tween>())
    8.                 {
    9.                     _transforms.Add(child.name + ????, component);
    10.                 }
    11.             }
    12.  
    I hope that makes sense, thanks in advance :)
     
  2. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,195
    It sounds like you just want to call `GetType().Name` on your component object?

    Also, to avoid confusion, note that it's "position", not "possition".
     
    AnalogUniverse likes this.
  3. AnalogUniverse

    AnalogUniverse

    Joined:
    Aug 10, 2018
    Posts:
    64
    Hi dgoyette thanks I'll give that a go just to clarify so my code should read

    Code (CSharp):
    1. _transforms.Add(child.name + component.GetType().Name, component);
     
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    If _transforms is your dictionary (weird name for a dict of components), then yes, that would be it.

    Though if this is to be used in this loop:

    Code (csharp):
    1.  
    2.             foreach (Tween component in child.GetComponents<Tween>())
    3.             {
    4.                 _transforms.Add(child.name + ????, component);
    5.             }
    6.  
    I'll point out that you're looping over all 'Tween' components. If you have 2 Tween components of the same type you're going to end up with duplicates strings and the dictionary will fail since you have 2 objects trying to be inserted with the key "MyChildTween" (or whatever).

    ...

    I also question the effectiveness of this. How are you looking these values up? Wouldn't you need a reference to 'child' regardless to reconstruct the string key?

    Why not just call 'GetComponent' at that point? The dictionary isn't going to really save you much speed (especially since name has overhead, gettype.name has overhead, and string hashing/comparison is actually on the slower side of hashing/comparison vs other types).
     
    SparrowGS likes this.
  5. AnalogUniverse

    AnalogUniverse

    Joined:
    Aug 10, 2018
    Posts:
    64
    Weirdly enough I was just thinking the name I gave _transforms might be a bit confusing, the code is work in progress Its legacy code I was initially writing it another way.

    Well the overall structure of what Im writing is this
    SceneManager
    >Scene1
    >Scene2
    >Scene3
    ect ect

    Then I have
    Scene1
    >UIElement1
    >UIElement2
    >UIElement3
    ect ect

    Then for example I might have
    UIElement1
    >TweenPosition
    >TweenRotation

    Now the manger changes the scenes by running a manual update method on each scene, which in turn runs the Tweens with a manual update method on each Tween, Now using GetCompnent to find all the tweens on every single frame is probably going to slow things down a bit, so to Update the scene transition I was going to use

    Code (CSharp):
    1. foreach(KeyValuePair<string, Tween> component in myTweens)
    2. {
    3.     // do something with component.Value
    4. }
    The SceneManger and Scene classes dont need to know the names or type of the entry, they just run all of the Tweens in the dictionary calling the parent scripts methods (ie the parent "Tween" script). But if I want to access individual Tweens, say I want to change the start and end point or the duration of a particular Tween, I need a quick consistent way of doing that with a simple naming convention ie "UIElelmentName" + "TweenType"

    Code (CSharp):
    1. _myScenesTweens.TryGetValue("MyUIElementNameTweenPossition", out myTween);
    Any thoughts ?
     
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    Then why not just use a list?

    Unfortunately you have this fragility I pointed out about how if you have 2 tweens of the same type on the same object you're going to have a dictionary collision.

    Thing is this is an action that wouldn't have to occur every frame.

    Let the list be used for the iterating over all tweens.

    But when looking it up, you can just look it up via any numerous ways where that efficiency isn't necessary. You could just use GetComponent, or you could loop over the list and check which is which, or whatever you'd like.

    Unlike the string key which:
    1) doesn't handle key collisions well
    2) needs to be known in some manner may it be hard coded (yay magic strings) or constructed from the name of the element and the tween type you want which could alternatively be done with a simple GetComponent.
    3) requires a lot of extra management code
     
    AnalogUniverse likes this.
  7. AnalogUniverse

    AnalogUniverse

    Joined:
    Aug 10, 2018
    Posts:
    64
    Hi lordofduct Thanks But Im unlikely to have two Tweens of the same type on a given Object tho because the Tweens themselves would conflict one another, for example two "TweenPosition" scripts running would completely nullify the first TweenPosition script executed on the list.
    Im presuming component.GetType().Name would return "TweenPosition" and "TweenRotation" ect ect and not the class they both inherit from ie "Tween"

    But yes you are of course correct thinking about it the odd time I have to access the tween specifically to change the tween simply using

    Code (CSharp):
    1. TweenPosition component = this.GetComponent<TweenPosition>()
    Wouldn't be breaking the bank and is easy to remember, I think Ive been overthinking the problem, which often comes from trying to write several classes all at once ;) I think I'll use a list like you said, unless I think of another scenario where the dictionary approach is advantageous.

    Thanks Again !
     
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    Mind you, I don't know your design of TweenPosition or TweenRotation. But lets say for instance you have 2 TweenPositions, one tweening down the x-axis, another down the y-axis, using different eases to get an interesting visual effect. Like you tween linearly from left to right, while it ping pongs vertically.

    I don't know if your TweenPosition supports that, I know mine does.

    And I know that my artist/designer (teammate) will exploit this in game.

    ...

    I should also point out that if you're going to create string tokens that combine information you should get used to putting delimiters in them.

    Take in consideration if someone names a UIElement something that overlaps with a class name and you can end up with weird tokens.

    Note, I look at this from larger scopes, especially that of teams, or distributing libraries...

    So lets say you have a class:
    TweenPosition

    But someone comes along and is like "I need a custom TweenPosition, I'll just inherit and name it something like 'AltTweenPosition'".

    But then someone goes and creates a few UIElements:
    ButtonCancel
    ButtonOK
    ButtonCancelAlt - (where this button is just an alternate of the previous cancel... maybe it has a different look & feel)

    Oh no, now you can have a token that is:
    "ButtonCancelAltTweenPosition"

    And you don't know if it's ButtonCancel with an AltTweenPosition, or ButtonCancelAlt with a TweenPosition.

    What I usually do is stick a | or * in between (something that wouldn't appear in a class name or element name). Something like:
    "ButtonCancel|TweenPosition"

    ...

    That is if you MUST do this. I do this for example in my json save files for my games. It's a nice way to tokenize complex information in a place that's already stringified.

    ...

    Now I bet you'll say that you just wouldn't name anything like that so it overlaps. But my theory always is... why allow your system to be designed in a way that could potentially break itself if someone isn't 100% aware of the underlying design.

    What if you have teammates?

    What if you released your design to others?

    What if 3 years from now you crack open some software and you don't remember? (I won't lie, 6 years ago me is my worst damn enemy)

    You never know... and these sorts of bugs are the WORST to deal with when they're invisible. It's why I call them "magic strings"... strings are just always a headache.

    This is why things like "public const string SOMEVALUE = "SomeValue";" is a common practice you'll find out in the wild with many other programs. By putting strings in consts you centralize potential spelling mistakes to one place and allow the compiler to yell at you if you mess it up elsewhere.

    ...

    My point, take it from people with experience... there's a reason we make these design choices.
     
    Last edited: Mar 13, 2019