informa
/
Design
Featured Blog

Tutorials, Tips, and Text in Unity

A simple system to designate game events on a per-level basis in Unity. Useful for displaying tutorial pop-ups or other graphics at various times in a particular level.

As I've mentioned before, PuckMania's design goals are constrained by the small development team (currently one person, that would be me), and that "team's" schedule and artistic capabilities.  This will hopefully result in a game that's attractive in a simple, non-flashy way, and fun to play without being terribly complicated. But I'd still like to be able to inject a bit of fun into the proceedings, and the design constraints don't preclude us from including some strategically placed text to spice things up.

We already have a need for some in-level text during the early levels in the form of tutorial text.  Now, people who play a lot of games might groan at the idea of having to endure having their hands held, especially in a simple game like this.  But as game developers, we can't take for granted that everyone who plays our games is as experienced as we are.  This is a casual game, targeted at a casual audience, and we can't make any assumptions about a new player's previous gaming experience, even with similar games.

So we'll have some introductory playing tips in the early levels, and some tips will explain how new obstacles and mechanics work.  However, I do agree these should be as non-intrusive and undistracting as possible; they shouldn't slow down or hold up gameplay in any way whatsoever. We'll do this by having any text or tips appear or disappear from non-intrusive areas of the screen at various times, and will never pause or slow down the game.

Most windows aren't this intrusive, and none of them block user input or steal focus.
Most windows aren't this intrusive, and none of them block user input or steal focus.

The goals are:

  1. A GameObject can be triggered to appear or disappear in response to certain events.  In PuckMania, this GameObject will contain some text and a background, but it could theoretically be anything.
  2. The triggering of these objects won't affect the progress of the game.  No slowing down or pausing.
  3. The GameObjects that appear, and what triggers them, can be specified on a per-level basis, without any level-specific code.

Our levels already have a LevelController component that tracks level-specific items such as bronze, silver, and gold medal criteria, level bounds for a moving camera, and et cetera.  We'll add to this a list of game events that our main routine can check.

    [System.Serializable]
    public class GameEventItem
    {
        public GameEventType Type;
        public int Number;
        public GameEventAction Action;
        public GameObject TargetStandalone;
        public GameObject TargetMobile;
    }

    public GameEventItem[] GameEvents;

    public enum GameEventType
    {
        LevelStart,
        LevelEnd,
        TotalShotsTaken,
        GoalTriggered,
        MouseClicked
    }

    public enum GameEventAction
    {
        ActivateGameObject,
        DeactivateGameObject
    }

Each event is contained in a GameEventItem, which contains the fields:

  • Type, which is what triggers this event.  It is a value of enum GameEventType which has a list of triggers we might be interested in.  Note to self:  I really ought to name this Trigger instead of Type next time I do some refactoring.*
  • Number.  Some of the triggers (such as TotalShotsTaken) will use this as an argument.  Others (such as LevelStart) will ignore it.
  • Action.  This is the action that will be taken when the event is triggered.  PuckMania is only using actions for activating and deactivating GameObjects in GameEventAction, since our needs are pretty simple.  More can be easily added later.
  • TargetStandalone and TargetMobile.  These are the targets of the action.  Originally there was only one, but I realized that I sometimes needed to specify different text for mobile platforms.

The events themselves are stored in a plain array called GameEvents.  Since the GameEventItem class is serialized, we can edit the contents of this array directly in the inspector:

Game Events
On level start, Text1 objects are displayed. When a shot is taken, those are hidden and Text2 is displayed.

Whenever an action happens in the game that we may need to take an action for, we'll call CheckGameEvent to check the list for a match:

    public void CheckGameEvent(LevelController.GameEventType type)
    {
        if (_gameevents != null)
        {
            for (int i = 0; i < _gameevents.Length; i++)
            {
#if UNITY_STANDALONE
                GameObject target = _gameevents[i].TargetStandalone;
#else
                GameObject target = _gameevents[i].TargetMobile;
#endif
                if (_gameevents[i].Type == type)
                {
                    bool takeaction = false;
                    takeaction = takeaction || (type == LevelController.GameEventType.LevelStart || type == LevelController.GameEventType.LevelEnd);
                    takeaction = takeaction || (type == LevelController.GameEventType.MouseClicked && _mouseclickcount == _gameevents[i].Number);
                    takeaction = takeaction || (type == LevelController.GameEventType.TotalShotsTaken && ShotsTaken == _gameevents[i].Number);
                    takeaction = takeaction || (type == LevelController.GameEventType.GoalTriggered && _goalstriggeredcount == _gameevents[i].Number);
                    if (takeaction)
                    {
                        if (_gameevents[i].Action == LevelController.GameEventAction.ActivateGameObject) target.SetActive(true);
                        if (_gameevents[i].Action == LevelController.GameEventAction.DeactivateGameObject) target.SetActive(false);
                    }
                }
            }
        }
    }

Usage of CheckGameEvent is quite simple, you simply call it whenever the appropriate action occurs in the game code.  For example, when the user takes a shot, you trigger it with the TotalShotsTaken event:

ShotsTaken++;
CheckGameEvent(LevelController.GameEventType.TotalShotsTaken);

If the level specifies that a GameObject be activated or deactivated, then it will happen.  Simple, but flexible.

This was implemented early in development to have something simple that worked.  Notice that almost everything done here is straight-up basic C# Unity scripting:  No delegates or level-specific code of any sort, or fancy editor scripts (though we do love those, don't we?)  About the only thing that might trip up a beginner is the requirement of the Serializable attribute. If we want to go back and tighten this up a bit, some possible changes/enhancements to this system might include:

  • Better handling of standalone vs. mobile.  Perhaps go back to only having a single Target field, and have the target itself have child GameObjects tagged for standalone or mobile.
  • A separate method for handling actions.  In this incarnation, we kept the action code inline since handling actions is simple.  But if more complex actions are added later, separating out the action code might be called for.
  • Having some events only occur a certain number of times for each player.  This would allow the very basic tutorial stuff to not show up again if someone returns to a previous level.

Conclusion

That's it, really.  This gives us a simple system for designating and triggering game events that don't require any level-specific code, or any additional trigger objects in the game world itself.  It's very good for responding - in a level-specific way - to user input, level start or end, or any variable such as score or number of moves.

Thanks for reading, and keep checking us out on www.puckmania.com!
-Marc

*This is one of the reasons why I like blogging my work.  Writing about work for an audience can sometimes reveal to yourself little improvements that you can make.

Originally posted at:  http://www.puckmania.com/blog/tutorials-tips-and-text-in-unity/

Latest Jobs

Sucker Punch Productions

Bellevue, Washington
08.27.21
Combat Designer

Xbox Graphics

Redmond, Washington
08.27.21
Senior Software Engineer: GPU Compilers

Insomniac Games

Burbank, California
08.27.21
Systems Designer

Deep Silver Volition

Champaign, Illinois
08.27.21
Senior Environment Artist
More Jobs   

CONNECT WITH US

Register for a
Subscribe to
Follow us

Game Developer Account

Game Developer Newsletter

@gamedevdotcom

Register for a

Game Developer Account

Gain full access to resources (events, white paper, webinars, reports, etc)
Single sign-on to all Informa products

Register
Subscribe to

Game Developer Newsletter

Get daily Game Developer top stories every morning straight into your inbox

Subscribe
Follow us

@gamedevdotcom

Follow us @gamedevdotcom to stay up-to-date with the latest news & insider information about events & more