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

How can you call for a component from one script to another?

Discussion in 'Scripting' started by DustyShinigami, Jul 9, 2019.

  1. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    Is there a way to call for a component from one script to another? Say for instance I want to call/reference a GameObject's Renderer, how would I do this? How would I set the code up?
     
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
  3. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    I've tried various ways of getting a Renderer from another script with GetComponent, and nothing has worked though. :-\ And to clarify, I'm trying to reference the player's renderer, but unless I use DontDestroyOnLoad, the player in the current scene won't carry over, and the reference to that version's renderer will be lost in the next scene. I'm trying to experiment getting the renderer without using DontDestroyOnLoad. Unless, of course, that's not possible at all...?
     
    Last edited: Jul 9, 2019
  4. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,616
    When you switch scenes you have to find the player in the new scene and get the component from that player. You can find the player GameObject using GameObject.Find, GameObject.FindWithTag, GameObject.FindObjectOfType, or something along those lines.

    If you have some sort of game manager object that's always loaded, you can also go the other direction- have the player object find the game manager and message the game manager to update it's reference to the player.
     
  5. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    Thanks for the suggestion. I'll give that a go. :)
     
  6. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    Nah, that's not working for me either. :( As soon as it changes to the next scene, the box in the Health Manager says 'None'. After that, if the player gets hit or killed it gives me an error saying it's trying to access it but can't. I've taken a screenshot of the game in action and the code I've put in the Health Manager at line 91.

    Untitled.png
     
  7. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,616
    It will because the player is in the scene that you unloaded. Your health manager script will lose this reference every time you unload the scene that the player was in. When you load the new scene, you have to find the player that's in the new scene and get it's renderer component. You set that variable in the Health manager script to that renderer.

    Maybe you can simply add a null check to your health manager script. If the player is null, than have the health manager script try to find the player again.

    Edit: By the way, that thing that you are doing with the scene manager looks like it should have worked, but did you check to see if getting the scene by name actually returned a result? Did you Debug.Log the current scene to see if it was the same index? That's not something you want to do in update, anyway. Finding a game object like that is slow, and you don't want to try to do it every frame.
     
    Last edited: Jul 9, 2019
    DustyShinigami likes this.
  8. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    Yeah, I know the 'old' version of the player causes the script to lose the reference once the scene has unloaded. It's a case of trying to figure out how to obtain the 'new' version's renderer. That's been giving me trouble for the past week or two. :-\

    I'll try the null check you suggested. And yeah, I tried a Debug.Log and it recognises the scene has loaded from that line. Would you suggest I do the GameObject finding in a new method? I would normally do it in Start or Awake, but as the Game/Health Manager doesn't get destroyed on load, doesn't that mean its Start/Awake functions have already been called and passed...?
     
  9. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    I did it this way, but still nothing. :(

    Code (CSharp):
    1. void Update()
    2. {
    3. if (SceneManager.GetActiveScene() == SceneManager.GetSceneByName("level 1, room 2"))
    4.         {
    5.             Debug.Log("checking for the player");
    6.             CheckPlayer();
    7.         }
    8.     }
    9.  
    10.     public void CheckPlayer()
    11.     {
    12.         if(thePlayer != null)
    13.         {
    14.             Debug.Log("found the player");
    15.             playerRenderer = GameObject.FindWithTag("Player").GetComponent<Renderer>();
    16.         }
    17.     }
    I did if (thePlayer == null) but it didn't do anything either. It comes up with "found the player" in the console though.

    EDIT: Hmm, it's still checking these every frame.
     
  10. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    You're not setting thePlayer, you're setting playerRenderer. So it's going to keep trying every frame.
    I would recommend reading up more throughly on the Unity tutorials - maybe this one? https://learn.unity.com/tutorial/getcomponent

    There are multiple ways to do this, but the simplest would be something like:

    Code (CSharp):
    1. // Don't use this directly, always call GetPlayerRenderer when you want it
    2. private Renderer playerRenderer;
    3.  
    4. public Renderer GetPlayerRenderer()
    5. {
    6.     // Note: This works because Unity overrides the normal meaning of null
    7.     // for UnityEngine.Object and descendents, so a destroyed object will count as null.
    8.     if(playerRenderer != null)
    9.         return playerRenderer;
    10.  
    11.     var player = GameObject.FindWithTag("Player");
    12.     Debug.Assert(player != null);
    13.  
    14.     playerRenderer = player.GetComponent<Renderer>();
    15.     Debug.Assert(playerRenderer != null);
    16.  
    17.     return playerRenderer;
    18. }
     
    Ryiah likes this.
  11. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,616
    For objects that are already in play, Start and Awake are not called again when a new scene is loaded. Just so you know.
     
    DustyShinigami likes this.
  12. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    @Errorsatz: The first person who replied gave me the same link to the GetComponent tutorial.

    I'm also having problems with your suggestion too. :( I've set it up as you've put, I've called the method from the HealthManager's Awake function, and it gives me the Assertion Failed error right at the start. If I don't call it on Awake, I get an error on line 70 and 127 saying that the Object reference is not set to an instance of an object, and in the very next scene I get the same Assertion error.

    Here's my HealthManager script:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using UnityEngine.SceneManagement;
    6.  
    7. public class HealthManager : MonoBehaviour
    8. {
    9.     //The counters will count down and will keep counting down based on the length variables
    10.     public int maxHealth;
    11.     public static int currentHealth;
    12.     public float invincibilityLength;
    13.     public float flashLength;
    14.     public float respawnLength;
    15.     public GameObject deathEffect;
    16.     public Image blackScreen;
    17.     public float fadeSpeed;
    18.     public float waitForFade;
    19.     public Image flame1;
    20.     public Image flame2;
    21.     public Image flame3;
    22.     public Sprite fullFlame;
    23.     //public Sprite threeQuarterFlame;
    24.     public Sprite halfFlame;
    25.     //public Sprite quarterFlame;
    26.     public Sprite EmptyFlame;
    27.     public GameObject playerPrefab;
    28.     //public GameObject rendererReference;
    29.  
    30.     private float invincibilityCounter;
    31.     private float flashCounter;
    32.     private bool isRespawning;
    33.     private Vector3 respawnPoint;
    34.     private bool isFadetoBlack;
    35.     private bool isFadefromBlack;
    36.     private PlayerController thePlayer;
    37.     private Quaternion startPosition;
    38.     private Renderer playerRenderer;
    39.  
    40.     /*void Awake()
    41.     {
    42.         if (SceneManager.GetActiveScene() == SceneManager.GetSceneByName("level 1, room 2"))
    43.         {
    44.             if (thePlayer != null)
    45.             {
    46.                 Debug.Log("found the player");
    47.                 playerRenderer = GameObject.FindWithTag("Player").GetComponent<Renderer>();
    48.             }
    49.         }
    50.     }*/
    51.  
    52.     void Start()
    53.     {
    54.         thePlayer = playerPrefab.GetComponent<PlayerController>();
    55.         currentHealth = maxHealth;
    56.         respawnPoint = playerPrefab.transform.position;
    57.         startPosition = playerPrefab.transform.rotation;
    58.     }
    59.  
    60.     void Update()
    61.     {
    62.         //These functions are checked every frame until the player takes damage
    63.         if (invincibilityCounter > 0)
    64.         {
    65.             invincibilityCounter -= Time.deltaTime;
    66.             flashCounter -= Time.deltaTime;
    67.             if (flashCounter <= 0)
    68.             //The Flash Counter is currently set at 0.1 and will be within the 0 region as it counts down. During this period, the playerRenderer will alternate between on and off
    69.             {
    70.                 playerRenderer.enabled = !playerRenderer.enabled;
    71.                 //The Flash Counter will keep counting down and reloop depending on the Flash Length time
    72.                 flashCounter = flashLength;
    73.             }
    74.             //This makes sure after the flashing and invincibility has worn off that the player renderer is always turned back on so you can see the player
    75.             if (invincibilityCounter <= 0)
    76.             {
    77.                 playerRenderer.enabled = true;
    78.             }
    79.         }
    80.         if (isFadetoBlack)
    81.         {
    82.             blackScreen.color = new Color(blackScreen.color.r, blackScreen.color.g, blackScreen.color.b, Mathf.MoveTowards(blackScreen.color.a, 1f, fadeSpeed * Time.deltaTime));
    83.             if (blackScreen.color.a == 1f)
    84.             {
    85.                 isFadetoBlack = false;
    86.             }
    87.         }
    88.         if (isFadefromBlack)
    89.         {
    90.             blackScreen.color = new Color(blackScreen.color.r, blackScreen.color.g, blackScreen.color.b, Mathf.MoveTowards(blackScreen.color.a, 0f, fadeSpeed * Time.deltaTime));
    91.             if (blackScreen.color.a == 0f)
    92.             {
    93.                 isFadefromBlack = false;
    94.             }
    95.         }
    96.     }
    97.  
    98.     public Renderer GetPlayerRenderer()
    99.     {
    100.         if (playerRenderer != null)
    101.             return playerRenderer;
    102.  
    103.         var player = GameObject.FindWithTag("Player");
    104.         Debug.Assert(player != null);
    105.  
    106.         playerRenderer = player.GetComponent<Renderer>();
    107.         Debug.Assert(playerRenderer != null);
    108.  
    109.         return playerRenderer;
    110.     }
    111.  
    112.     public void HurtPlayer(int damage, Vector3 direction)
    113.     {
    114.         //If the invincibility countdown reaches zero it stops, making you no longer invincible and prone to taking damage again
    115.         if (invincibilityCounter <= 0)
    116.         {
    117.             currentHealth -= damage;
    118.             FlameMetre();
    119.             if (currentHealth <= 0)
    120.             {
    121.                 Respawn();
    122.             }
    123.             else
    124.             {
    125.                 thePlayer.Knockback(direction);
    126.                 invincibilityCounter = invincibilityLength;
    127.                 playerRenderer.enabled = false;
    128.                 flashCounter = flashLength;
    129.             }
    130.         }
    131.     }
    132.  
    133.     public void Respawn()
    134.     {
    135.         //A StartCoroutine must be set up before the IEnumerator can begin
    136.         if (!isRespawning)
    137.         {
    138.  
    139.             StartCoroutine("RespawnCo");
    140.         }
    141.     }
    142.  
    143.     //IEnumerators or Coroutines will execute the code separately at specified times while the rest of the code in a codeblock will carry on executing as normal
    144.     public IEnumerator RespawnCo()
    145.     {
    146.         if (!Checkpoint.checkpointActive)
    147.         {
    148.             isRespawning = true;
    149.             thePlayer.gameObject.SetActive(false);
    150.             Instantiate(deathEffect, thePlayer.transform.position, thePlayer.transform.rotation);
    151.             yield return new WaitForSeconds(respawnLength);
    152.             isFadetoBlack = true;
    153.             yield return new WaitForSeconds(waitForFade);
    154.             isFadefromBlack = true;
    155.             isRespawning = false;
    156.             thePlayer.gameObject.SetActive(true);
    157.             currentHealth = maxHealth;
    158.             FlameMetre();
    159.             invincibilityCounter = invincibilityLength;
    160.             playerRenderer.enabled = false;
    161.             flashCounter = flashLength;
    162.             SceneManager.LoadScene(SceneManager.GetActiveScene().name);
    163.             GameManager.currentEmbers = 0;
    164.         }
    165.  
    166.         else if (Checkpoint.checkpointActive)
    167.         {
    168.             isRespawning = true;
    169.             thePlayer.gameObject.SetActive(false);
    170.             Instantiate(deathEffect, thePlayer.transform.position, thePlayer.transform.rotation);
    171.             yield return new WaitForSeconds(respawnLength);
    172.             isFadetoBlack = true;
    173.             yield return new WaitForSeconds(waitForFade);
    174.             isFadefromBlack = true;
    175.             isRespawning = false;
    176.             thePlayer.gameObject.SetActive(true);
    177.             thePlayer.transform.position = respawnPoint;
    178.             thePlayer.transform.rotation = startPosition;
    179.             currentHealth = maxHealth;
    180.             FlameMetre();
    181.             invincibilityCounter = invincibilityLength;
    182.             playerRenderer.enabled = false;
    183.             flashCounter = flashLength;
    184.         }
    185.     }
    186.  
    187.     public void FlameMetre()
    188.     {
    189.         switch (currentHealth)
    190.         {
    191.             case 30:
    192.                 flame1.sprite = fullFlame;
    193.                 flame2.sprite = fullFlame;
    194.                 flame3.sprite = fullFlame;
    195.                 return;
    196.             case 25:
    197.                 flame1.sprite = fullFlame;
    198.                 flame2.sprite = fullFlame;
    199.                 flame3.sprite = halfFlame;
    200.                 return;
    201.             case 20:
    202.                 flame1.sprite = fullFlame;
    203.                 flame2.sprite = fullFlame;
    204.                 flame3.sprite = EmptyFlame;
    205.                 return;
    206.             case 15:
    207.                 flame1.sprite = fullFlame;
    208.                 flame2.sprite = halfFlame;
    209.                 flame3.sprite = EmptyFlame;
    210.                 return;
    211.             case 10:
    212.                 flame1.sprite = fullFlame;
    213.                 flame2.sprite = EmptyFlame;
    214.                 flame3.sprite = EmptyFlame;
    215.                 return;
    216.             case 5:
    217.                 flame1.sprite = halfFlame;
    218.                 flame2.sprite = EmptyFlame;
    219.                 flame3.sprite = EmptyFlame;
    220.                 return;
    221.             case 0:
    222.                 flame1.sprite = EmptyFlame;
    223.                 flame2.sprite = EmptyFlame;
    224.                 flame3.sprite = EmptyFlame;
    225.                 return;
    226.  
    227.             default:
    228.                 flame1.sprite = EmptyFlame;
    229.                 flame2.sprite = EmptyFlame;
    230.                 flame3.sprite = EmptyFlame;
    231.                 return;
    232.         }
    233.     }
    234.  
    235.     /*public void HealPlayer(int healAmount)
    236.     {
    237.         currentHealth += healAmount;
    238.         if(currentHealth > maxHealth)
    239.         {
    240.             currentHealth = maxHealth;
    241.         }
    242.     }*/
    243.  
    244.     public void SetSpawnPoint(Vector3 newPosition)
    245.     {
    246.         respawnPoint = newPosition;
    247.     }
    248. }
     
  13. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    Totally baffling, this. Obviously, the player and its renderer are being destroyed in the new scene from the previous scene, but there must be a way of getting it again. And yet, every method that I've tried and has been suggested just doesn't appear to work.
     
  14. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    "thePlayer" member is private and not serialized, so it hasn't been initialized at the time you call it in Awake()- it is always going to be null at that point. Awake only gets called once, the same frame the GameObject/Component is created. It also has zero influence on the next line- GameObject.FindWithTag("Player") will locate any object in the scene tagged with the "Player" tag and return it- if "thePlayer" is already a working reference to the player object, you don't need to search the scene- use thePlayer.GetComponent<Renderer>() instead. I'm not really sure what result is desired from this block of code, as its written.

    "If thePlayer object has a reference, search the scene for a new object with the 'Player' tag, return it, and then get the Renderer component from it" makes little sense to me.

    You inserted the GetPlayerRenderer method but aren't actually using it anywhere. The idea is that every time you go to try using the player's renderer, you first make sure that it exists, and get a new one if it doesn't. That's not what's happening- every frame, Update is being run, and as the error states at line 70 and 127, you're trying to reference the renderer and change a value in it, but it doesn't exist because of the scene change.

    Edit: Actually, one sec...
     
  15. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    Correction (making this a new comment so that it fires the alert again).

    Since you're not tracking the scene changes or reference losses using events, delete GetPlayerRenderer and try this instead:
    Code (csharp):
    1. private Renderer _playerRenderer = null;
    2. private Renderer playerRenderer
    3. {
    4.     get
    5.     {
    6.         if(_playerRenderer != null)
    7.             return _playerRenderer;
    8.  
    9.         _playerRenderer = GameObject.FindWithTag("Player")?.GetComponent<Renderer>();
    10.         return _playerRenderer;
    11.     }
    12. }
    Then you shouldn't need to change anything else in the code- whenever playerRenderer is used, it'll check for null and get the new reference if it's been lost, without the middle-man method.
     
  16. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    Thanks for the suggestion. Now I keep getting the error 'There is no 'Renderer' attached to the "Player" game object, but a script is trying to access it.' o_O I figured that it's actually the SkinnedMeshRenderer I need to be referencing, but I get the same error with that as well. o_O

    I mean, it's right there...

    Untitled.png

    It's not because it's a part of a child object, is it? Because it's part of the Body and not the Player parent...?
     
  17. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    GetComponent only retrieves Components directly on the GameObject you're checking- if you want one from a child, use GetComponentInChildren instead. If you have more than one child object with the same Component, then you'll probably have to add the correct Renderer to a reference in the parent, so that you can use GameObject.FindWithTag("Player")?.GetComponent<SomePlayerScript>().bodyRenderer or something (you can separate that into multiple lines of course). If the bodyRenderer field is serializable, you can drag and drop the child Renderer script into the reference in the inspector, and assuming you're using a prefab for the whole player object that should work well enough.
     
    Last edited: Jul 10, 2019
    DustyShinigami likes this.
  18. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    Thank you, my good sir! That seems to have done it! At last! :D It's only taken me about 2 weeks or so... :p Checking in the Inspector's Debug, the Player Renderer box is empty at the beginning of a scene, but once the player gets hit, the box adds the Skinned Mesh Renderer. :)

    The only problem I have now is that I have a bug with my death trigger in the next scene, but I don't think that's related to the issue I've been having. For some weird reason, my player isn't getting destroyed, the death particles don't show, and the player isn't returning to the checkpoint. Another issue to investigate...
     
  19. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    @Lysander Just a quick question: with the 'GameObject.FindWithTag("Player")?' what is the purpose of the question mark? What does that do exactly in this case?
     
  20. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,616
    It checks for null.
    https://docs.microsoft.com/en-us/do...ss-operators#null-conditional-operators--and-

    something like
    Code (csharp):
    1.  
    2. Renderer playerRenderer =GameObject.FindWithTag("Player")?.GetComponent<SomePlayerScript>().bodyRenderer
    3.  
    Is kind of like a short way to write
    Code (csharp):
    1.  
    2. Renderer playerRenderer;
    3. if (GameObject.FindWithTag("Player") != null)
    4. {playerRenderer = GameObject.FindWithTag("Player").GetComponent<SomePlayerScript>().bodyRenderer;
    5. }
    6. else
    7. {
    8. playerRenderer=null;
    9. }
    10.  
     
  21. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    Ahhh, I see. Thanks. :)
     
  22. Coolgamer132

    Coolgamer132

    Joined:
    Jun 11, 2019
    Posts:
    2
    To use one variable in another class if this is what you want you do this.
    Make an instance of the class the variable is in inside the class you want to access in.
    Then make sure the variable is public so we can access it then you access it, here is some code of how.

    ClassName class = new ClassName();
    int variable = class.VARIABLE_NAME; //example accessing int in ClassName class.