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

Change another script variable from OnGUI with C#

Discussion in 'Scripting' started by Zoltar26, Aug 12, 2013.

  1. Zoltar26

    Zoltar26

    Joined:
    Sep 6, 2012
    Posts:
    17
    Hi folks,

    One of the main frustrating things in Unity scripting is the access of variables from other scripts.
    So here is my issue with C# scripting:

    This is the Ball script which is attached to a Ball object:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Ball : MonoBehaviour
    6. {
    7.     int playerScore = 0;
    8.     public int PlayerScore
    9.     {
    10.         get { return playerScore; }
    11.         set { playerScore = value; }
    12.     }
    13.  
    14.     void Start ()
    15.     {
    16.         PlayerScore = 0;
    17.     }
    18.    
    19.    void OnCollisionEnter(Collision theCollision)
    20.     {
    21.         if(theCollision.gameObject.name == "LeftBorder")
    22.         {
    23.             PlayerScore++;
    24.         }
    25.     }  
    26. }
    27.  

    This is the Interface script which is attached to a PlayerMoney empty object (for displaying gui text only):

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Interface : MonoBehaviour
    6. {
    7.     Ball ball;
    8.        
    9.     void Start()
    10.     {
    11.         ball = new GameObject("Ball").AddComponent<Ball>();
    12.     }
    13.    
    14.     void OnGUI()
    15.     {
    16.         GUI.color = Color.blue;
    17.         GUI.Label(new Rect(400, 25, 100, 100), "Earned cash: " + ball.PlayerScore);
    18.     }
    19. }
    20.  
    The PlayerScore++ variable is updating correctly in the Ball script but in the Interface script
    the GUI.Label(new Rect(400, 25, 100, 100), "Earned cash: " + ball.PlayerScore); always displays
    the following text: Earned cash: 0 and it should display Earned cash: 1 because of
    the PlayerScore++ in the Ball script.
    Where am I doing wrong ?
    Thanks in advance for any help folks.
     
    Last edited: Aug 12, 2013
  2. trololo

    trololo

    Joined:
    Dec 13, 2012
    Posts:
    17
  3. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    First make it sure that void OnCollisionEnter really advances the counter.

    Try Debug.Log a line after the line 22:

    Code (csharp):
    1. void OnCollisionEnter(Collision theCollision)
    2. {
    3.     if(theCollision.gameObject.name == "LeftBorder")
    4.     {
    5.         PlayerScore++;
    6.         Debug.Log("PlayerScore changed to " + PlayerScore);
    7.     }
    8. }
     
  4. Zoltar26

    Zoltar26

    Joined:
    Sep 6, 2012
    Posts:
    17
    Yes, it advances correctly and I've mentioned it in my post:

    "The PlayerScore++ variable is updating correctly in the Ball script but in the Interface script
    the GUI.Label(new Rect(400, 25, 100, 100), "Earned cash: " + ball.PlayerScore); always displays
    the following text: Earned cash: 0 and it should display Earned cash: 1 because of
    the PlayerScore++ in the Ball script."


    Please help ... ?
     
  5. sam-perisentient

    sam-perisentient

    Joined:
    Aug 11, 2013
    Posts:
    31
    I just tested your code with a few mods, and it worked fine for me. Perhaps the problem *is* with the collision code?

    $Screen Shot 2013-08-13 at 4.33.55 AM.png

    interface is unchanged, but since I couldn't be bothered setting up collision stuff I changed the ball class to the following:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Ball : MonoBehaviour {
    6.     int playerScore = 0;
    7.  
    8.     public int PlayerScore {
    9.         get { return playerScore; }
    10.         set { playerScore = value; }
    11.     }
    12.  
    13.     // Use this for initialization
    14.     void Start () {
    15.    
    16.     }
    17.    
    18.     // Update is called once per frame
    19.     void Update () {
    20.         PlayerScore++;
    21.     }
    22. }
    23.  
    The only difference (that I see) between this and yours is that this isn't using collision code. Please try the above, and see if you get the same result that I do. If so, I expect that you have 2 different ball objects, and only 1 is getting collisions? Or perhaps something similar.
     
    Last edited: Aug 12, 2013
  6. kral

    kral

    Joined:
    Aug 7, 2011
    Posts:
    25
    ball is a class you cant create gameobject from that you need to define a gameobject

    Code (csharp):
    1.  
    2.  
    3. using UnityEngine;
    4.  
    5. using System.Collections;
    6.  
    7.  
    8.  
    9. public class Interface : MonoBehaviour
    10.  
    11. {
    12.  
    13.     Ball ball;
    14.     private GameObject ballIns;
    15.  
    16.        
    17.  
    18.     void Start()
    19.  
    20.     {
    21.  
    22.         ballIns = new GameObject("Ball");
    23.         ballIns.AddComponent("Ball");
    24.         ball = ballIns.GetComponent<Ball>();
    25.  
    26.     }
    27.  
    28.    
    29.  
    30.     void OnGUI()
    31.  
    32.     {
    33.  
    34.         GUI.color = Color.blue;
    35.  
    36.         GUI.Label(new Rect(400, 25, 100, 100), "Earned cash: " + ball.PlayerScore.ToString());
    37.  
    38.     }
    39.  
    40. }
    41.  
    42.  
    43.  
     
  7. Zoltar26

    Zoltar26

    Joined:
    Sep 6, 2012
    Posts:
    17
    Hi Sam and Kral,

    Your code works fine as you said but I didn't find any two ball objects.
    The collision code is ok because if I put Debug.Log(PlayerScore++); in the collision code
    under PlayerScore++; it shows 1 in the console.

    I also tried Kral's option and I didn't get any error messages but still It displays Earned cash: 0

    Any more suggestions please ?
     
  8. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Maybe Unity has problems with variables having the same name?

    Try:

    Code (csharp):
    1. int _playerScore = 0;
    2. public int PlayerScore
    3. {
    4.     get { return _playerScore; }
    5.     set { _playerScore= value; }
    6. }
    PS really having no clue..
     
  9. Zoltar26

    Zoltar26

    Joined:
    Sep 6, 2012
    Posts:
    17
    No dkozar, it didn't help.
    Unfortunately the only way I can make it work is by changing the variable playerScore in the Ball script into a static variable and then everything
    works fine.
    It seems I have no other choice...
     
  10. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
  11. Mister-E2

    Mister-E2

    Joined:
    Jun 6, 2013
    Posts:
    179
    put the OnGUI function inside the ball class to make sure it is actually changing. Then work from there.
     
  12. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Yeah, I also meant to say that, but I believe the man that the number increases. :)

    Anyway, for the Ball script:

    Code (csharp):
    1. void OnGUI()
    2. {
    3.     if (GUI.Button(new Rect(10, 10, 100, 40), "Increase")
    4.         PlayerScore++;
    5. }
     
  13. Zoltar26

    Zoltar26

    Joined:
    Sep 6, 2012
    Posts:
    17
    This code works ok if I press on the Increase button, but I want to display the PlayerScore from the Ball script.
     
  14. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    ???

    Haven't you say that you want the value of PlayerScore variable, being inceremented by the Ball script, to be displayed by the Interface.cs script?
     
  15. Zoltar26

    Zoltar26

    Joined:
    Sep 6, 2012
    Posts:
    17
    There was maybe a misunderstanding:
    The value of PlayerScore variable is indeed incremented in the Ball script if there's a collision between the Ball object and
    the LeftBorder object.
    Your example was with a button from the Ball script and as I said it works fine.
    But, I'm trying to use a Interface script to display the same PlayerScore variable by getting access to the Ball script
    and there's the problem.
    Why it isn't possible to display the updated ball.PlayerScore value from the Interface script ?
    What if I don't want to use the OnGUI method from the Ball script ?
    The GUI should be in a different script and not in the Ball script.
    I can make it work if I change the PlayerScore variable into a static one, but this isn't the way I want it to work.

    Here is something that might give some clue:
    I've added to the Update method in the Ball script the following:
    Debug.Log(PlayerScore);
    Remember that PlayerScore++ variable is under the OnCollisionEnter method, and this
    is what I get in the console window:

    $13-08-2013 12-17-37.png

    Why the log shows at first 0 and after the collision it shows 1 and then 0 again and etc.
    It should show only 1 continuasly. this is weird...

    Thanks for any help folks.
     
    Last edited: Aug 13, 2013
  16. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    If "it works fine" means that it is really incrementing the value the way the "Interface.cs" script gets its UI updated, then your problem is solved.

    But I believe by "it works fine" you meant that it is incrementing the value in the Ball script, but that is what it's supposed to do. So this was then another misunderstanding.

    The GUI snipped I gave you was meant to be put into the Ball script - indeed.

    The reason is to bypass the collider thing because I (and other readers of the thread) don't believe that the collider thing works ;)
     
  17. sam-perisentient

    sam-perisentient

    Joined:
    Aug 11, 2013
    Posts:
    31
    I don't think you want to hear this, but the reason you're getting 1, 0, 1, 0 etc is because there's more than one Ball component floating around. Perhaps one that you added manually (which is getting the collisions), and one that you're adding procedurally (which is not). That would explain the behaviour we're seeing. As dkozar, and others have shown; this is not a problem with access of a variable from one script to another.
     
  18. DougMcFarlane

    DougMcFarlane

    Joined:
    Apr 25, 2009
    Posts:
    197
    Adding to what Sam mentioned, that would explain why making the variable static seems to fix the problem, as all instances would now reference the same variable. When your game is running, how many 'Ball' game objects are in the hierarchy view?

    You could create a 'Ball' prefab with the Ball.cs script attached, and instantiate this prefab instead. Shouldn't really do anything different, but may make future updates to the 'Ball' easier.
     
  19. Zoltar26

    Zoltar26

    Joined:
    Sep 6, 2012
    Posts:
    17
    Dear folks,

    thank you sooooo much for your help.
    I finally solved the problem.

    Here is the code that I needed to display the ball.PlayerScore in the Interface script:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Interface : MonoBehaviour
    6. {
    7.     Ball ball;
    8.    
    9.     void Start()
    10.     {
    11.         ball = GameObject.Find("Ball").GetComponent<Ball>();
    12.     }
    13.    
    14.     void OnGUI()
    15.     {
    16.         GUI.color = Color.blue;
    17.         GUI.Label(new Rect(400, 25, 100, 100), "Earned cash: " + ball.PlayerScore);
    18.     }
    19. }
    20.  
    It was actually this line of code that I was missing: ball = GameObject.Find ("Ball").GetComponent<Ball>();
    Now, it displays the updated PlayerScore from the Ball script: Earned cash: 1

    Thank you all for your help, I really appreciate it.

    $13-08-2013 23-16-44.png
     
  20. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Ah, so the reason for your problems was this:

    Code (csharp):
    1. void Start()
    2. {
    3.     ball = new GameObject("Ball").AddComponent<Ball>();
    4. }
    It was actually creating a new game object in the hierarchy root (naming it "Ball"), and adding a Ball component to it. It was also returning a reference to this (non-moving) object so your Interface.cs script was monitoring this instance (and not with the Ball script attached to the same game object) :)

    Now, if you pasted my OnGUI snippet into the Ball component, you should have seen a duplicated "Increment" button: this should have help you to easier spot the problem. See? ;)