Gamasutra is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Gamasutra: The Art & Business of Making Gamesspacer
View All     RSS
March 6, 2021
arrowPress Releases







If you enjoy reading this site, you might also want to check out these UBM Tech sites:


 

Tutorials, Tips, and Text in Unity

by Marc Breaux on 10/16/14 01:32:00 pm   Featured Blogs

The following blog post, unless otherwise noted, was written by a member of Gamasutra’s community.
The thoughts and opinions expressed are those of the writer and not Gamasutra or its parent company.

 

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/


Related Jobs

Bitwise Alchemy
Bitwise Alchemy — Austin, Remote, Remote
[03.05.21]

Senior Software Engineer (Remote)
Sucker Punch Productions
Sucker Punch Productions — Bellevue, Washington, United States
[03.05.21]

Audio Programmer
Airship Syndicate
Airship Syndicate — Austin, Texas, United States
[03.05.21]

Senior Gameplay Programmer
Airship Syndicate
Airship Syndicate — Austin, Texas, United States
[03.05.21]

Gameplay Programmer





Loading Comments

loader image