Game
Gnome More War
8 years ago

Unity and the Grid Utility


Like any other game out there, providing an interface to relay instructions and messages to player is a must. This is accomplished by message box pop ups, sometimes accompanied by one or two buttons, depending on the nature of the message. In conventional programming, one only has to create a showMessageBox() function, or a class that encapsulates the function, and call it from anywhere in the game. Presto manifesto, you’ve got a message box.

5d0b78bf40eba.png

Confirm prompt from Final Fantasy 8

Unfortunately, such task is a bit different in Unity, which is the engine I’m using to develop Gnome More War. Unity supports static classes and functions, which makes for the global methods that I can access anytime and from any scene. The main problem however is the message box user interface.

For those unfamiliar with Unity, it sets up the project by using scenes, and a scene is populated with game objects. Game objects are complex entities that can encapsulate sprites, scripts, audio, etc. Basically, if I want something displayed on the screen, it must be contained in a game object that must be loaded in the scene first. I’m not going to go through the basics of Unity here, as I’m assuming that the reader has at least intermediate experience with the engine. If you’d like to get familiarized with Unity, I suggest you start with these tutorial videos:

https://unity3d.com/learn
https://www.youtube.com/user/Brackeys

For those already familiar with Unity, you already know of the two simplest ways of getting a message box to pop up: 1. Create a message box object in the scene, hidden by default, and simply set it to active when needed, or 2. Create a message box prefab, instantiate it in a scene that needs it, destroy if no longer needed. There are more ways to it, but the simplest approach is basically to have a copy or instance of a message box game object for every scene. It may be fine if you’ve only got one scene, or if your message box will not appear in any other scene except one, but that’s not the case for my game.

I wanted a single instance of my message box object to be accessible from any of my game’s scenes. I wanted a simple, one liner code that commands my message box to show itself and display whatever message I tell it to.

So I wrote my MessageBox class. I’m not going to go into the details of the class, as I would have to explain its components and its ties to the rest of my game…which can get us off topic real fast. Just imagine that I have a class ready for use, with all the attached components. What’s important is how I managed to make this class (and the game object it is attached to) global and accessible from any scene. I used some concept known as a Grid.

Take note that this isn’t something I invented— it’s a long standing concept used in Unity to create singleton-like Monobehaviors.

		
			/*
  Grid - is a standard utility for all projects. It is the only practical way to have 'game manager MonoBehaviours' in Unity:

              Grid.scoring.GiveBonus()
              Grid.soundEffects.Explosion(13)
              Grid.ai.GuessHand()

  It's that simple.

  How to use:

  (0) This file Grid.cs itself is NOT attached to anything - it just sits there in your project

  (i) Of course your project must have a "preload" scene. Every Unity project must have a "preload" scene. 

  (ii) In "preload" make a Gameobject "__holder".  Mark "__holder" as DontDestroyOnLoad

  (iii) Put your "manager" scripts, for example, Scoring.cs, Maps.cs, PayPal.cs on "__holder"

  (v) Just fill-in the example code below for each of your scripts.

  That's it.

  You now access your "singleton-like MonoBehaviours" from anywhere like this ..

             Grid.payPal.CheckBalance();
             Grid.scoring.SaveToCloud();

  That's all there is to it.
  */

using UnityEngine;

static class Grid
{
    public static UIDialogBox dialogBox;

    static Grid()
    {
        GameObject g;

        g = safeFind("_holder");
        dialogBox = (UIDialogBox)SafeComponent( g, "UIDialogBox" );
    }

    /* The following trivial routines just help this script work in a tidy manner. Note that when Grid wakes up, it automatically checks everything is in place. */

    // If desired, you can type Grid.SayHello() 
    // anywhere in the project to confirm it is working
    public static void SayHello()
    {
        Debug.Log("Confirming to developer that the Grid is working fine.");
    }

    private static GameObject safeFind(string s)
    {
        GameObject g = GameObject.Find(s);
        if ( g == null ) BigProblem("The " +s+ " game object is not in this scene. You're stuffed.");
        return g;
    }
    private static Component SafeComponent(GameObject g, string s)
    {
        Component c = g.GetComponent(s);
        if ( c == null ) BigProblem("The " +s+ " component is not there. You're stuffed.");
        return c;
    }
    private static void BigProblem(string error)
    {
        for (int i=10;i>0;--i) Debug.LogError(" >>>>>>>>>>>> Cannot proceed... " +error);
        for (int i=10;i>0;--i) Debug.LogError(" !!! Is it possible you just forgot to launch from scene zero, the preload scene.");
        Debug.Break();
    }
}

//////////////////////////////////////////////////////////////////////////////
		
	

The code above doesn’t have to be attached to any game object in order to work. You will only need to keep it in your project. This Grid class will provide us easy access to any game objects we want to make global throughout the game. One example is me using it to have a message box object that can easily be called from anywhere:

		
			Grid.messageBox.ShowDialog(MessageBox.TYPE.TYPE_CLOSE, "Warning", "Coming soon...");
		
	
5d0b78bfe60ca.png

Message box called from the “War Room” scene

I said earlier that I wanted only a single instance of my message box object, as opposed to multiple instances across every scene. In order to make this possible I actually need one instance of it existing in one of my scenes.

When developing games with Unity, it is common to have a preload scene— this is where you put all singly initialized objects, and is loaded before any of the gameplay scenes. Our Grid should be located in this scene.

5d0b78c0d244a.png

In my case, my preload scene is the splash scene. It’s the first scene of the game, and it doesn’t really do anything else besides show the studio logo and move to the next screen after a few seconds. I created a game object _holder in this scene. This is a persistent game object that will exist across all scenes. In Unity, when a new scene is loaded, all objects of the current scene will be destroyed. Our _holder game object will normally be destroyed once we leave the splash scene. To prevent this, we must mark it with DontDestroyOnLoad().

		
			using UnityEngine;
using System.Collections;

public class DontDestroyOnLoad : MonoBehaviour
{
    void Awake() {
        DontDestroyOnLoad(transform.gameObject);
    }

    // Use this for initialization
    void Start ()
    {

    }

    // Update is called once per frame
    void Update ()
    {

    }
}
		
	

Just attach the above script to the _holder game object and it is guaranteed that this game object will never be destroyed when a new scene is loaded.

5d0b78c16ee1f.png

My _holder game object will contain all the global game objects I have in my game; that includes the message box.

5d0b78c3bfc85.png

“dialog_prompt” is my message box game object

In order to have access to this game object from a script, I have to attach my message box script somewhere. If we follow the existing Grid class, each of these children are accessed as components. There are many ways to go about this, but I chose to attach the scripts to my _holder game object.

5d0b78c45968a.png

“UIDialogBox” is my message box script

The code of UIDialogBox encapsulates the necessary methods for showing and hiding my message box. Since Grid is a static class, it will be initialized on the first call to it. Recall this constructor:

		
			    static Grid()
    {
        GameObject g;

        g = safeFind("_holder");
        dialogBox = (UIDialogBox)SafeComponent( g, "UIDialogBox" );
    }
		
	

So once you tried to access Grid from any other scripts in your project (for the first time):

		
			Grid.dialogBox.ShowDialog(UIDialogBox.TYPE.TYPE_YES_NO, "Confirm", "Unlock map, are you sure?");
		
	

The static constructor will kick in. If you analyse the constructor, you’ll see that it will look for a game object named “_holder” in the current scene. It will be found as long as it exists in the scene, and we can be sure that it will be there since we marked it with DontDestroyOnLoad(). Once a reference is established, the component of name “UIDialogBox”, which is the attached script you see above, will be referenced. The calling script will now have access to the public static object dialogBox.

5d0b78c53953f.png

It’s not overly complicated, and it makes sense. It is also not limited to just message boxes. I myself have included some in-game menus, a loading screen, and more, into the Grid. Just remember that the core idea is to have a single instance of a game object that you plan on calling from many scenes in your project.

You may be wondering what all of this has to do with a “grid”. The concept can be likened to a power grid of sorts, as we are turning persistent game objects on and off (show and hide). In reality, you can name your Grid class whatever you want.

I’d like to give credit to my source— as I have been doing my research on instantiating a prefab from a static function, I came across this:
http://answers.unity3d.com/questions/551297/how-can-i-instantiate-a-prefab-from-a-static-funct.html

Cheers!



0 comments

Loading...

Next up

Concept design for the Dark Forest level. #gamedev

Scouts have reported that gnomes have been sighted marching towards So'leil fishing village! #indiegaming #indiegames #screenshotsaturday #gamedev #2dgames #Pooyan #RetroGaming #retro #retrovibes #gameart

Update 1.1.8c is out now. With this I have optimized the game to take up around 300mb of RAM. Hopefully this eliminates all those ANRs and crashes! Check it out: https://tinyurl.com/2s43rhk8

Did you know that Gnome More War is also on the Nintendo Switch? I partnered with Keybol Games to get the game published. Head over to the e-shop and grab your copy! #Pooyan #indiegame #RetroGaming #nintendoswitch #gnomemorewar

I added an ability called "Confuse Cloud". This causes enemies to get confused and stop attacking you. On max level it will also stop enemies from stealing from your barn. Works great on bosses! #screenshotsaturday

Title Screen v3

I thought I had move on...but Google Play warned me to update my app to support the latest Android version or else it would be taken down. Now I have new gameplay ideas so I decided to make another update. Coming to Gamejolt soon!

For this week's #screenshotsaturday I present Magic Circle Hex. At its max level it has a 5% chance (can be boosted by LUCK) to turn enemies into a chicken. Gnomes will be tricked into picking these up instead of stealing from the barn.

For this week's progress here's a gameplay of the So'leil fishing village map. #gnomemorewar #indiegame #gamedev #screenshotsaturday #retrogaming

Made some adjustments to the boss AI so that the fight is more strategic, less spammy. This is meant to be the first boss encounter. What do you guys think?