Search Unity

C Sharp OnGUI

Discussion in 'Immediate Mode GUI (IMGUI)' started by neutrino15, Jun 16, 2008.

  1. neutrino15

    neutrino15

    Joined:
    May 3, 2008
    Posts:
    27
    I switched over most of my code from Javascript to C#. It is giving me this error now when I try to draw stuff using "void OnGUI" The error is as follows:

    My code is as follows:

    in the main script file for this gameobject:
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class HUD : MonoBehaviour {
    5.     private Drawer drawer;
    6.  
    7.     // Use this for initialization
    8.     void Start () {
    9.         drawer = new Drawer();
    10.     }
    11.    
    12.     // Update is called once per frame
    13.     void Update () {
    14.    
    15.     }
    16.    
    17.     void OnGUI () {
    18.         drawer.display(true);
    19.     }
    20.  
    and the Drawer.cs file:
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Drawer : MonoBehaviour {
    5.     private string Text;
    6.  
    7.     // Use this for initialization
    8.     void Start () {
    9.         Text = "";
    10.     }
    11.    
    12.     public void display(bool v) {
    13.         if (v)  {
    14.             GUI.BeginGroup (new Rect (0, Screen.height - 50, Screen.width, 50));
    15.                 Text = GUI.TextArea (new Rect (15, 15, Screen.width - 30, 20), Text);
    16.             GUI.EndGroup();
    17.         }
    18.     }  
    19.  
    20. }
    21.  

    Thanks in advance for any help!
     
  2. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    thats somehow related to beeing in a different class. I get the same. When I put the whole functionality in 1 class it works without any problem.

    Guess thats a special thing with the OnGUI behavior.
     
  3. neutrino15

    neutrino15

    Joined:
    May 3, 2008
    Posts:
    27
    Wouldn't that kindof miss the point of being object oriented at all? That is, if you just had one big class for everything?
     
  4. MatthewW

    MatthewW

    Joined:
    Nov 30, 2006
    Posts:
    1,356
    The error has to do with mismatched Begin/End statements. So somewhere in your code you have one (or more) BeginSomething() calls than you do EndSomething() calls.
     
  5. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    Conclusion:
    Its definitely OnGUI that bombs when using external classes. Similar to how it does not allow coroutines.
    Sounds fishy thought in this case

    Workaround:
    To solve it, put the alternative class as component to your object as well, give it an OnGUI that calls the display and (thats the point where I am stuck right now) disable the component somehow from the main OnGUI class until its needed.
     
  6. MatthewW

    MatthewW

    Joined:
    Nov 30, 2006
    Posts:
    1,356
    Actually, I think I see the problem--your Text member needs to be public. Any variable used by the GUI system needs to be public.
     
  7. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    Does not make any difference.
    The C# part definitely needs some documentation if it behaves even "worse" than JavaScript ...
     
  8. neutrino15

    neutrino15

    Joined:
    May 3, 2008
    Posts:
    27
    I don't think I have mismatched Begin() and End() calls.. (the code in post 1 is all the gui code I have!)

    And darn, I really would like C# to work. I absolutely hate Javascript's "_prototype" approach to objects..
     
  9. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Um, "Prototype" is web Javascript. ;) Unity's Javascript doesn't do prototype any more than C# does.

    Code (csharp):
    1. private var drawer : Drawer;
    2.  
    3. function Start () {
    4.    drawer = new Drawer();
    5. }
    6.  
    7. function OnGUI () {
    8.    drawer.display(true);
    9. }
    Code (csharp):
    1. class Drawer {
    2.     private var Text : String;
    3.    
    4.     // Need a constructor; Start doesn't get called automatically unless it's attached to a gameObject
    5.     function Drawer () {
    6.         Text = "";
    7.     }
    8.    
    9.     function display(v : boolean) {
    10.        if (v) {
    11.           GUI.BeginGroup (new Rect (0, Screen.height - 50, Screen.width, 50));
    12.              Text = GUI.TextArea (Rect (15, 15, Screen.width - 30, 20), Text);
    13.           GUI.EndGroup();
    14.        }
    15.     }
    16. }
    Works fine....

    --Eric
     
  10. neutrino15

    neutrino15

    Joined:
    May 3, 2008
    Posts:
    27
    Oh... My... Lawd...

    I LOVE YOU!!!

    ok, not really, but thanks a lot! That really brightened my spirit!
     
  11. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Well, I hate prototype too. :) (But then, I never wrote any web Javascript until after learning Unityscript.)

    --Eric
     
  12. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    so this is a bug in the current OnGUI - C# implementation as no one has an answer
     
  13. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    No, it's just because the "Text" variable was never getting initialized; read the comment in my code. The GUI error was actually just a cascading error caused by the null reference exception -- always remember to look at the first error. Should work in C# too I would assume.

    --Eric
     
  14. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    stupid me, must have missed that one.

    Adding a constructor solved it. Thank you very much
     
  15. nafonso

    nafonso

    Joined:
    Aug 10, 2006
    Posts:
    377
    I think that the problem is that by default, when you do

    Code (csharp):
    1.  public class Drawer : MonoBehaviour {
    2.    private string Text;
    3.    ...
    4.  
    Text is set with null. And at the moment, for some reason (which I believe a bug, maybe Nicholas can correct me if I'm wrong), OnGUI starts to be called before Start is. So you end up feeding null vars to your GUI code, which will launch an exception that will make the code abort, never getting to the EndGroup, which will give those messages.

    Try putting that initializes Text in Awake instead of Start, and if this doesn't work either (then it is _really_ a bug, because it means that OnGUI is being called before Awake) just declare it as private string Text = "";

    Regards,
    Afonso
     
  16. runbellerun

    runbellerun

    Joined:
    Jul 25, 2007
    Posts:
    57
    So, adding a constructor works... but I was just reading the 'Writing scripts in c#' notes:
    http://unity3d.com/support/documentation/ScriptReference/index.Writing_Scripts_in_Csharp.html.

    "7. Avoid using the constructor.

    Never initialize any values in the constructor. Instead use Awake or Start for this purpose. Unity automatically invokes the constructor even when in edit mode. This usually happens directly after compilation of a script, because the constructor needs to be invoked in order to retrieve default values of a script. Not only will the constructor be called at unforeseen times, it might also be called for prefabs or inactive game objects.

    ...

    Actually there is no reason why you should ever have any code in a constructor for a class that inherits from MonoBehaviour. "

    So as Eric says, the start() function doesn't get called if the script isn't attached to a game object (man Eric you know lots of random useful stuff!).

    So what is the correct way to initialise values in these kinds of c# scripts, if we're not actually supposed to have a constructor?
     
  17. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    Thats the exact passage that let me run into this problem at all.
    Guess the documentation should clearly point out that this is mainly valid and of importance if you create a component for a GameObject.

    Awake would be a way to initialize with a fixed "initial setup" but not if you need to have control from outside of the init.
     
  18. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    It should be OK as far as I know in this case, then (which, admittedly, isn't very far). In fact I learned about using the constructor from a script that one of the UT people posted quite some time ago.

    --Eric
     
  19. jeremyace

    jeremyace

    Joined:
    Oct 12, 2005
    Posts:
    1,661
    You can use constructors all you like, overload them, even dance with 'em.

    The only issue comes when your script subclasses MonoBehaviour, and therefore is designed to be added to an object directly.

    The reason for this is because Unity handles the initialization and all other event calls for MonoBehaviour classes itself, and because of the way it handles things behind the scenes, this means that your constructor can be called anytime Unity feels like it, including in the editor IIRC.

    You can create instances of any class you wish inside a MonoBehaviour script of course without problems, as has probably been said already.

    -Jeremy
     
  20. runbellerun

    runbellerun

    Joined:
    Jul 25, 2007
    Posts:
    57
    Ah I see - I never actually knew Monobehaviour was solely for game objects, although it seems completely obvious now!
    Cheers guys, I just noticed that in Eric's example, the class 'drawer' doesn't infact make any references to 'Monobehaviour'. So he was right, again! :)