Development

Waiting for Godot

Hey there everyone, just wanted to drop a quick update to let you all know that I spent the last year doing contract work. Finally got that professional Unity developer experience I’d been hankering for, but now I’m back to working on my own projects. Ironically, I’ve fallen in love with the Godot game engine… and I’d say at this point that Sandalfoot is turning into a Godot shop. I’m currently in the process of porting all of our existing titles over to Godot, which is going smoothly. The status of Dungeon Ho!: Unity is “still in development”, although I think the odds of my finishing the existing Unity version before porting the whole thing to Godot are pretty slim. We’ll see how that goes.

Anyway, stay tuned! One of the reasons I moved to Godot is that it’s an infinitely faster workflow, especially for UI. I’m getting a lot more done with less effort, so I’ll definitely have some neat new stuff to show soon.

-Steve

Categories: Development, Godot | Leave a comment

Taking a Detour: the MeinKraft voxel demo

It’s been slow on the job front lately. I’ve been trying to get a position as a Unity programmer, and you’d think it wouldn’t be that difficult with the experience I have. The main problem is, most hiring managers want to see portfolios, and due to the volatile nature of the gaming industry, nearly everything I’ve worked on is either proprietary or no longer available.

So I’ve decided to remedy this. Over the last day or so I’ve been putting together a Minecraft/voxel demo that’ll be right up the alley of all those guys who need to see something concrete. As of bedtime yesterday I got the world mesh building correctly in Unity, take a gander;

And just now I’ve gotten the texture lookups working so I can have block types other than dirt. Fancy! Stay tuned for future updates regarding this demo, and of course more Dungeon Ho!: Unity development news.

-Steve

Categories: Development, Unity | Tags: , | Leave a comment

Dungeon Ho!: Unity – Dev Blog #14

Welcome back a second time, dungeon generators!

Today (again), we’re going to look at the DungeonGenerator class of objects, which are Unity ScriptableObjects and really, really useful.

What is a ScriptableObject?  I’m still not entirely sure.  But near as I can tell, they’re Unity components that basically hold data, and can have scripts attached to them, but are not actually GameObjects and don’t exist in the game world.  Therefore, I decided to use them to generate my dungeons.  Did I need to do this?  Couldn’t I just create a bunch of, say, static C# classes derived from a DungeonGenerator class?

Well, yes; I could have.  One thing that makes ScriptableObjects neat though is that they’re exposed to the Unity editor, and you can store data directly in them.  As you’ll see, this comes in pretty handy.

But first, the basics.

Here’s my original abstract DungeonGenerator class:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;

[CreateAssetMenu(fileName = "DungeonGenerator", menuName = "Dungeon Generator", order = 0)]
public class DungeonGenerator: ScriptableObject
{
    public void generateDungeon(Dungeon dungeon, int level, bool questLevel, int w, int h)
    {
        Debug.Log(string.Format("DungeonGenerator.generateDungeon({0} - {1});", dungeon.getEnvironment().name, level));

        generateDungeonMap(dungeon.getDungeonMap(), w, h);

        populateDungeon(dungeon, level, questLevel);
    }

    public virtual void generateDungeonMap(DungeonMap dungeonMap, int w, int h)
    {
    }

    public virtual void populateDungeon(Dungeon dungeon, int level, bool questLevel)
    {
    }
}

The CreateAsset tag creates options in the Unity Editor’s Assets menu to create a new asset of this type.  It’s kinda useless for the DungeonGenerator object, because, much like a pet platypus, it doesn’t do much.  It’s the subclasses that we really want to use it for.  So why do I have it here?  Truthfully, I don’t know.  Probably just so I can copy-paste it and change the parameters, because I’m an old man and my memory is failing.

Anyhoo, you’ll notice that there are three methods; generateDungeon(). generateDungeonMap(), and populateDungeon().  The Dungeon class, which contains a public variable of type DungeonGenerator, calls the generateDungeon() method to initialize its map variable, which is of course the DungeonMap object we’ve looked at previously (and which, as you notice here, is passed in to the generation method proper).

So, all we have to do in order to create a new DungeonGenerator is to subclass this class (…why isn’t it abstract, then?  It can be, but it’s not 100% necessary I guess), then override the two virtual methods.  We’ll look at that in a second.

Why is this useful?  Well, during development you definitely want the ability to generate different kinds of dungeons.  Currently, I have the DefaultDungeonGenerator, which creates the actual, legit dungeons that are used by the game, a TestDungeonGenerator, which creates a simple, 10×10 map every time (that I mainly use to test basic stuff without being harassed by other Mobs), and a MonsterMashDungeonGenerator, which generates a 10×10 map like the TestDungeonGenerator… but fills it with a passed-in monster type so that I can… test that kind of monster.  There’s a lot of reasons you may want to simply dump an army of kobolds into your map and see what happens, the very least of which is to see if their randomly-generated attributes and gear break under stress.  So it’s very useful to do this.  But, I digress.

So the first thing you need to do is create a member variable in your Dungeon class of type (wait for it) DungeonGenerator.  Then, once you’ve created DungeonGenerator assets in your Unity project, if you want to change the type of DungeonGenerator being used in the game, simply drag the new asset into this field and off you go.  Here’s a screenshot of the assets themselves, after they’ve been created with the Assets->Create command.

DHo ss 1

…and here’s the Dungeon object with the generator asset assigned via the script’s public variable.

DHo ss 2

(We may look at that startRevealed boolean another time; for now just note that it’s another debugging tool that alters the visibility of the map, which lets me not have to explore the entire thing to get a bird’s eye view of the proceedings.  Handy!)

Now, as I said, the TestDungeonGenerator simply generates a basic “box” with entrances and exits, so we’re not going to do much with that one.  We’ll close out this post by taking a quick look at the MonsterMashDungeonGenerator’s ability to take data.

Here’s the code of the MMDG;

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "MonsterMashDungeonGenerator", menuName = "(Monster Mash) Dungeon Generator", order = 0)]
public class MonsterMashDungeonGenerator : TestDungeonGenerator
{
    public string monsterType;

    public override void populateDungeon(Dungeon dungeon, int level, bool questLevel)
    {
        Monster monster;

        for (int r = 5; r < 10; r += 2)
        {
            for (int c = 3; c <= 7; c += 2)
            {
                monster = ObjectSpawner.spawnMonster(monsterType, 1, false, false);

                monster.setPosition(c, r);

                monster.attachToDungeon(dungeon);
            }
        }      
    }
}

Note the public monsterType variable.  Once you’ve created a MonsterMashDungeonGenerator asset in your Unity Editor, this field will appear in the Inspector window when you click on it.  Each type of Monster in DHo!:U has a “tag” variable defined that denotes its monster type.  eg a Kobold’s tag is “kobold”.  If you pass that type tag into the ObjectSpawner, it spawns a monster of that type.  How?  Magic.  But seriously, we’ll look at the ObjectSpawner in more depth in a future blog, because it’s both very complex and very simple.  I’m quite happy with it.

Anyway, once you assign the MMDG asset with a monster type set to the Dungeon as indicated above, a box map filled with that kind of monster will be generated in the game, and there you go.  And the map itself?  Well, note that MMDG is a child class of TestDungeonGenerator, and I did not override the parent generateDungeon() method.  This means that it will simply generate the same map that a TestDungeon does.

Being able to swap out DungeonGenerators is so useful it simply can’t be repeated enough.  Hopefully you find this little trick relevant for your projects, either now or in the future.  Tune in next time when I go over the algorithm used to create the mazes used in the game.

-Steve

Categories: Development, Dungeon Ho!, Unity | Leave a comment

Dungeon Ho!: Unity – Dev Blog #13

Welcome back, dungeon generators!  Once again I’ve been too busy actually developing to stop and create new blog entries… but I have good news.  That being, that Dungeon Ho!: Unity is about to enter what I call the “core systems playtesting” stage.  This is where I make a standalone build of the game, hand it off to some willing friends, and watch them utterly break everything I’ve labored to build over the last, uh… amount of time that I don’t really want to ponder.

That’s actually a good question, though; how long has DHo!:U been in development?  Well, the astute among you will notice that I began this dev blog roughly two years ago.  Unfortunately, I haven’t been working on the game straight through all of this time; I’ve been adding bits and pieces to the core engine when time has permitted, documenting the features as I added them in this blog.  (And you can see a rough timeline of how that’s gone by looking at the post dates.)

The good news is, since this great nation of ours decided it was a crime to set foot in your front yard I’ve had plenty of time to focus on building the game, and development progress has far outstripped the rate at which I’ve been blogging, since as you can imagine given the choice between implementing features or testing the game and blogging about implementing features, I’d rather be pushing forward and doing the former.

So, today we’re going to take a detour from looking at the inventory management UI and dig into the first of the next two topics I want to cover, which people will find a lot more interesting than how to hook up a Unity event; dungeon generation.  Dungeon Ho! uses a fairly straightforward but really cool random dungeon generator that does way more than simply connect squares together like a lot of Roguelikes, but it doesn’t go crazy and use far-out generation procedures involving noise or natural terrain or anything of that nature, balancing atop the line between “simple” and “complex” while still managing to look really cool.

Before I go into that though, I want to touch in more depth on the topic of DungeonGenerators.  I alluded to this back when I first began developing the dungeon generation code, but didn’t really say much about it in any great detail.  In recent days it’s been an absolutely invaluable tool for both development and testing, though, so I want to do a bit of a deep-dive and show you just what I’m on about.  So, bop on over to the next blog entry after this one and we’ll take a look.

-Steve

Categories: Development, Dungeon Ho!, Unity | Leave a comment

A moment of reflection

Hello everyone,

Dungeon Ho!: Unity has been in development for long enough that I’ve got a solid prototype working, and since one of the primary focuses (foci?) of the project has been refactoring and redesigning, I thought I’d take a moment to talk about the two biggest time-savers I’ve managed to implement in the rewrite.

Using the platform’s native UI framework

This is a huge one.  HUGE.  I’ll say it louder for the people in the back; IT’S HUGE.

When I first wrote Dungeon Ho! for the Android platform, I didn’t know anything about layouts or its native input controls.  All I could do was blit to a SurfaceView, and given that my background was in J2ME development, which is both Java-based and requires you to do that, I knew that would be enough.  I was used to rolling my own UI components; I knew how to make buttons click and dropdowns… drop down.  I could scoll my own text, even; and I could be assured that it’d all work as I expected and I wouldn’t see some last-minute framerate drop because you can’t overlay, say, a FrameLayout over your SurfaceView because reasons.

That was the idea, anyway; go with what you know to keep development time short.  And I think in the long run it was a good idea, because DHo! still took close to two years to finish.  Imagine how long it’d have taken if I had to learn Android’s UI stuff on top of it.

Now, I’ve gone the other way.  I learned Unity’s UI/canvas system before I started working on the port and all of the UI controls are done “the Unity way”, and it’s saving me a shit-ton of work.  All of that event handling code, the animation code, the mouse-clickthrough code, the math and resizing code, all of it is already done for me.  Adding a new screen is as easy as drag-and-dropping a new canvas and tossing some buttons on it.  I did the entire start-game flow (…with placeholder art, natch) in little under an hour.  That would have taken me a day or two, the old way.

So in short, know your tools.  You have no idea how much it helps, no matter how good you are in other ways.

Data, data, everywhere

Roguelikes are procedurally-generated, and DHo! probably moreso than most, these days.  Everything is created in code, from the maps to the monsters’ stats to the items; there’s no entry in a list that says “Potion of healing: heals 25hp”.  The code does it all; generates a base potion, randomly figures out what effects it has when drunk, and then names it accordingly.  This makes for a very, very varied playthrough experience.

It also makes for a fucking huge, complex codebase.

I’ve already touched on this when I wrote about the monster definitions; yes, you can follow the same process to copy-paste code templates and rename variables to create a new Monster, but why would you?  Just define a JSON file with the monster’s parameters (eg. “Kobolds range from 50 to 100 pounds”) and let a generic routine parse that data and figure it out.  It’s so much simpler to add an entry to a generic Item file that says “Daggers can be made of wood or metal” than to have to remember which chunks of code to copy and modify to say “if this item is a dagger, make it out of wood or metal”.

Why didn’t I do this the first time?  I think for some reason I assumed that if I defined data files I’d not have the flexibility I had in procedural generation.  Also, and this goes back to knowing your tools, I didn’t know how to handle text files, especially JSON, in Android… and didn’t think it was worth researching.  I will never do that again.

Well, there you have it; the two biggest lessons I’ve learned in porting this project so far.  The new codebase is much cleaner and more flexible than the old, and I’ll be very careful to make sure I don’t sacrifice this going forward.  I will also never not take the time to do proper research on a feature and cut corners in my personal projects again.  After all, I’m not on a deadline.

Tune in next time when we return to coding, with the promised dungeon generation classes.

-Steve

Categories: Development, Dungeon Ho! | Leave a comment

Dungeon Ho!: Unity – Art Blog #2

Good news, everyone!  I’ve completed the initial revamp of the random dungeon generation algorithm, complete with more detailed tilesets.  Here’s a screenshot of it in action;

DHU ss1

The next dev blog will detour from the usual wibbling on about inventory pickups and delve into the process I used here.  Stay tuned.

-Steve

Categories: Artwork, Development, Dungeon Ho!, Uncategorized, Unity | Leave a comment

Dungeon Ho!: Unity – Livestream update

o hai.  Do you… like to watch?

I’ve started livestreaming the development of Dungeon Ho!: Unity on Twitch.  In theory, I am live from 11am to 4pm weekdays, Pacific Standard Time.  In reality, I’m not very good at keeping that schedule and tend to roll in anywhere from 11-12:30.  If the coding process interests you to the extent that you would love to watch it unfold in realtime, come visit my channel at https://www.twitch.tv/sandalfootproductions/ – and be sure to Follow me so you can get notified of such things relevant to your interests.

-Steve

Categories: Development, Dungeon Ho!, Uncategorized, Unity | Tags: | Leave a comment

Dungeon Ho!: Unity – Art Blog #1

‘sup.  We’re breaking from our usual content format to give you a sneak peek at the game’s graphics overhaul, which is currently in progress.  The first step is to expand the game’s tileset to allow for a more richly detailed map.  You can see the bulk of the work being done here in the new Tomb/Dungeon environment tiles.

Tombs Tileset

-Steve

Categories: Artwork, Development, Dungeon Ho! | Leave a comment

Dungeon Ho!: Unity – Dev Blog #12

Welcome back, Item Picker-Uppers!  Today we’re finally going to look at the PickUpItemsDialog and how to leverage all of the things we’ve done so far in order to get items from the map into the player’s inventory, and back again.  But first, we need to know how to trigger this sort of thing from a keypress.

Do you remember the DungeonMaster class?  That’s the one that handles player keypresses.  I’ve done a lot of things to this class since we’ve seen it last, but the thing we’re most interested in is the collectInput() method:

// Primary keyboard driver - Queries Input object and puts commands in queue.
    public void collectInput()
    { 
        UIScreen topmostScreen;

        if (uiManager == null)
            uiManager = dungeon.getGame().getUIManager();

        topmostScreen = uiManager.getTopmostUiComponent();

        // Hotkeys
        if (Input.GetKeyDown(KeyCode.P))
            addCommand(new PassCommand(dungeon.getPlayer()));

        // Hotkeys for particular screens ////////////////////////////////////////
        if (topmostScreen != null)
        {
            // We don't want the user to be able to operate stuff "under"
            // any currently-displaying UI components.  IE, they shouldn't
            // be able to move with the character screen up.
            topmostScreen.handleHotkey();

            if (Input.GetKeyDown(KeyCode.Escape))
                topmostScreen.hide();

            return;
        }

        // Screen activation /////////////////////////////////////////////////////
        if (Input.GetKeyDown(KeyCode.I))
            uiManager.showUI(UIManager.INVENTORY_SCREEN);

        if (Input.GetKeyDown(KeyCode.C))
            uiManager.showUI(UIManager.CHARACTER_SHEET);

        if (Input.GetKeyDown(KeyCode.G))
            uiManager.showUI(UIManager.PICK_UP_ITEMS);

        if (Input.GetKeyDown(KeyCode.Q))
            uiManager.showUI(UIManager.QUEST_LOG);

        // Directionals //////////////////////////////////////////////////////////
        if (Input.GetKeyDown(KeyCode.LeftArrow))
            addCommand(new MoveCommand(dungeon.getPlayer(), MoveCommand.WEST));

        if (Input.GetKeyDown(KeyCode.RightArrow))
            addCommand(new MoveCommand(dungeon.getPlayer(), MoveCommand.EAST));

        if (Input.GetKeyDown(KeyCode.UpArrow))
            addCommand(new MoveCommand(dungeon.getPlayer(), MoveCommand.NORTH));

        if (Input.GetKeyDown(KeyCode.DownArrow))
            addCommand(new MoveCommand(dungeon.getPlayer(), MoveCommand.SOUTH));

        // TODO: Read abstract game actions
        // TODO: Read mouse/clicks
    }

Fun stuff!  The first part of the method assigns a reference to the pre-existing UIManager so that we have something to work with, assuming we haven’t already done so, and the next bit determines the topmost UI Canvas so that we can feed it keypresses.  Basically, if there’s a screen already active, it should get all the keys and then exit the method.  If there isn’t a UI Canvas visible, then we can assume a keypress will be one of the player commands… such as picking up an item (…see what I did there?).

As you can see, we’ll trigger the PickUpItemsDialog with the ‘G’ key on the keyboard – and eventually the mouse, when we get that far.  But that’s a post for another time.

If you recall from last time, we can simply call the showUI() method of the UIManager to activate the relevant canvas.  In this case, it’s the UIManager.PICK_UP_ITEMS canvas.

Speaking of which, let’s take a look at it;

DHU 31

I’ll leave it as an exercise to you, the reader, to recreate this dialog in the Unity editor.  It’s basically just three buttons and a ScrollView.  Each element of the ScrollView is a Prefab denoting an entry in the list, which is currently just a Text label.  We’ll look at slot prefabs in more detail once we take a deep dive into the inventory screen.

public class PickUpItemsDialog : UIScreen
{
     public GameObject dungeon;
     public GameObject contents;
     public GameObject slotPrefab;
}

At its base, the PickUpItemsDialog is a UIScreen, the abstract class we looked at last time.  It has public slots for the dungeon, its own contents (the GameObject representing the ScrollView’s content object), and its object Prefab, which – as previously explained – is simply a Label.  The beauty of using a Prefab here is that we can design a new slot object, perhaps with icons or something, and swap it out with no extra effort on our end apart from maybe having to change a line or two of code to not reference the Label if we decided to delete it.  Simply drag and drop the relevant game objects into these slots in the Editor and away we go.

Next up is the overridden method, prepare():

public override void prepare()
{
     Vector2Int playerPos;

     base.prepare();

     playerPos = player.getPosition();

     populateList(dungeon.GetComponent<Dungeon>().getAllEntitiesOnTile(playerPos.x, playerPos.y));
}

We’ll need the player’s position, so we grab it after we call the base prepare() method (so that we have a player to grab it from, among other things).  Then we populate the list of items based on the objects we get from the dungeon object.  (Since the dungeon is a GameObject, we get a reference to its Dungeon component first, since that’s where all of the data is.)

private void populateList(List<Entity> entsOnTile)
{
     clearcontents();

     if (entsOnTile != null)
     {
          foreach (Entity entity in entsOnTile)
          {
               if (entity != player)
                    createEntitySlot(entity).transform.SetParent(contents.transform);
          }
     }
}

private void clearcontents()
{
     foreach (Transform text in contents.transform)
     {
          GameObject.Destroy(text.gameObject);
     }
}

populateList() populates the contents object with slot prefabs based on the items passed into the method.  First it removes all of the existing items from the list, then creates a new Slot prefab for each item and attaches it by parenting it to the contents object.  The slot prefab is created in the createEntitySlot() method, which spawns a new instance of the Slot prefab and sets its text to the name property of the passed-in item.

private GameObject createEntitySlot(Entity item)
{
     GameObject slot = Instantiate(slotPrefab);

     PickupSlot slotComponent = slot.GetComponent<PickupSlot>();

     slot.name = slot.name.Replace("(Clone)", "");

     slotComponent.pickupDialog = this;

     slotComponent.setItem(item);

     slot.GetComponentInChildren<Text>().text = item.name;

     return slot;
}

Each SlotComponent (…which we’ll look at next) contains a reference to the dialog, as well as the Item object it’s attached to.  We need both of these objects so that it can manipulate the dialog and tell the PickUpCommand which item it should run on when the user clicks the relevant button.

Speaking of the PickupSlot Prefab object, it has the following script attached;

public class PickupSlot : MonoBehaviour
{
     private Color unselectedColor = Color.black;
     private Color selectedColor = Color.green;

     private Entity item;

     private bool selected;

     public PickUpItemsDialog pickupDialog;

     public void setItem(Entity i)
     {
          item = i;
     }

     public Entity getItem()
     {
          return item;
     }

     public void toggleSelected()
     {
          selected = !selected;

          if (selected)
               gameObject.GetComponentInChildren<Text>().color = selectedColor;
          else
               gameObject.GetComponentInChildren<Text>().color = unselectedColor;
     }

     public bool isSelected()
     {
          return selected;
     }
}

The most interesting method is the toggleSelected() method, which changes the color of the Prefab based on whether or not it’s selected.  When the user clicks on an item in the Pickup dialog, we want to highlight it/de-highlight it so that it’s included (or not) in the items that are to be picked up.  Therefore, each slot has a boolean that determines if it’s been selected or not.

So, how does all of that work?  Well, it requires an Event Trigger.  We define one and then attach it to the PickupSlot prefab, like so;

public class PickUpItemEventTriggerHandler : EventTrigger
{
     public override void OnPointerClick(PointerEventData data)
     {
          PickupSlot slot = gameObject.GetComponent<PickupSlot>();

          slot.pickupDialog.onItemSelected(slot);
     }
}

The OnPointerClick() method is automagically called when the Prefab is clicked, passing in the Event data of the PointerEvent that was generated by said click.  The event was generated by the gameObject the script is attached to, so we simply grab the PickupSlot script component of that gameObject, then call its attached pickupDialog’s onItemSelected() method, passing in that slot component.  We do this because the dialog may have to do some bookkeeping relevant to itself when the slot is selected, and we don’t want to tangle that code up in here.  Keeps everything neat.

The onItemSelected() method looks like this;

public void onItemSelected(PickupSlot slot)
{
     slot.toggleSelected();
}

And finally, we use the Unity Editor to call either pickUpSelectedItems() or pickUpAllItems() on the PickUpItemsDialog when the relevant button has been clicked.  pickUpAllItems() simply loops through all of the slots attached to the ScrollView and generates a PickUpItemCommand for each one, so we won’t bother to look at it.  We’ll look at pickUpSelectedItems() instead.

public void pickUpSelectedItems()
{
     DungeonMaster dm = dungeon.GetComponent<Dungeon>().getDungeonMaster();

     PickupSlot slot;

     foreach (Transform child in contents.transform)
     {
          slot = child.gameObject.GetComponent<PickupSlot>();

          if (slot.isSelected())
               dm.addCommand(new PickUpItemCommand(player, slot.getItem()));
     }

     hide();
}

As you can see, the foreach loop checks all of the contents’ object’s children to see if their PickupSlot component is selected.  If it is, a new PickUpItemCommand – which we saw way back when we looked at Command objects – is generated with the player as the source and the Item entity linked to the slot as the target.  The DungeonMaster object then processes all of these commands.

Could we have simply added the selected items to a separate list and then looped through that list and picked up all the items?  Sure, we could have.  Whether or not that’s a better solution is left as an exercise to the reader.  Finally, though, we call hide() on the pickupDialog, which hides it from view until we’re ready to pick up more items.

There’s one final note, here; I’ve implemented a maximum length on the player’s command queue, so if there are more items in the tile than slots in the queue, the player will only be able to pick up as many as there are free slots.  So for example, if the queue’s maximum length is 10 and there’s 20 objects in the tile, the player will only be able to pick up 10 items with one click, no matter how many they select in the dialog.

Well, that certainly was a big one! (obligatory “That’s what she said” joke here.) Hope it was worth the wait!  We’ll be using a similar model for other UI dialogs, especially the Inventory screen.  Tune in next time when we do just that, which will give us the ability to drop items back into the dungeon.

– Steve

Categories: Development, Dungeon Ho!, Unity | Leave a comment

Dungeon Ho!: Unity – Dev Blog #11

Welcome back, my long-lost readers!  It’s been way too long.  You’re looking well.  I wish I could say I’ve been hard at work on the ‘Ho!, but that would be a lie.  You know how it is.  Things happen.

Anyway… I’m here now, and that’s all that matters.  When last we met, I promised y’all that we would take a look at the UIManager and its various related classes, which is a convenience that I whipped up in order to manage all of the various menu screens and dialogs needed to make the game work.

But first, let’s talk design.  The UIManager class is basically a collection of methods that tracks, keeps reference to, and handles various functionality – such as hiding and showing – screens and dialogs from within the game.  These various UI components are, as you may well know, Unity Canvas objects with a script attached, appropriately named UIScreen.

Before we carry on with that, I noticed something that’s been happening ever since I started using text blocks to format my code examples.  Specifically, that the left and right angle brackets (< and >) in my code are getting “swallowed” by the HTML parser, and I can’t be assed to go in and correct every single instance of it.  If it affects anything important, like if-then logic, I’ll deal with it.  Otherwise, the bulk of the cases are List object declarations; so if you see something like List myList, assume I’m really trying to say List<Class> myList and roll with it.  You’re smart, you should be able to figure out what the relevant class is supposed to be.  Got it?  Good.  Let’s continue.

In Dungeon Ho! parlance, a UIScreen is basically a Unity Canvas with some additional functionality attached via a script, like so;

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UIScreen : MonoBehaviour {

    protected Player player;

    // Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
		
	}

    // Override this method to prepare the screen for viewing.
    public virtual void prepare()
    {
        if (player == null)
            player = GameObject.Find("Dungeon").GetComponent().getPlayer();
    }

    public void show()
    {
        // Yes, this doesn't make sense - but if the prepare(); method
        // alters the contents of the UI, its positions/dimensions/etc. 
        // won't get re-calculated if it's not active.
        gameObject.SetActive(true);

        prepare();
    }

    public void hide()
    {
        gameObject.SetActive(false);
    }

    public void toggle()
    {
        if (gameObject.activeSelf)
            hide();
        else
            show();
    }

    public bool isShowing()
    {
        return gameObject.activeSelf;
    }

    public virtual bool handleHotkey()
    {
        return false;
    }
}

The most interesting bits of the code are in the prepare() method.  This method is called before the UIScreen is about to be shown, and is where each subclass initializes its UI components with the relevant data, as we’ll see in a future post.  It also makes sure that there’s a reference to the game’s Player object, by explicitly caching a reference to one if it can find it.  (ProTip:  If it can’t find it, you broke the game somehow.  There will always be a player object for it to find.)

Why do we do this, rather than simply go into the Unity UI and drag our Player into the relevant slot?  Because it’s one line of code that does the same thing for every UI component and I can’t be bothered to do it the other way.  Maybe if I had a UI guy wiring up these dialogs for me, I’d care.  Rule #1 of solo projects, it’s okay to cut corners and not always follow “best practice” – which is usually only “best” because engineers love to follow the herd.  Mini-rant over.

Also note that the show() method puts what we just described into practice, by calling the prepare() method after setting the gameObject (IE, the UI component this script is attached to) active.  Why don’t we call prepare() first?  Because one of the first “features” I discovered when (logically) implementing it that way is that Canvases don’t update their UI components when they’re not active, so trying to set the data of, say, a ScrollContainer in the Canvas will simply fail.  (Hey, that’s in the code comments – whaddaya know.  Rule #2 of solo programming, always document this stuff when you find it… especially when you’re old and your memory is going, like I am – and mine is.)

OK, enough about UIScreen.  We’ll see how to actually make one in a future blog post.  Let’s look at UIManager next.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UIManager
{
    public const int INVENTORY_SCREEN   = 0;
    public const int CHARACTER_SHEET    = 1;
    public const int PICK_UP_ITEMS      = 2;
    public const int QUEST_LOG          = 3;

    private List uiComponents;

    public UIManager(GameObject ui)
    {
        uiComponents = new List();

        foreach (Transform canvas in ui.transform)
        {
            uiComponents.Add(canvas.gameObject.GetComponent());
        }

        Debug.Log("UIManager found " + uiComponents.Count + " UI components.");
    }

    public void showUI(int id)
    {
        uiComponents[id].show();
    }

    public void hideAll()
    {
        foreach (UIScreen screen in uiComponents)
        {
            screen.hide();
        }
    }

    public void hideUI(int id)
    {
        uiComponents[id].hide();
    }

    public void toggleUI(int id)
    {
        uiComponents[id].toggle();
    }

    public bool isShowing(int id)
    {
        return uiComponents[id].isShowing();
    }

    public UIScreen getUiComponent(int id)
    {
        return uiComponents[id];
    }

    public UIScreen getTopmostUiComponent()
    {
        UIScreen top = null;

        foreach (UIScreen screen in uiComponents)
        {
            if (screen.isShowing())
                top = screen;
        }

        return top;
    }
}

First thing you’ll notice is a bit of laziness; I loop through the passed-in GameObject (…which is the top-level scene node that I have parented all of my UI canvases to) and grab references to them, putting them into a local List, which is indexed by constants.  (I could also have made those constants Strings and used a Dictionary.  Either-or.)

Now, whenever I want to reference a particular UI component, I just use its index constant.  Like so;

uiManager.hide(UIManager.QUEST_LOG);

…and speaking of, the main Game object instantiates an instance of UIManager in its own Unity-supplied start() method;

uiManager = new UIManager(GameObject.Find("UI").gameObject);

Note that I pass in the GameObject named “UI”, as previously described.

Pretty straightforward, eh?  Tune in next time when we look at the Inventory Screen, how it’s derived from UIScreen, and how it’s wired up to the Player’s inventory.  Once that’s out of the way, we’ll intercept some mouse clicks and be able to pick up and drop items… finally!

– Steve

Categories: Development, Dungeon Ho!, Unity | Leave a comment

Create a free website or blog at WordPress.com.