Search Unity

[Help] How do you set up a GameManager?

Discussion in 'Scripting' started by Reactorcore, Apr 7, 2012.

  1. Reactorcore

    Reactorcore

    Joined:
    May 19, 2011
    Posts:
    105
    Hello

    With Unity doing the magic for you behind the scenes has had a nasty side-effect; since I don't know how it works, I'm not sure on how to control the flow of the program. I obviously need a GameManager for that, but how do you set one up?

    A GameManager is a something that keeps track of what state the game is in, manages the menu/pause systems, records and stores information for various purposes (audio/video settings, control bindings, game save data). It needs to exist over multiple levels.

    The question is: How do I set up this GameManager in Unity?


    Do I need to make a gameObject with a script attached to it? If so, then how will it persist over multiple scenes?
    How do you code it?


    Please help, I just can't seem to wrap my head around this.
     
    Perrorist and h5n1xp like this.
  2. foxter888

    foxter888

    Joined:
    May 3, 2010
    Posts:
    530
    if you want it to persist do the DontDestroyOnLoad function, search for that on the manual.
    other things you could do is things like maybe using player prefs if you are trying to save settings like options and so on, it would all depend on the type of game you are making.
    since this so called game manager would be on a empty gameobject on the scene why not make it have some static variables?
    it would be easier to call them, then again this would be more towards a single player so it would depend on what you are trying to do.
     
  3. Tobias J.

    Tobias J.

    Joined:
    Feb 21, 2012
    Posts:
    423
    I won't claim you need that, but that's how I do it.

    You have the Don'tDestroyOnLoad() function.

    I have set up an enum like this to help me control the flow of the game:

    Code (csharp):
    1. public enum StateType
    2. {
    3.     DEFAULT,      //Fall-back state, should never happen
    4.     WAITING,      //waiting for other player to finish his turn
    5.     STARTTURN,    //Once, on start of each player's turn
    6.     PLAYING,      //My turn
    7.     PLACING,      //placing a new obstacle
    8.     BUYING,       //Buying something new
    9.     SHOOTING,     //aiming to shoot
    10.     BALLWAIT,     //Waiting for ball to stop
    11.     TURNOVER,
    12.     GAMEOVER,
    13.     GAMESTART,
    14.     LOBBY,        //Player is in the lobby
    15.     MENU,         //Player is viewing in-game menu
    16.     OPTIONS       //player is adjusting game options
    17. };
    My GameController script has a static var of type StateType, which is set and checked against all over the code. I've put this in the Update() function:

    Code (csharp):
    1.  
    2. switch (state)
    3.         {
    4.             case StateType.PLACING:
    5.                 if (objectToPlace != null)
    6.                     PlaceObject();
    7.                 else
    8.                     Debug.Log("ERROR: No object to place.");
    9.                 break;
    10.             case StateType.BUYING:
    11.                 break;
    12.             case StateType.BALLWAIT:
    13.                 //StopBouncing();
    14.                 break;
    15.             case StateType.STARTTURN:
    16.                 print("New Turn by: " + nowPlaying);
    17.                 IEvent startTurn = new StartTurn();
    18.                 EventManager.instance.QueueEvent(startTurn);
    19.                 AddEnergy();
    20.                 SetState(StateType.PLAYING, this);
    21.                 break;
    22.             case StateType.PLAYING:
    23.                 //check for numActions > MaxActions
    24.                 break;
    25.             case StateType.SHOOTING:
    26.                 break;
    27.             case StateType.TURNOVER:
    28.                 ChangePlayer(); //Do this last (from server!)
    29.                 SetState(StateType.STARTTURN, this);
    30.                 print("Turn is over!");
    31.                 break;
    32.             default:
    33.                 Debug.Log("ERROR: Unknown game state: " + state);
    34.                 break;
    35.         }
    36.  
    Obviously still very crude, but it's evolving in tandem with the game and so far it gets the job done just fine.

    Godspeed!
     
    labhanshpatel likes this.
  4. Tseng

    Tseng

    Joined:
    Nov 29, 2010
    Posts:
    1,217
    Also, since you seem to be more advanced, you can also go with a singleton pattern for your game manager. Then you don't need a gameobject.

    GameObjects with DontDestroyOnLoad tend to have a big drawback: You have to start a game from Scene 0 (and not the current one you are editing) in order to be there or add them manually to everyscene which is kind of redudant, especially if you have sound, music, game manangers

    Code (csharp):
    1.  
    2. public class GameManager {
    3.     private static GameMananger instance;
    4.  
    5.     private GameMananger() {
    6.         // initialize your game manager here. Do not reference to GameObjects here (i.e. GameObject.Find etc.)
    7.         // because the game manager will be created before the objects
    8.     }    
    9.  
    10.     public static GameManager Instance {
    11.         get {
    12.             if(instance==null) {
    13.                 instance = new GameManager();
    14.             }
    15.  
    16.             return instance;
    17.         }
    18.     }
    19.  
    20.     // Add your game mananger members here
    21.     public void Pause(bool paused) {
    22.     }
    23. ...
    24. }
    25.  
    In your MonoBehaviour scripts simply use
    Code (csharp):
    1.  
    2. // Always use the .Instance Property to get the current instance of the GameMananger)
    3. GameManager.Instance.Pause(true);
    4.  
     
    Last edited: Apr 7, 2012
    LinesinRows and Chiaros like this.
  5. Reactorcore

    Reactorcore

    Joined:
    May 19, 2011
    Posts:
    105
    @foxter888:

    I just read the manual page for PlayerPrefs and this sounds interesting, but how do I use it?
    And what exactly can I store in it? Is it limited to only storing some preset values or how does it work?

    As for what I'm doing, its a standard commercial grade single player game. You can read more about it here:
    http://forum.unity3d.com/threads/12...plex-game-architecture-where-to-begin-and-how


    @Tobias J.:

    Very useful, especially for managing the menu system I've come up with.
    I'm very curious, with this system you have, does it remember settings or previous game progress when turning on/off the application?
    I want to have a check on startup, that a user can toggle to show or disable the intro at the beginning, but I'm not sure how to do that.


    @Tseng:

    Yes, the singleton pattern is something I've been reading about lately and most articles that talk about a game manager have something to do with a singleton pattern. This one I have a lot to ask about.

    This is where I get most confused about this: If I don't need to attach the gameManager to a script, then how do I know its running?
    How does it exist and run within the program without being in a scene? What do I do to make it load when I hit play?

    Also why is this gameManager not static? How will it work if it needs to present everywhere and accessible by everything?
    Or am I misunderstanding how the keyword "static" works?



    On a sidenote, on what magic do static classes work? I did some experimenting by building the menu system pages as individual script files. Each page is a static class and they sit in the project folder (aka the place where stuff sits dormant until called by a script or dragged to the scene by yourself) and I have this testing gameObject that has a menuSystem script attached to it that calls for the individual scripts sitting the project folder (e. g. the menu pages are not attached to the gameObject, just sitting in the project view).

    It works great, but I'm completely confused to as how do I pass information from one menu page to another and how exactly do static methods and variables work within unity. And more importantly, why does it work this way? Where do the menu page scripts exist if the script that has their execution is sitting the project view, only called by a scripted gameObject in the scene?

    I know where the script that calls the menu pages is (attached to an empty gameObject in the scene), but where are the static menu pages scripts existing then? I figured nothing does anything in the project view, because its a just a place for dormant assets.


    Man, maybe I'm just making this too complicate, but I'm confused as hell.
    Not only that, but it seems everyone recommends using C# instead of UnityScript, so I need to relearn the way to code too. Gah.
     
    Last edited: Apr 7, 2012
  6. Tobias J.

    Tobias J.

    Joined:
    Feb 21, 2012
    Posts:
    423
    No, it won't remember anything. You have to use a DB on a server or a PlayerPrefs file for that.

    I think the easiest way to allow skipping of the intro is to just stop it if the player presses a key. But it seems like you'll need some logic to decide which scene to start with in any case, so you could just include the option you mention in that decision.
     
  7. Tseng

    Tseng

    Joined:
    Nov 29, 2010
    Posts:
    1,217
    My bad, just wrote it quickly and forgot to make it static. Of course it has to be static.

    The idea is that the first time you call the "Instance" property the GameManager will be instantiated (you can also have instantly instantiated

    Code (csharp):
    1.  
    2. public class GameManager {
    3.     private static GameManager instance = new GameManager();
    4.    
    5.     // make sure the constructor is private, so it can only be instantiated here
    6.     private GameManager() {
    7.     }
    8.  
    9.     public static Instance {
    10.         get { return instance; }
    11.     }
    12. }
    13.  
    Then it will be instantiated too. It depends on the usage, if it's something thats not always used you can instantiate it inside the "Instance" property

    A static class can only contain static members and hence can't be instantiated. A static class can't have a constructor. It's basically similar to a sealed class with static members and private constructor, but it gives the compiler a hint and the compiler will guarantee that this class can't be compiled (or throw error if you accidentally added something non-static).

    Usually you use it for utility classes, i.e. Mathf, where you put math functions in but where you know it will never be needed to instantiate it.

    I don't know how you designed your menus, but the usual OOP approach are events. Well, one thing about static methods is, that they don't depend on some member variables. You pass everything that's needed via parameters.

    What information you like to pass? And what GUI System are you using? (Unity built-in, guitextures, some GUI framework like ezGUI? In the OOP world you usually do it via events.

    While this way you got a bit overhead, you are more flexible (i.e you can sign for different events like: Click, Drag, MouseOver, MouseEnter, MouseLeave) while with static methods (like the Unity3D built-ui) you can only do one thing per call (i.e.click)
     
    unity_539074 likes this.
  8. Reactorcore

    Reactorcore

    Joined:
    May 19, 2011
    Posts:
    105
    @Tobias J.:

    The toggle function for the intro is part of the design, because the game is built to last and potentially used by multiple people on the same machine (family use or similar, the game features a profile system for that). As the game is expected to be in heavy use and turned on countless times, being able to turn off the intro all together is essential.
    However, I do also have a "button press to skip" thing too. It was pretty easy to do and works fine, just gotta learn how to add a delay for the transition though.

    Also that PlayerPrefs is something I really should look into more. Thanks.


    @Tseng:

    Okay, I'm almost getting how to set up the singleton, but the most basic thing I'm still missing. Sorry if I sound a bit basic (read: retarded) here, but this bit is whats had me stuck for quite a while without being able to continue.

    1. So I do as you instruct and have this singleton gameManager in the project folder.
    2. Then when I build, compile and run the game, the scripts are automatically loaded and the gameManager gets instantiated and now effectively exist in the system and I can now use it.
    3. But... then what? How do I control the flow of the game? When the game is loaded, how can it know what to do next?

    First it needs to check if it needs to run the intro based on previous user settings (or default if none present), then load all user data which will be used in the Profiles menu page where the user chooses his profile or creates a new one. Then he/she is now in the main menu from where he can start the game, tweak options and so on.

    Here is a diagram I made for the system flow of my game:




    The menu system will require some more advanced controls like textfields, dropdown selection boxes and scrollable areas, so Unity's OnGUI seems perfect for this. It has everything I need and I already learned how to use it (don't like the pixel placement and math involved to get the things right as opposed to a visual editor, but it works great though, so I shouldn't complain). Although if there is something better, I'm listening.

    Also overhead shouldn't be too much of a problem, the project is aimed at high-end PC and Mac as a hardcore action game.


    Now, you said events. I'm all for OOP based design so it looks like this is something I really need to learn about. How do I implement events in Unity?
     
  9. Tseng

    Tseng

    Joined:
    Nov 29, 2010
    Posts:
    1,217
    Basically that's it. In the GameManager class you would have stuff like pause/resume, load next level, loading and saving the game.

    Code (csharp):
    1.  
    2. public class GameManager {
    3.     private static GameManager instance = new GameManager();
    4.     private GameStates currentState;
    5.  
    6.     // make sure the constructor is private, so it can only be instantiated here
    7.     private GameManager() {
    8.     }
    9.  
    10.     public static Instance {
    11.         get { return instance; }
    12.     }
    13.  
    14.     public void Pause(bool paused) {
    15.         if(pasued) {
    16.             // pause the game/physic
    17.             Time.time = 0.0f;
    18.         } else {
    19.             // resume
    20.             Time.time = 1.0f
    21.         }
    22.     }
    23.     public GameStates CurrentState {
    24.         // only update state if it has been changed
    25.         if(currentState!=value) {
    26.             currentState = value;
    27.  
    28.             switch(value) {
    29.                 case GameStates.Intro:
    30.                     // Play Intro here
    31.                     break;
    32.                 ...
    33.                 default:
    34.                     // Do this when none of the cases above fit
    35.                     break;
    36.             }
    37.         }
    38.    }
    39. }
    40.  
    41. public enum GameStates {
    42.     Intro,
    43.     Mainmenu,
    44.     LevelSelection
    45. }
    46.  
    And in your GUI code something like (assuming you are using Unity built-in GUI
    Code (csharp):
    1.  
    2. void OnGUI() {
    3.     if(GUI.Button(new Rect(50,50,200,50), "Pause)) {
    4.        GameManager.Instance.Pause(true);
    5.    }
    6. }
    7.  
    You keep your GUI code of course somewhere separated and only save certain stings (like the current state) in the game manager similar to Tobias code above.

    Well the menu should not be associated with the Game Manager. The Game Manager would only save the current state. In your GUI code you test it and display it if it matches.

    i.e. Options menu

    Code (csharp):
    1.  
    2. void OnGUI() {
    3.     if(GameManager.Instace.CurrentState == GameStates.Options) {
    4.         // Draw your options menu
    5.     }
    6. }
    7.  
    Google for "C# Events". Basically you make a button with a click Button event and some other code which subscribes to this event.

    Code (csharp):
    1.  
    2. public class MyButton : MonoBehaviour {
    3.     public delegate void ClickEventHandler(); // delegate signature. Only methods which return void and have no arguments can be subscribed to the event
    4.     public event ClickEventHandler Click;
    5.  
    6.     void Update() {
    7.         if(/* check if user has clicked the button*/) {
    8.             // Check if anyone has subscribed to the event, otherwise you may get a Exception
    9.             if(Click!=null)
    10.                 Click();
    11.         }
    12.     }
    13. }
    14.  
    15. // in your UI script
    16. void Awake() {
    17.     MyButton button = ...; // get reference or instantiate to your button
    18.     button.Click += OnClicked; // Register the OnClicked method (see below) to the event. When ever the user clicks the button, the OnClicked Method will be called
    19. }
    20.  
    21. void OnClicked() {
    22.     // user has clicked the button
    23.     Debug.Log("Button was clicked");
    24. }
    25.  
    But it goes beyond a forum post to describe events, MSDN (C#) offers plenty of examples and documentation.

    UnityScipt doesn't support the += notation to subscribe for the events
     
  10. JRavey

    JRavey

    Joined:
    May 12, 2009
    Posts:
    2,377
    It's a small thing, but in the example code you weren't unsubscribing to an event. In C#, the subscriber has the responsibility unsubscribe (using the -= operator), otherwise you'll get errors or have an outstanding hold on an object which will preclude the garbage collector from removing the now defunct subscriber.
     
  11. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,749
    AFAIK this is not true as a delegate does not count as a gc reference. But i havent tested it just read about.

    i also would suggest you a finite state machine where every state your game can be in is its own class and is managing its own behavior and transition to other states. this way you can nicely decouple the states and in the enter/exit methods of each state (automatically called from the state manager) you can handle cleanup, loadlevel, preparation etc..
    have a read here (3 pages): http://www.ai-junkie.com/architecture/state_driven/tut_state1.html
    the example refers to ai but you will get the point.
    you can use the mentioned gamestate enum in the gamestate manager to store in which gamestate you are currently. for example the game starts in the intro state and when user clicks a button the intro state calls the gamestatemanager.changestate(mainmenustate). this method calls introstate.exit and mainmenustate.enter. its overkill for a tetris clone but i think larger games can be nicely handled this way.
     
  12. Tseng

    Tseng

    Joined:
    Nov 29, 2010
    Posts:
    1,217
    No, he's right. The multicast delegate (underlying event) will reference the delegate from the subscriber.

    http://msdn.microsoft.com/en-us/library/ms366768.aspx


    In i.e. WinForms this usually isn't an issue, because the object (button, texfield etc.) is inside the same class as the subscriber (button_OnClicked(...) method), because you sign up the events in code behind of the same file.

    Code (csharp):
    1.  
    2. using System.Windows.Forms;
    3. pubic MyWindow : Form {
    4.     Button okButton = new Button();
    5.     okButton.Text = "My Button";
    6.     okButton.Click += OnClick;
    7.  
    8.     okButton_OnClick(object sender, EventArgs e) {
    9.     }
    10. }
    11.  
    okButton will hold a reference to MyWindows/okButton_OnClick method/delegate. But when MyWindow gets disposed/closed, all references to okButton will get removed too, so both of then can be disposed.

    However, if you have something like:

    Code (csharp):
    1.  
    2. public class Score {
    3.     private static Score instance = new Score();
    4.  
    5.     public delegate ScoreUpdateHandler(Score score, int newScore);
    6.     public event ScoreUpdateHandler ScoreUpdate;
    7.  
    8.     public Score Instance {
    9.         get { return instance; }
    10.     }
    11.     public void AddScore(float points) {
    12.         currentScore += (int)points;
    13.         lastScoreUpdate = Time.time;
    14.        
    15.         if(ScoreUpdate!=null)
    16.             ScoreUpdate(this);
    17.         }
    18.     }
    19.  
    20. ...
    21. public class DisplayScore : MonoBehaviour {
    22.     void Awake() {
    23.         Score.Instance.ScoreUpdate += delegate(Score score, int newScore) {
    24.             guiText.text = newScore.ToString();
    25.         }
    26.     }
    27. }
    28.  
    Now if you remove the DisplayScore or the object it's in, it wouldn't be collected by the garbage collection, because Score is still referring to it and it wouldn't be able to be collected by the GC until score is freed too... which actually never happens, since it's a singleton.
     
    Last edited: Aug 27, 2012
  13. JRavey

    JRavey

    Joined:
    May 12, 2009
    Posts:
    2,377
    That was a really nice example, I might have to lift that sometime.
     
  14. Reactorcore

    Reactorcore

    Joined:
    May 19, 2011
    Posts:
    105
    Sorry for no reply yesterday, I had to think deep about all this on how it all works and also learn C# along the way.


    Wait a minute, I think I just realized something here I missed earlier.

    If I understood right, then that means its now existing in the scene, which means it might as well be considered an empty gameObject with a script attached to it(sort of, metaphorically speaking) and thus it will run its code. It was that simple. *facepalm*


    The thing I wanted to understand is where is ther void Main() function in Unity is and how can I use it, but now I think I get it.
    Once the gameManager script instatiates itself, it will already have the code to make approriate actions to control the flow of the game through states and what not, first loading through the settings and then executing the appropriate game state, causing the game to begin the intro or go to main menu. From there on, its all clear.

    Also, Tseng, massive thanks for also mentioning what to put in the gameManager and what to keep seperate; I was intending for the gameManager to handle the entire menu system and the game UI previously.


    I'm currently furiously learning C# as so far most Unity tutorials have been to had me start with UnityScript, so lots of fun having to relearn how to code. Sometimes I really wish tutorials for Unity were not so all over the place and focus on more serious project making than what they teach you by default. Oh well, its a growing industry. : P


    @ exiguous: Thank you for that link. It seems I'm also going to use this for enemy AI as well as the menu/gamestate system, so that is very good info there.
     
    Last edited: Apr 9, 2012
  15. Georgesmith12

    Georgesmith12

    Joined:
    Jul 6, 2021
    Posts:
    1
    Well, the post created years ago, when I was just a child now helping me to learn from experts after tuning 22 last day. This makes me love the internet that it helps a lot in learning and clearing your doubts. Hats off to all of those who contributed on this thread. Thanks.