Saturday, August 29, 2020

A Look At Scriptable Objects Part 1

The Unity Game Object

Recently, I have been taking a look at Scriptable Objects, a relatively new topic in my Unity learning experience. I wanted to understand them to determine if I should be using them in my game or not.  If you are not familiar with Unity, the engine is built on the concept of the UnityEngine.GameObject, the base class for every entity in a game scene.  By themselves, you can think of them as empty containers.  They can be filled with Components which can be dragged and dropped onto or otherwise attached to game objects to add specific features and functionality, such as animation, sound, physics, and everything else a game object may need.  The most basic game object comes with a single transform component which is responsible for holding the position, rotation, and scale information of the game object in the scene.  Without additional components, the game object is pretty useless except as a sorting item in the scenes object hierarchy.  In fact, it is common to use empty game objects for the sole purpose of organizing all the objects in the scene into parent-child relationships which make managing them easier.  But if they are to be used in the game, they require more components to make them visible and integral until they gain the form and function to become what they are meant to be, whether it be a player, a piece of terrain, an enemy, a building, or a weapon.  When a developer needs to add functionality to a game object that isn't provided in Unity's base component selection list, or if the developer needs to add custom coding to a game object, a C# script can be created and attached as a component.  While scripts can hold any fields, properties, methods, or events the developer decides to code into them, they all have one requirement if they are to serve as a component of a game object.  They must derive from Unity's UnityEngine.MonoBehaviour base class.

Scripts attached to game objects operate within Unity's game cycle, which is one big continuous game loop that runs at the speed of the game's frame-rate.  By operate, I mean each script deriving from MonoBehaviour has base functionality to access events that fire within the game loop.  The most familiar of these are Awake(), Start(), and Update().  The Unity Manual provides a very nice chart which shows the order of execution in the game life cycle.  It's worth studying.

Static Verses State Data

Every game needs to store defining information about each game object.  A sword might have a strength property, a length, a hand requirement (single or double-handed) and a minimal damage value.  A shield might have a block or glance rate and a size value compared to other shields.  A piece of food might provide a specific nutritional or healing effect on the player.  This kind of information that defines and differentiates one game object from another is called Static Game Data.  It is different from information like where the sword is stored or how much damage the sword has or who owns the sword.  Any information that is subject to change from its original creation point in the game is known as State Data.  State data is typically saved in a file when the player saves the game so that it can be reloaded with the same state values when game play continues.  But what about static data, that information that defines game objects and never changes?  Where does that get stored?

Storage Methods For Static Data

Game developers utilize several methods for storing static game data.  One method is to simply embed the game object with every property value that defines it.  For example, lets say you have an NPC enemy in your game.  The script that is attached to your game object might have the following properties:

private int enemyId;

private float maxHealth;

public float health;

public float attackValue;

public float attackBonus;

public float defenseValue;

public float defenseBonus;

Let's say health, attackBonus and defenseBonus are state data that gets saved and loaded in the game's save file for each enemy NPC.  Everything else is static game data.  Having the other static properties on the game object's script is not necessarily wrong.  But is it the best approach?  What if there were 10 enemies, or 100, or 10,000 with each one hauling around their static game data properties?  That would seem wasteful.  Instead of holding that information on every game object, what if we held it in a single location but made it accessible for any enemy of the same type?

The Singleton Pattern

One solution for offloading the extra weight of static game data is to store that data in a class that is only instantiated one time when the game loads.  This methodology is known as the Singleton pattern.  To make a class a Singleton, it is constructed in this manner:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class MyNPCManager : MonoBehaviour
{
        public static MyNPCManager Instance { get; private set; }

        private void Awake()
        {
            if (Instance == null)
            {
                Instance = this;
                DontDestroyOnLoad(gameObject);
            }
            else
            {
                Destroy(gameObject);
            }
        }

        //lets store some game data here
        public float MaxHealth = 100f;
}

To use this pattern, create an empty game object in your scene and attach the script to it.  Then, move the game object up towards the top of your object hierarchy above any game objects that would reference it.  This class can then have a single set of properties defined for the enemy NPC which could be accessed from any game object script.  Supposing we now have the property MaxHealth in the Singleton class and we needed to make sure a heal did not go over that value.  We could check this by accessing the instance of the Singleton.

if(myNPC.Health + HealAmount > MyNPCManager.Instance.MaxHealth)

This allows the MaxHealth to be stored once so that every instance of the NPC does not have to tote that value around.

I am currently using the Singleton pattern in several places in my game.  For example, all of the map tile static properties are held in a list of tiles generated in a Singleton class.  The definitions for my trees are also similarly held in a Singleton class as a list of flora.  While each tree game object isn't holding all the information about itself, the Singleton is still holding all the information for every single type of tree in the game.  But what if I had a map with only 3 types of trees in it?  The extra tree information would still be held in the Singleton, taking up memory.  Also, each time I access information about a single tree, I have to query the list of trees, locate the one I am searching for, and return the information I need from it.  I will mention another side-note about storing my data in a Singleton.  I cannot change the data at runtime.  If I want a tree to spawn 5 logs instead of 4 when it is chopped down, I have to edit the script manually.  Is there yet a better way to manage static data?  Unity says, "Yes there is."

Scriptable Objects

I guess that was a lot of rambling to get to today's topic, but I felt I needed to present you with a valid reason for talking about this somewhat-new-to-me Unity feature.  So, what is a Scriptable Object and how are they different from game objects?  Well, the better question to ask is how are they different from UnityEngine.MonoBehaviour?  "Scriptable" kind of gives it away that scripts are involved.  And I already mentioned that all scripts added as components to game objects MUST be derived from MonoBehaviour.  Therefore, scriptable objects are not script components of game objects.  They are in fact objects unto themselves that may be accessed in a scene but by attaching them to a game object component.

I am definitely not yet an expert on the subject as I have only just recently started learning about them.  But my immediate take-away thoughts were these.  First, as I mentioned, you cannot add a scriptable object as a component of a game object.  This totally threw me when I first read about that.  I thought, Where in the world do they go if they do not go on a game object?  Everything in Unity revolves around the game object and its components, right?  That was my thinking.  But then when I started thinking of scriptable objects as scripts (i.e. c# classes) that operate outside of game objects (i.e. I don't have to attach them to anything to use them), the smoke began to clear.

Here is where things get interesting.  Scriptable Objects basically derive from the same base as MonoBehaviours!  The main difference is that you MUST attach MonoBehaviours to game objects to use them, while you MUST NOT attach Scriptable Objects to game objects to use them.  One relies on game objects; the other does not.  But since they both have a common ancestry, they are both equally recognizable in the Unity engine space.  Here's another interesting fact.  Since scriptable objects are not attached to game objects, they DO NOT participate in the game cycle the way MonoBehaviours do and therefore do not consume frame time.  

Now let's pause and think about this, because an obvious question might start to rise up in your mind when you think about these things called Scriptable Objects.  How are they different from plain old C# classes?  I am glad you asked.  Here is the answer as I understand it.  Plain C# classes are not part of the Unity "object" universe.  They don't appear in the Inspector.  You cannot tweak their property values at runtime.  But scriptable objects are part of the Unity universe.  They do appear in the inspector when you select them, and they can be tweaked at runtime as well as design time.  You can use them as plain c# classes (derived from ScriptableObject of course).  But you can also work with them and modify values.  Those modifications are maintained when you exit play mode.  This is fantastic when you think that you can test values at runtime and the values will be retained when you exit play mode as opposed to manually editing script properties after leaving play mode.  Also, because they do not participate in the game loop, they never take up overhead until you need them.  This makes them an ideal location for static Game Data.

Sound interesting?  Yes, it does to me as well which is why I am preparing to replace much of my Singleton data stores with scriptable objects.  I have run long with this post, so I will have to bring you a part two sometime soon.  Meanwhile, I will be working to implement scriptable objects into my game and will certainly have some things to say about the refactoring that lies ahead of me and what I have learned from it.  

Until next time...

Sunday, August 16, 2020

Trees and Context Menus


Recently, I was working on the right click context menu for trees and realized I needed to do something different.  Up to that point I had two colliders on the tree objects.  The first was a Capsule Collider 2D.  This game component allows collision detection between it and the Rigidbody 2D component that sits on the player object.  When they touch an event is fired which allows me to change the tree sprite's alpha or transparency channel from solid to something almost invisible.  You can see the effect here.

Tree Fade

You can imagine how important tree fading is going to be when the player is stuck in a thick forest trying to avoid hostile animals, or in a swamp trying to make sure not to step on a quicksand tile hidden beneath some moss.

The second collider was a simple box collider positioned on the bottom portion of the tree.  This collider was used by the physics engine to perform a raycast2D hit check from the point on the screen where the mouse right click event fired down to the tree's box collider.  If the collider hit was a box collider and the object's tag was "tree" I knew a tree had been right clicked and I needed to show the tree menu, which currently looks like this.

Tree Context Menu

This was working fine except for two things.  First, the orange square you see in the image above indicates the selected tile.  It hops from tile to tile following the mouse as it moves across the scene.  However, it did not exist when I was using the box collider.  Instead, I had a mouse pointer that resembled a target.  The second issue had to do with lining up that little mouse pointer just right so the player could select the tree and right click the invisible box collider hoping to score a hit to open the menu.  No, this was not my idea of a mini-game.  It was down right annoying!  Over half the time I missed the collider altogether.  There was a third issue that could be argued as being more important than the previous two.  Physics collision is expensive!  In fact, the more complex the collider type is, the more resources are required by the physics engine to calculate a hit.  This is why I chose a capsule collider instead of a polygon or edge collider for the tree fade check.  Getting back to the task at hand, something had to change.

This got me thinking about the nature of this game and what tools I had to tackle this problem.  Being a tile-based 2D game, I could rely on a few data structures I manage in memory.  Every tree has a corresponding tilemap tile it sits above.  Every tile space is limited to just one tree object.  The tilemap object has built-in methods to get a specific tile based on capturing a screen point (i.e. the mouse click spot) and translating it to a cell on the tilemap.  What made this so great was that I could remove the box collider from the tree prefab, which improves performance!

The new approach looks like this.  In my PlayerMovement.cs class I capture the right mouse click event. I get the click's world position and translate it to a tilemap grid position.  I then get the player's position which is stored in a singleton class.  Singleton classes are classes in C# that can only be instantiated once during the lifetime of the instance.  Once you instantiate them, you call class.Instance() to access them.  I limit interaction with the world to a few tiles around the player.  It wouldn't make sense to chop a tree that was 10 tiles away!

Once it is determined that the player is close enough to the target to perform the action, I needed to check a few data structures to understand what is beneath the mouse click.  I already mentioned there can only be one tree per tile.  But the tile may not have a tree.  It might be an animal, another plant, or a log cabin!  The order of checks then has to be to check for any game object on top of the tile.  Then, if not found check for the top portion of the tile (grass, etc.).  If that doesn't exist, the third and final option is the tile's core.  This allows me to "know" what exists at that position on the tilemap.  From there, it is just a matter of turning on the correct menu and positioning it where I want it to appear.

This refactoring of the code took a few hours but it was totally worth it.  It saved precious physics calculations.  Also, this new method of locating trees is more intuitive for the player.  As long as the user has a visual aid to selecting the desired tile, the player will be able to interact with every game object in a way that feels second-nature.  Check out the video below for a look at the context menu in action.  Until next time...




Saturday, August 15, 2020

The Evolution of Trees

When I started on the web game, I had envisioned trees as being something akin to a tile type.  By that, I mean the trees would be the same size as the tile they occupied.  Here is a screenshot from the web game to show a few types like maple, oak, pine and fir.

Early Trees from the Web Game

When I decided to abandon that project, I also abandoned the idea of everything in the game being limited to a single tile space.  This led me to searching for trees that were larger than a tile and more detailed.  I was surprised to find so many free basic tree images on the internet.  Some were a bit too simple and others were a bit too generic.  I wanted the player to be able to look at a tree and know what kind it was.  Unfortunately, tree images are so prolific on the web that you can find just about the same generic image labeled as a dozen types of trees, none of which are actually represented by the image.    Here are a few decent samples I was able to find.

Early Tree Sources

The trees didn't look bad, but they really didn't feel like the kind of trees I wanted in the Unity game.  Speaking of Unity, there are many great 3D tree assets on the Unity Store, but I am not building a 3D game.  I wanted flat 2D trees that would be pretty easy to identify or at least close enough to the actual species that the player would learn to recognize them.  Another idea I had was to have sprite sheets showing age variations for each tree type.  Here are the cherry and cedar tree sprite sheets.


The more savvy reader might recognize the cedar sprites.  They are from a popular free art asset package prolifically distributed on the web.  I merely resized them and touched up the colors.  I suppose I would have stayed with these sprite sheets were it not for my obsession to detail.  No, these were just not genuine enough for me.  I wanted trees that would stand out and provide a real sense of immersion.  I wanted the player to feel like they were surviving in a real forest.  Then the idea came to me.  What if I could use real tree images within a 2D pixel game?  The idea was just crazy enough for me to want to try it out.  I began looking around the web for free public tree pictures.  They didn't have to be standalone images.  They just had to be public domain and good enough to edit.  Fortunately, I found some excellent source images and proceeded to clean them up for use in the game.  The result was exactly what I was after.  Take a look at two of the trees found in Survive and Thrive.

The Pine and Aspen Trees

And here is an in-game screenshot of the Aspen after adding some additional shading.

Survive And Thrive
Standing Under the Aspen Tree

By using real images as a foundation, I hope to create a look and feel that is very much wilderness in the minds of the player, but still a fun 2D world.  Taking this path was certainly not the easiest.  I spent many hours on each tree, working each pixel cleaning them up and modifying areas that did not look like I wanted.  Some required more work than others.  I believe only one tree has survived in the game that wasn't taken from a real photo.  It was actually a vector image of the type of tree I was looking for.  To date, I have still not found a real image of that tree.  Perhaps I will not find it, but the vector tree serves the purpose for now.  Until next time.


Thursday, August 6, 2020

Delving Deeper Into Tileset Sprites

From my previous posts, you may recall the tiles in the game have top and core sections.  There are nineteen top tilesets in the game at present.  Five are grasses.  The tilesets are the result of many hundreds of hours of tweaks, variations, and texture experiments.  The easy part was choosing the 16-tile pattern. Once that was done, I started researching different grasses and experimenting with different styles.  I did not want just one grass type.  I wanted many different types so that each one would provide different resources in the game.  I researched the real-world and historical uses of the grass types I chose for inspiration.  Of course, the goal is not realism but merely a shadow of it that players can draw from and relate to in their minds.  Let me illustrate with saltgrass (DISTICHLIS SPICATA), a grass that is common in marshes and wetlands.  It grows quite tall.  Saltgrass is also said to have been used medicinally for respiratory allergies by Native Americans.  It also has some usefulness as hay for animals, albeit negligible.  With all those thoughts in mind, here was my original concept.

First Saltgrass Concept

It looks really interesting at first glance.  It seems to have some depth and layer to it.  It looks wild as you would expect it to be.  But when you simulate a portion of the map using the first slice, you begin to notice some obvious short-comings.

First Saltgrass Map Section

First, it is quite apparent the map section is 3 x 3 tiles.  That's not good.  They should blend with less obvious tile edges.  Second, the brightness is way too high.  Think how it would look at night!  Third, the orange colors are not exactly indicative of saltgrass, which is more green and yellow.  I cannot remember why I originally chose orange for this tileset.  Fourth, with so much going on within the tile itself, it doesn't leave much room for the eye to notice other things that might be on the tile, like game objects or wild plants growing on top.  Another negative with this concept is uniformity of the tile edges.  Does that really look like grass or does it look more like paint splatter?  The more I studied my beautiful tileset the more I realized how ugly and impractical it was.  When I started building maps with this tileset, I knew I needed to do something different. It needed to be toned down.  It needed to look more like grass!  During this process I began to realize that grass tilesets looked better when I stopped trying to create differentiating patterns and colors.  So I changed the style, colors, brightness, and the uniform paint splattering into what you see below.

Current Saltgrass Tilesheet

While not perfect by any means, I think the current saltgrass tilesheet is much more fitting the purpose.  The pattern is noisy but not distracting.  The edges are not uniform.  Instead of simulating vertical thickness with a dizzy pattern, I went with deeper-cut edges, and a black outline.  But best of all, it is almost seamless!

Current Saltgrass Map Section

When placed on top of a soil core, the result I was after comes into focus.

Current Saltgrass On Soil Core

I still have so much work to do with the tilesets before I publish the game, but I think my OCD has subsided for the time being, at least with this one.  In my next post I will talk about the trees.  They have gone through even more transformation over time than the tilesets have.  Well, that is all for now.  Thanks for following along in this journey as I work on my Survive And Thrive game.

Monday, August 3, 2020

The Chunking Engine


While testing the map generator with various size worlds, I knew the tilemaps did not need to keep every tile on the map all the time.  Only the visible tiles need to be shown in order to reduce the memory footprint, especially when the maps could be into the millions in tile count.  The arrays that held the map data needed to stay in memory because eventually they will be updated constantly with changes that occur in a "living" world.  Ah, but do they?  When I reach the point in development where I am simulating tree growth, NPC actions, animal migrations and other changes over time, I will have to advance the progression of time-related changes as the chunks are loaded in order to present things as they "are" not as they "were" when the chunk data was last saved to disk.  I look forward to that simulation work.

Getting back to the chunk engine, I needed to develop something that could offload tiles that were not being shown on the screen.  I determined to establish a chunk size setting to allow a player to adjust how small or large the map chunk would be to better align the game with their hardware.  A larger chunk loads tiles less frequently but at the expense of more lag during the load process and more memory consumption.  There are currently three chunk sizes:  20x20, 25x25, and 30x30.  These may be too large, but I will go back and adjust these sizes later when I get to performance testing.

When a map is saved for the first time, each chunk of the map is stored in a single JSON file with the id embedded in the filename.  The player starts in a location on the map. The chunk engine manages the 9 chunks by determining where the player is and checking to make sure that every chunk around the player is loaded.  Taking a 20x20 chunk size into account means that 3600 tiles would be loaded for the top tilemap, and another 3600 would be needed for the core tilemap.  That's a lot of tiles, but Unity seems to be handling it with ease!

9 Chunks Around Player

C0 is the chunk the player is on.  All chunks are loaded in a clockwise order starting with C0 and continuing through the chunks in the order shown.  When a chunk is loaded, the id is stored in a list for reference.  There are two methods that are running simultaneously: a load chunk method and an unload chunk method.  The order of events involved in the load chunk method looks something like this:
  1. Get position of the player on the tilemap.
  2. Check to see if the current chunk id the player is on exists in the list.
  3. If so, skip loading and advance the load direction variable for the next check in the sequence.
  4. If not, load the chunk and store the id in the list.  Other chunk files like flora and game objects are also handled at the same time as the map chunks.
  5. repeat.
That was pretty straightforward.  However, the order of events involved in the unload chunk method are more complicated because there is more to consider and to do to clean up the tilemap.  Loading chunks only loads at most 9 chunks.  But as the player is moving around the map, the potential chunks that must be checked and unloaded increases to 12.  This is because we don't ever want those original 9 chunks around the player to be unloaded.  Therefore, we go to the outer rim of those chunks and check there.  Consider this diagram.



As the player moves around, there is a constant check against the player position and the id of the chunk that is two chunks away from the player's position.  Those need to be saved, then unloaded.  Currently, the check is very precise at exactly 2 chunk sizes away from the player.  This does lead to scenarios where a player could conceivably cause chunks to load and unload in fast repetition.  But I will go back and address this later during performance testing.  I have prepared a silent video which demonstrates the chunk engine at work.  Please note that Pixel Perfect and the camera had not yet been optimized when this video was produced.  The jumpiness seen is mostly due to those factors and not the chunk engine itself.

Chunk Engine Test

Overall, I am very pleased with the performance of the chunk engine.  The loading and unloading happens off screen so the player gets the sense of a seamless world.  This is the goal.  Incidentally, when the player reaches the edge of the map, they currently travel to the edge tiles with the player always in the center of the screen.  In future updates, the cinema camera will allow the player to travel to the edge tiles without the camera revealing the blackness that is beyond the world.