Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Accessing specific Components on GameObjects

Discussion in 'Scripting' started by xofreshxo, Oct 13, 2020.

  1. xofreshxo

    xofreshxo

    Joined:
    Apr 12, 2017
    Posts:
    79
    Hello,
    I'm in the process of setting up a spell system for an RPG game but seem to have hit a problem when trying to access the spells. I have a Spell base class (Monobehavior) and let's say I have three spells: Fireball, Frostbolt, and Lightningbolt. Each of these three spells have their own script that override functions of the Spell base class.

    In the Unity editor, I have a Game Object called PlayerSpells (child to my Player game object). I add each of these spells as components to the PlayerSpells GameObject. Whenever I wish to cast a spell (i.e. Fireball), I'd like to access the component through a button with a script that calls the following on press:
    Code (CSharp):
    1. public string spellReference;
    2. public GameObject skillManager
    3. public void OnPress()
    4. {
    5. skillManager.GetComponent(spellReference).GetComponent<Skill>().OnCallSkill();
    6. }
    Where the component name, "Fireball", "Frostbolt" and "Lightningbolt" would each be entered in the Button Game Object under spellReference. The issue I am having is that whenever GetComponent<Skill>() is called, the first Spell component is always returned (i.e. all 3 return Fireball), even if GetComponent(spellReference) returns the correct spells (Fireball, Frost bolt, and Lightning Bolt). What this leads to is Fireball.OnCallSkill(); always being called. Is there a way to call the Spell function OnCallSkill() without using GetComponent? Because it seems like as soon as I do, it just defaults to the first Spell on the PlayerSpells Game Object.
     
    Last edited: Oct 13, 2020
  2. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    I don't understand what you're trying to achieve by calling GetComponet twice. Components do not have their own sub-components; the second GetComponent is effectively throwing away the first result and going back to the GameObject that all of these components are attached to.
     
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,520
    That is a hairy line of code. I recommend you break it apart:

    How to break down hairy lines of code:

    http://plbm.com/?p=248

    Also, I recommend liberally sprinkling Debug.Log() statements through your code to display information in realtime.

    Print the string you're passing into GetComponent()... is it what oyu think?

    Doing this should help you answer these types of questions:

    - is this code even running? which parts are running? how often does it run?
    - what are the values of the variables involved? Are they initialized?

    Knowing this information will help you reason about the behavior you are seeing.
     
  4. xofreshxo

    xofreshxo

    Joined:
    Apr 12, 2017
    Posts:
    79
    So when I call just:

    Code (CSharp):
    1. skillManager.GetComponent(spellReference);
    I cannot access any of the functions from the component it is referencing to or from the base class Spell. For instance, I cannot call

    Code (CSharp):
    1. skillManager.GetComponent(spellReference).OnCallSkill();
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,520
    This is because you are using the string version of GetComponent, which can only return a Component.

    https://docs.unity3d.com/ScriptReference/GameObject.GetComponent.html

    You need to cast it to what you think it is before using it.

    Personally I recommend using the templated version:

    Code (csharp):
    1. var fireballSpell = foo.GetComponent<Fireball>();
    but I accept that you may have structured your program to work with strings. I'm just not sure it will get you the kind of flexibility you currently anticipate.

    Another approach is to use interfaces, which are supported in a very typesafe and first class citizen way in Unity. You can even GetComponent<>() with an interface, but in the case where you have multiple scripts expressing the same interface, it will still be undefined which one you get.
     
    xofreshxo and Vryken like this.
  6. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    If you've never done dynamic casts before, the typical way to phrase that would be something like

    Code (CSharp):
    1. Spell mySpell = skillManager.GetComponent(spellName) as Spell;
    Note: this will return null if the component you got isn't a subtype of Spell
     
    xofreshxo and Vryken like this.
  7. xofreshxo

    xofreshxo

    Joined:
    Apr 12, 2017
    Posts:
    79
    Thanks! This is exactly what I was trying to achieve. It is working as intended.
     
    Kurt-Dekker likes this.