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

Unity Scripting an interactive game tutorial

Discussion in 'Immediate Mode GUI (IMGUI)' started by Kthieu, Apr 18, 2014.

  1. Kthieu

    Kthieu

    Joined:
    Apr 9, 2014
    Posts:
    2
    Hello. I'm new to Unity and working on a game project with my group. My job is to script an interactive game tutorial. What is the first step in doing so? Right now I have a GUI.Button that jumps to a scene with the game itself. I want to use this to make an interactive tutorial where text bubbles or labels appear to instruct the player what to do, and allow them to do it until they are ready to proceed to the next lesson. I hope people understand my intention here. I tried looking around but the keyword involved show basic tutorials that don't help with what I'm trying to accomplish. A small first step or a push in a direction will help. Thanks.
     
  2. _met44

    _met44

    Joined:
    Jun 1, 2013
    Posts:
    633
    Hi, if you need your tutorial system to control the game state, it'll be easier if your game already has the proper archictecture with for example and event manager that the tutorial can use to lock the state until the player performs a given action.

    Once you have that (or don't need it), -unless your GUI/game is completely frozen and nothing never changes position and scale, and nor does resolution- you must consider how you will look up the position of items you want to emphasis at each step of the tutorial.

    If you use the built in GUI, you might need to access the rects of the elements, and if you use another GUI system like NGUI, you need to reference which widget to target.

    You now have done half of the work, the rest depends on how you want your tutorial to behave, do you want/need it to be linear or not ? In both case you'll end up with something looking like a state machine, with each state behing a step of the tutorial.
     
  3. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,670
    _met44's great advice is spot on. Since you're getting up to speed in Unity, I thought I might briefly sketch out one way to implement _met44's suggestion. This is just one of many approaches you could take.

    Create a folder named Tutorial to contain all the files described below.

    Create a GameObject named Tutorial. If you're going to change scenes during the tutorial, use DontDestroyOnLoad() to prevent the GameObject from being destroyed when changing scenes.

    Let's say you're doing a fantasy RTS tutorial with three steps:

    1. Gather lumber
    2. Build mill
    3. Train archer

    Each step will be a state in the state machine. When a state is done, the state machine can transition to the next state. Each state will be a MonoBehaviour (script) on the Tutorial GameObject.

    For simplicity, let's assume there's another GameObject named GameInfo with a MonoBehaviour named PlayerInfo that keeps track of how much lumber has been gathered, etc.

    Create the state scripts, based off of a parent class named TutorialState. These should all be in separate .cs files in the Tutorial folder, but I'm including them in one block here for simplicity:
    Code (csharp):
    1.  
    2. //=== TutorialState.cs:
    3. using UnityEngine;
    4.  
    5. public class TutorialState : MonoBehaviour {
    6.  
    7.     protected PlayerInfo playerInfo;
    8.  
    9.     void Start() {
    10.         playerInfo = GameObject.Find("GameInfo").GetComponent<PlayerInfo>(); // null-checking omitted for clarity
    11.     }
    12.  
    13.     void GotoState<T>() {
    14.         gameObject.AddComponent<T>(); // Add the new state.
    15.         Destroy(this); // Remove this state.
    16.     }
    17. }
    18.  
    19. //============================
    20. //=== GatherLumberTutorialState.cs:
    21.  
    22. public class GatherLumberTutorialState : TutorialState {
    23.  
    24.     void Update() {
    25.         if (playerInfo.lumber >= 50) {
    26.             GotoState<BuildMillTutorialState>();
    27.         }
    28.     }
    29.  
    30.     void OnGUI() {
    31.         GUI.Label(new Rect(0,0,200,50), "Gather 50 lumber");
    32.     }
    33. }
    34.  
    35. //============================
    36. //=== BuildMillTutorialState.cs:
    37.  
    38. public class BuildMillTutorialState : TutorialState {
    39.  
    40.     void Update() {
    41.         if (playerInfo.millsBuilt >= 1) {
    42.             GotoState<TrainArcherTutorialState>();
    43.         }
    44.     }
    45.  
    46.     void OnGUI() {
    47.         GUI.Label(new Rect(0,0,200,50), "Build a mill");
    48.     }
    49. }
    50.  
    51. //============================
    52. //=== TrainArcherTutorialState.cs:
    53.  
    54. public class TrainArcherState : TutorialState {
    55.  
    56.     void Update() {
    57.         if (playerInfo.archers >= 1) {
    58.             Destroy(this); // end tutorial.
    59.         }
    60.     }
    61.  
    62.     void OnGUI() {
    63.         GUI.Label(new Rect(0,0,200,50), "Train an archer");
    64.     }
    65. }
    66.  
    Add the first state, GatherLumberTutorialState.cs, to the Tutorial GameObject.

    When GatherLumberTutorialState finishes, it will destroy itself and add the next state. This way, only one state will be on the GameObject at a time.

    The states use the Update() method, which is called every frame, to check whether the requirements have been met. They use OnGUI() to draw the tutorial instructions. Start() is called before the first frame (if the script is enabled). You can read more about execution order here.

    If you need to time things more finely, you can add more states or use coroutines.
     
  4. Kthieu

    Kthieu

    Joined:
    Apr 9, 2014
    Posts:
    2
    Thank you both for the head start in the right direction. I have worked with state machines before in a different project so this should be relatively tame.

    Is it possible to have a linear progress in terms of GUI.labels in OnGUI()? This could very well be implemented using "redundant" states but I want to ask if if its possible, upon an action in Update() being satisfied, that OnGUI() will present a different label while in the same script. A head on approach would be booleans, but that might seem to get nasty later on.
     
  5. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,670
    Enums instead of Booleans would give you more options than just true/false:
    Code (csharp):
    1.  
    2. enum LumberMillStage { LayFoundation, BuildWalls, BuildRoof }
    3.  
    But ultimately it can get just as nasty as Booleans.

    I'd suggest sub-state machines.
    Code (csharp):
    1.  
    2. public CompoundTutorialState : TutorialState {
    3.  
    4.     TutorialState substate;
    5.    
    6.     void Start() {
    7.         substate = AddComponent<LayFoundationState>();
    8.     }
    9.  
    10.     void Update() {
    11.         // Check if substate is done; if so, go to next substate.
    12.         // When all substates are done, go to next state.
    13.     }
    14. }
    15.  
    It can go through the substates sequentially until they're all done, at which point the compound tutorial state is done. The substates would be responsible for implementing OnGUI. I glossed over Update(). It would require some reworking of the code in my previous post. Maybe you'd want to put all tutorial states on the GameObject at design time and just activate the one(s) that are supposed to be active at any given time. (Use MonoBehaviour.enabled = true/false.) This way, you could also keep track of substates in an array:
    Code (csharp):
    1.  
    2. public CompoundTutorialState : TutorialState {
    3.     TutorialState[] substates;
    4.     int currentSubstateIndex;
    5.     ...
    6.  
    So this lets you create a hierarchy of FSMs. (But not a hierarchical FSM, if you want to get picky with terminology.) You could even implement your overarching tutorial as a single CompoundTutorialState, with the major stages as substates. Each of the major states could in turn be CompoundTutorialStates, and so on.
     
  6. _met44

    _met44

    Joined:
    Jun 1, 2013
    Posts:
    633
    For a linear tutorial you could easily have a tutorial stage data holding class, with a text string and a rect of screen coordinates, then a list of all the stages in your tutorial manager class.

    From there all you'd have to do is to move the cursor forward as the player fulfils a stage (for example when he clicks within the given coordinates ;) and your OnGUI() method would display the tutorial stage data held at the current position in the list, and some sort of visual emphasis arround the screen position you have for that stage.

    It doesn't get simpler than that... (except maybe for a pdf manual xd)