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

Discussion How to access another script on the same object if you do not know the name of the other script?

Discussion in 'Scripting' started by SuperLuigi4, Jun 11, 2023.

  1. SuperLuigi4

    SuperLuigi4

    Joined:
    Jul 5, 2018
    Posts:
    5
    Hello guys I have a quick question. I currently have an EnemyHpScript.cs that I attach to a parent enemy object. The parent has child objects each with an EnemyHurtBoxScript.cs. The problem I'm running into, however, is that I also want to have another script on the enemy parent that's specific to its kind. So, a zombie enemy prefab would consist of the following: the parent object is the Zombie with both a ZombieScript.cs and an EnemyHpScript.cs, and this parent would have about 6 child objects (head, body, both arms, and both legs) each with a trigger collider and an EnemyHurtBoxScript.cs. This is ideal because that way I only need one enemy hurtbox script and one enemy hp script in my project that I can use across the board for literally every single enemy.

    Code (CSharp):
    1. parent.GetComponent<EnemyHPScript>().Damaged(weaponDamage);

    But then let's say I want to trigger something like a "rage" mode in the zombie when it's Hp drops below 50%. I would need to trigger this behavior change in ZombieScript.cs, not in EnemyHpScript.cs. Currently this is how I'm going about it:

    Code (CSharp):
    1.  
    2. public class EnemyHPScript
    3.         if(hp < maxHp * 0.5f)
    4.         {
    5.             if(gameObject.name.Contains("Zombie"))
    6.             {
    7.                 GetComponent<ZombieScript>().rage = true;
    8.             }
    9.  
    10.             if (gameObject.name.Contains("Vampire"))
    11.             {
    12.                 GetComponent<VampireScript>().rage = true;
    13.             }
    14.             ...etc
    15.         }

    As you can imagine this can become a very long list depending on how many different types of enemies there are. Is there a better or "correct" way to do this? Thanks in advance
     
  2. SuperLuigi4

    SuperLuigi4

    Joined:
    Jul 5, 2018
    Posts:
    5
    So to add more context, rather than all the if statements checking for the enemy's name and then feeing in the correct script into getcomponent<> I would prefer something like

    Code (CSharp):
    1.     public string otherScriptsName;
    2.  
    3.     void Update()
    4.     {
    5.         if (hp < maxHp * 0.5f)
    6.         {
    7.             GetComponent<otherScriptsName>().rage= true;
    8.         }
    9.            
    10.     }
    this way I can have a rage bool field in all enemy type scripts and I can just trigger that to true regardless of whether or not they actually go into rage mode. Really the problem comes from GetComponent not taking strings. I haven't been able to find a way to store a script component in a field. That way I can just pass the correct enemy type script to each EnemyHpScript's otherScriptName field in the inspector.
     
  3. CodeRonnie

    CodeRonnie

    Joined:
    Oct 2, 2015
    Posts:
    323
    The EnemyHPScript can have an event that is invoked when HP drops below 50%. Any other method that matches the required method signature can be added to that event. So, the Zombie can listen to the HP component, and what happens for a zombie under 50% gets defined in Zombie. Also, make sure you're not using GetComponent every time you need to reference the other component. Make a member field that caches the reference returned by GetComponent, like in the Awake() method, and use that cached reference from then on.
     
    SuperLuigi4 likes this.
  4. overdrink

    overdrink

    Joined:
    Oct 31, 2022
    Posts:
    3
    Hello, I suggest you to look at interfaces. It's really usefull to identify scripts as categories.

    For example :
    I use this interface in my characters that can take damages (every time I hit a collider I can just check if it has a IDamageable component).

    Code (CSharp):
    1. interface IDamageable
    2. {
    3.     void Damage(float arg_damages);
    4. }
    In your case the interface would look like this:

    Code (CSharp):
    1. interface IRageListener
    2. {
    3.     void RageMode();
    4. }
    And each ZombieScript/VampireScript/ etc.. would implement this interface.

    The advantage of doing this is that you can simply use one line on your EnemyHPScript by doing this :
    Code (CSharp):
    1. GetComponent<IRageListener>().RageMode();
     
  5. SuperLuigi4

    SuperLuigi4

    Joined:
    Jul 5, 2018
    Posts:
    5
    I guess an event would work. I know what events are, but I've never actually used one. I tried the suggestion below yours since you also liked that reply and sure enough that one worked. Is there a benefit to using an event over the interface suggestion?
     
  6. SuperLuigi4

    SuperLuigi4

    Joined:
    Jul 5, 2018
    Posts:
    5
    @overdrink thanks. I can confirm this works and I'll probably come back tomorrow to add my code to help anyone in the future. I had no idea interfaces could be used this way, I just learned that they were meant to be used as sort of a contract. I don't know why there isn't a more straightforward way to access a script, but hey this works just fine I guess.
     
  7. Adrian

    Adrian

    Joined:
    Apr 5, 2008
    Posts:
    1,051
    In addition to what the other have mentioned, instead of checking the game object's name, tag or some other contextual information to infer the existence of a component, it's much more robust to just directly check the component, preferably using
    TryGetComponent
    :
    Code (CSharp):
    1. if (target.TryGetComponent(out ZombieScript zombie)) {
    2.     zombie.range = true;
    3. }
     
    SuperLuigi4 and Ryiah like this.
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,963
    This method basically asks, "Do you support the RageListener contract?" by asking for the object and testing it for null.

    Which part of that could be more straightforward?

    Anyway, interfaces are amazing. Here's my blurb, probably redundant to some already-posted links:

    Using Interfaces in Unity3D:

    https://forum.unity.com/threads/how...rereceiver-error-message.920801/#post-6028457

    https://forum.unity.com/threads/manager-classes.965609/#post-6286820

    Check Youtube for other tutorials about interfaces and working in Unity3D. It's a pretty powerful combination.
     
    SuperLuigi4 and Ryiah like this.
  9. CodeRonnie

    CodeRonnie

    Joined:
    Oct 2, 2015
    Posts:
    323
    In my head I imagined getting back to you with an explanation of events and interfaces, but I tend to be long winded, and this is a pretty broad topic if you want it be. I happen to be working my way through a popular, free 10 hour video course on learning Unity from CodeMonkey, and it has dedicated sections on events and interfaces. So, rather than me sitting down to write up a big long post covering these topics, it's probably quicker to just link you to this tutorial example that demonstrates real world use cases for both:
     
    SuperLuigi4 likes this.