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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

C# - Need some help with getComponent

Discussion in 'Scripting' started by Zocky, Mar 1, 2014.

  1. Zocky

    Zocky

    Joined:
    Dec 26, 2013
    Posts:
    22
    Hey all.

    I have a little problem with getComponent. To put it simply, lets say playet is moving "planet X", which has "Script_X" assigned to it.

    Now, thing is, we have "planet_A", "planet_B", and "planet_C" in scene, and they all have a script (each) assigned to them (script_A, script_B, script_C..."planet_A has script_A and so on).

    My problem is, when player moves planet X, it can hit any of the planets, A, B or C, but i have to access their script(or rather, property in them), so i wanted to store script name in variable, and use that variable in getComponent via:
    "other.GetComponent<scriptName>().enemyName"

    Where "scriptName" is variable of type "string".
    But in such case, i get error something like "scriptname is field, but is used like a "type".

    ___________

    So, basically, what i'm trying to ask is, if you have multiple objects, and you don't know which one it will hit, how would you just say "look, just look at collider, and take whatever script it has assined to, and get me property X from it (since you have multiple options, you don't really know what the name of script is).

    Or is there better way to approach this problem? Tnx!
     
  2. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    Well, you can pass a string by doing this:
    Code (csharp):
    1.  
    2.  other.GetComponent(scriptname);
    3.  
    (without the <>'s)

    But it then returns a type Component, which you would need to cast to the type you want, and you don't know the type. There are two ways to do it, the quick and dirty way, or the correct way. Quick and dirty way:
    Code (csharp):
    1.  
    2. Component c = other.GetComponent("scriptName");
    3. string enemyName = c.GetType().GetField("enemyName").GetValue(c) as string;
    4.  
    But good programmers will throw things at you for doing that because it's open to typos in the strings that aren't caught by the compiler, plus it ignores type-safety, and is generally frowned upon. So if you want to do it the right way, you should make either an interface or a base class that has the fields that all the other classes share and then inherit that in the planet scripts. Like this

    Code (csharp):
    1.  
    2. public class BasePlanet : MonoBehaviour
    3. {
    4.     public string enemyName;
    5.     public float otherThing;
    6.     public int andSoOn;
    7. }
    8.  
    9. public class script_A : BasePlanet
    10. {
    11.     ....etc
    12. }
    13.  
     
  3. El_Guig

    El_Guig

    Joined:
    May 13, 2013
    Posts:
    40
    There are different signatures for GetComponent in C#:
    Code (csharp):
    1.  
    2. this.GetComponent<ScriptA>(); // ScriptA is a Type -> returns a ScriptA
    3. this.GetComponent(ScriptA); // ScriptA is a Type -> returns a Component
    4. this.GetComponent("ScriptA"); // ScriptA is a string -> returns a Component
    5.  
    However, you will need to use the third option since you do not know the type originally (and well, you could convert your string to a type but that would be really dirty and definitely not modular), and would not know what type would be returned.
    I suggest you either combine all your ScriptA, ScriptB, ScriptC in one script taking different values depending on which object it's attached to (from the Inspector. this is only possible if the methods you want to call are similar). The second option is to use an interface (PlanetCollide for example), which all of your scripts would implement. You can then return an object of type PlanetCollide from GetComponent and call the given method that would be implemented in different ways in ScriptA, ScriptB and ScriptC.
     
  4. Zocky

    Zocky

    Joined:
    Dec 26, 2013
    Posts:
    22
    @makeshiftwings

    Code (csharp):
    1.    
    2.  public class BasePlanet : MonoBehaviour
    3.     {
    4.         public string enemyName;
    5.         public vector3 planetDirection;
    6.     }
    7.      
    8.     public class script_A : BasePlanet
    9.     {
    10.        planetDirection = new vector3(1,1,0);
    11.     }
    12.  
    So, in such case, i still assign script_A to planet_A, but i'm still a bit hazy on when my planet_X hits it, what exactly do a type to get planetDirection from script_A?

    Or do you somehow call parent class of the gameobject's current class?

    Could you explain me this part a bit more?

    Thing is,this planetDirection is being changed everytime a planet is hit by another planet; in such case, this particular property is changed.

    Also, tnx for your help!
     
  5. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    Well, first are you sure you need actual different classes for each planet? Or do you just want one class and put it on each of the planets? If all you're doing is changing the values of fields like planetDirection, you should just have one script, make all the things public, and set their values in the Unity editor instead of in code.

    But assuming you do need a bunch of classes you could do this:
    Code (csharp):
    1.  
    2. var planet = other.GetComponent("Script_A") as BasePlanet;
    3. var direction = planet.planetDirection;
    4.  
    So you'd still use the class name as a string, but you can cast the value that's returned as the parent class type and get the values that way.
     
  6. Zocky

    Zocky

    Joined:
    Dec 26, 2013
    Posts:
    22
    Hm yeah i think i could pull it of with just one class...i think.

    Thing is, many things that will be changing, are like you said, just properties, which could be set on each planet via public variables, but there will be also some other things, like, each planet will have some abilities, and those abilities will change, but i think i'll make a code for those abilities in special file and just link them or something like that, so that i could still just have one class.

    @makeshiftwings, could you "port" these two line into c#? I'm not too familiar with JS, so i'd just like to make sure i understand what that does.

    Speaking of :
    var planet = other.GetComponent("Script_A") as BasePlanet;
    var direction = planet.planetDirection;
     
  7. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    That is C#. ;) You can use "var" in C# as well. If you don't like var you can do it as

    Code (csharp):
    1.  
    2. BasePlanet planet = other.GetComponent("Script_A") as BasePlanet;
    3. Vector3 direction = planet.planetDirection;
    4.  
     
  8. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    This might be a little high-level, but the best way to do things in Unity is to split things into components. So all your planets could have a "Planet" script that handles everything that all planets have in common, and then you could also have a "Radioactive" script that handles things that are radioactive and a "HighGravity" script that handles things that are high gravity. Then if you wanted to make a radioactive, high gravity planet, you would just put all three of those scripts onto one planet. You should try to keep as much as you can within the scripts themselves so they don't have to reference each other, but when you do have to reference something from another script, you can do something like:

    Code (csharp):
    1.  
    2. Radioactive rad = planet.GetComponent<Radioactive>();
    3. if(rad != null)
    4. {
    5.     myShip.TakeRadiationDamage(rad.powerLevel);
    6. }
    7.  
    If a planet has the Radioactive component on it, it will do damage; if it doesn't, it will just skip over that part. If you keep things split into components, then you can easily add other things that are Radioactive or High-Gravity that aren't planets, like attaching Radioactive to an enemy ship, or High-Gravity to a black hole.
     
  9. Zocky

    Zocky

    Joined:
    Dec 26, 2013
    Posts:
    22
    Heh, you guys are great, tnx a lot for all the help!

    Yeah i didn't know you could se var statement in c# as well so i got confused.
    Hm yeah, i think your idea sound very usefull indeed. I'll have to think a bit about it, but i think i do understand it for the most part.

    Tnx again man!