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

Post navigation

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: