Thursday, December 31, 2020

Year One Milestone - A Year In Retrospect

It is hard to believe that I have passed the one year mark in this game development journey.  Poking round my list of backups, I noticed the first one was dated Dec. 16th, 2019.  But the actual start of this Unity project began in November during Thanksgiving week.  For those of you who have not read my first post in this developer blog, the game I am currently working on started out as a web-based project.  I decided to abandoned that project when I reached the inventory system features and realized the game I was imagining would not be feasible running on the technology stack I was using.  This led me to researching an alternative, which you can read about here.  This post is about my thoughts on the first year; a retrospect of sorts.  If you have ever wondered what it is like to develop a game, then permit me to share some thoughts on the subject.

A Gamer and A Game

Computers have always been my fascination since childhood and getting my first Commodore 64 in the 1980's.  I have been an avid gamer for over 35 years.  My first MMO was Meridian59 which came out in 1996.  Being a gamer and making my own game has always sounded like a perfect match.  Maybe you know the feeling of playing your favorite games and thinking to yourself how awesome it would be to make something similar.  Something better!  Perhaps you are like me and have notebooks of ideas you have scratched-down over the years about game designs, systems, rules, etc.  One can become obsessed with this build-a-better-mousetrap mentality, especially a gamer!

Before I move on, I must in good conscience warn you about something that has happened to me.  Making a game has mostly ruined my gaming experiences.  Let me explain.  Ever since I started working on Survive and Thrive in a serious capacity, i.e. the last year, my game time has suffered greatly!  I dare say I have played less time this past year than any year ever.  Here is my problem.  Every time I log into a game and begin playing, I get filled with an overwhelming sense of guilt that I am wasting time because I am not working on my game.  The days of relaxing in my favorite MMO for hours-on-end have all but vanished.  My wife tells me all the time that I should not feel guilty taking a break and enjoying my hobby.  Of course, she is right.  But for some reason the drive to make a game has taken me into directions  that lead me away from gaming.  I use to wonder why game developers seldom spend time playing the games they developed.  I do not have to wonder that any longer.  If you can relate and have wanted to make a game of your own, just remember that such a commitment may overtake every other aspect of your gaming life.  Making a game can ruin your gaming!  Just saying...

One day I realized my real-life job as a software developer had given me some skills that I could possibly put to use to begin forming all those better ideas into a game of my own.  The hurdles for me have always been learning the tools much more so than having the ideas.  I have libraries of ideas and so do many of you!  One day I finally decided to dedicate some real time and energy into using what tools I had to make something.  This was the genesis of the game I am working on today.

A Year of Unity

This past year was not my first year knowing about the Unity game engine.  It has been installed on my computer for many years.  But it was my first year of focused determination to actually learn the engine and use it to implement my game vision.  What has it been like to learn Unity?  Well, to be honest, it has been both enjoyable and frustrating.  It is not uncommon for me to sit down in a development session and have experiences that range from lofty highs watching my game come to life to agonizing lows fighting with quirky bugs and inexplicable crashes.  Just yesterday I posted a very frustrating Unity form post about two or three bugs-of-the-moment that made me want to consider switching game engines, they were so bad!  But then later that same day I downloaded the next update to the release I am using and they seemed to disappear; the bugs I mean...  

Being a software engineer, I am used to these sorts of highs and lows.  It really does come with the job and game development is no different in that regard.  Remember earlier when I said the hurdles for me have always been learning the tools?  Well, Unity is chock full of them and each one is a college course of content to master.  Add to this the third-party assets from the Unity Store where every developer has a different coding style and things can get messy.

There are two things that drove my decision to go with Unity over some other game engines.  For one, C# is the scripting language and I love C#.  Since I work with it every day in my day job, it feels natural to use it in the game's development.  There are some other game engines that provide C# as a scripting option, but I have not yet found one as mature as Unity.  This brings me to my second reason I chose Unity.  It is a mature platform that is growing in popularity and is continually improving in the tools I need to focus on my game.  These are both pluses for me.  However, there are some negatives to using Unity.  For one, at its core Unity is a 3D game engine.  They are working hard to bring in 2D tooling, but it is still a 3D engine.  This means there will be some baggage that my game will have to carry into release.  Another negative in my book is the pace of development.  With so many changes coming out with multiple releases, I tend to think Unity suffers in the areas of testing and QA.  Patches come out practically every week.  I am always nervous about updating my project to the latest version because I have no idea what will break when I do.  All-in-all I have to say that I have grown comfortable with Unity and I am glad I chose it for this game.

A Year of Survive And Thrive

I have only published a few short silent videos of the game.  The reason for this is the fact that most of the development has been under the hood.  There has not been much to showcase.  However, I expect the next year to be very different.  In fact, I plan to publish a few videos on my YouTube channel which will highlight some of the work that has been done in the last month.  But looking back at this year, I have been able to make significant progress on some of the most difficult coding challenges.  Here is a list of first-year milestones.

  • A configurable tilemap engine
    • Custom world design with configurable terrain types, sub-terrain types and flora.
    • Perlin generated with customized parameters and seeding
    • Map previewing with saving/loading of templates
    • Dynamic chunking system
    • Map sizes ranging from thousands to millions of tiles.
    • 18 water, grass, stone, and lava tile types
    • 22 ore, mineral, metal, and gem tile types
    • Saving/Loading of game world
  • Player content
    • WASD, mouse movement systems
    • Walking, running, wading, swimming with associated sounds and animations
    • Chopping of trees with fall animations
    • Status/Progress bars for player actions
  • Flora content
    • 5 coniferous tree types
    • 4 fruit tree types
    • 10 deciduous tree types
  • Misc. Systems
    • Intro screen and Custom World screens
    • Game screen with window bar
    • Draggable windows with edge detection and toggle on/off via window bar
    • Clock, Food/Water, Player Inventory, Hand Crafting, Info windows
    • Hunger/Thirst/Nutrition/Hydration systems
    • Game clock with Day/Night cycle and associated ambient sounds
    • Beginnings of the crafting system with the first 100 inventory icons completed.
I am sure I am missing one or two things, but this about sums up the first year list of features.  It is very satisfying to be able to learn the Unity game engine sufficiently to be able to produce this list of features within the first year.  I hope that next year will see an explosion of new content, videos, blog posts and more as I work to bring this game to life.  Thanks for following along on my journey.

Until next time...  

Monday, December 21, 2020

A New PC And New Game Features

 A New PC

It has been quite a while since I posted an update on Survive And Thrive.  The game is progressing well, but I had to do a pause in order to take care of something that comes around about every eight to ten years for me.  I had reached the point where it was time to build a new PC!  This one was my fourth build.  I decided November was a good time to do it seeing how holiday sales were abundant.  I had determined to wait until Black Friday to ensure I was getting the best deals for the components.  That didn't happen though.

I started as I usually do by establishing a price target for this build.  When it comes to computers,  I have to set myself a price target, especially when building one.  There is always a cheaper option and there is always a more expensive one.  If I do not set a target, I will spend forever talking myself up to the next level for each component until the build is out of reach.  I have found over the years that a $2000 build is going to get me to that sweet spot in the technology curve where price and performance become balanced enough to take me forward another 8 to 10 years.  This one came in a little above that number but not by much.

A build requires the following components.  A case, a power supply, a motherboard, a processor, heat sink, memory modules, hard drives, an optical drive, a video monitor or two, and now more than ever, a video card.  Of course, an operating system is also needed.  I opted to keep using my existing keyboard and mouse.  The keyboard is a Logitech G710+ mechanical and I just love it!  A mouse is a mouse and ironically, mine died about three weeks after I finished the build.  In case you are interested, here is what I ended up with:

Case:  Antec Dark Phantom DP502 FLUX Mid Tower

Power Supply:  EVGA SuperNOVA 850 GA 80 Plus GOLD modular

Motherboard:  MSI MEG X570 UNIFY AM4 AMD

Processor: AMD Ryzen 7 3800XT 8-core, 16-thread Unlocked 4.38 Ghz.

Processor Heat Sink:  Noctua NH-U12S SE-AM4

Memory:  Corsair Vengeance RGB Pro 32GB 288 pin DDR4 3200

Hard Drives:  Samsung 970 EVO Plus SSD 1tb M.2 NVMe (primary), Samsung 970 EVO SSD 500gb  M.2 NVMe (development), Samsung 860 EVO SSD 1tb (gaming), Seagate ST3100 standard 1tb (archive) for 3.5tb total storage.  I pulled the latter two older drives from my old machine, which were not part of the build price.  I also dropped an optical drive into the build for OS install and also to provide capability to install older software.

Monitor:  GIGABYTE G27Q 27" 144hz 1440P IPS panel.  I also borrowed one of my existing monitors to use as a second.

Video Card:  MSI GeForce GTX 1660 TI PCI Express 3.0 GDDR6 6gig

Operating System:  Windows 10 Pro 64-bit

I had these all picked out and ready to purchase once I noticed them go on sale.  However, as I mentioned above, I had to modify my purchase strategy a bit.  A few weeks before Black Friday, things started to disappear from NewEgg and Amazon!  Some of the components went out of stock and others were backordered.  As I watched each day, I decided it was better to get what I wanted than to wait for a sale and have them snatched up from me.  Once they were back in stock, I did not hesitate to go ahead and get them.

Over the course of two weeks I was able to purchase all the components. I finished the build just before Thanksgiving.  I expect this new machine to carry me for the next 8-10 years.  The only part that I typically have to upgrade is the video card during the life of the PC.  I expect this one to be no different.  Once I had completed the new build and migrated my data over from the old PC, it was back to work on the game.

Game Time Simulation

I wrote a rudimentary day and night cycle quite a while back.  The Main Camera has a game object with a Light 2D (Experimental) Global light type component attached to it.  The only other light in my sandbox scene is one attached to the player prefab.  I fondly called it the Newbie Light since I plan to disable it at a certain difficulty level and/or after a certain amount of play time.  This is a survival game after-all.  It is also a Light 2D (Experimental) light but it is a Point light.

I had several goals for a Game Time Simulation (GTS).  Many survival games use the concept of survival days rather than a typical calendar approach.  This decision is subject to change, but for now I want to do the same.  Simple enough, the game begins on Day 1.  Another goal was being able to control the game speed during certain events.  For example, sleeping will occur at an advanced rate.  Crafting items will also cause the game time to advance at a much faster rate.  The GTS can be set to one of fifteen speeds.  A setting of 1 will progress the game clock at the normal rate and 15 will speed up that rate 15X when necessary.  Different events will advance the clock at different rates.  Another configurable option for a custom game will be to select sunrise and sunset times.  The defaults for a standard game are sunset beginning at 6PM and sunrise at 6AM.

I wanted the clock to be presented as a simple string value in the game UI without using C# DateTime types.  Seconds are really not shown and I know from my day job as a software engineer that DateTime calculations are costly compared to just building a string value.  It is simple enough to simulate.  The following method takes in the calculated hour and minute values from the GTS with the hours ranging from 0 to 23.  The method returns a nicely formatted 12 hour time string with AM and PM designations.  

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
private string FormatGameTime(int hour, int minute)
{
	if (hour == 0)
		return $"{hour + 12}:{minute:00} AM";
	else if (hour < 12)
		return $"{hour}:{minute:00} AM";
	else if(hour == 12)
		return $"{hour}:{minute:00} PM";
	else
		return $"{hour - 12}:{minute:00} PM";
}

Since the GTS is built on the 24 hour variation by default, building that string is a single line of code.

1
DateTimeDisplay.text = $"Day: {day} Time: {hour:00}:{minute:00}";

Day-Night Cycle

The GTS gave some sense of purpose to my existing day and night cycle once I had completed refactoring to accommodate the clock display. The sunrise and sunset events at this stage only involve incrementally adjusting the sun light's intensity along with the Newbie light's intensity in a smooth transition from one light source to the other.  During testing, I discovered that activating and deactivating the 2D light sources in the scene after they reached their polar intensities caused major issues with the game's framerate.  It was so different that daytime clock ticks were visibly slower than nighttime.  At first I did not know why.  After some time working through this, I was able to realize a stabilized framerate by simply leaving both lights set to active but adjusting the intensity to 0f to simulate turning off rather than actually setting them to disable.  I never realized how much lighting impacts a scene!  I know it is lighting because unchecking the object in the scene is the only thing necessary to produce the issue. Plus, I have reference objects on the script, so my code is not doing any kind of tag searching to locate them.  In fact, to date, I believe I have not used a single GameObject.FindGameObjectsWithTag method.  Searching thousands of objects in the scene is  not to be considered a best-practice in my book.

Tree Shadows

With light sources changing, I thought it would be appropriate to introduce some shadows.  Survive And Thrive is mostly a 2D, orthographic, oblique-perspective game, but I wanted to explore some ways to alter that perspective just a little to add a sense of one more dimension.  Swapping z-axis values on the player and the tree on the player's tile is one way to present a less flat game world and allows the player to walk in front and behind trees.  Another is the use of shadows.  While digging around Unity's API, I discovered the URP 2D lighting system comes with a component that somewhat simulates shadows.  It is called the Shadow Caster 2D (Experimental) component.  I decided to experiment with it.  

Shadow Caster 2D (Experimental)

Please excuse the baked shadows of this tree.  If you have been reading along, you know I took photos of real trees to use for the game trees.  I didn't bother removing them. While configuring the shadow Shape (the white square in the image above), it was only necessary to make it the same width as the tree trunk and roughly a square.  It simulates the shadow being cast from the base of the tree in a quite basic way.  The shadows are cast upon the tilemap tiles when the player's Newbie Light gets close.  The shadow stretches opposite the player's position to the tree and into infinity.  I wish it were more like the shape of the tree, but it will have to do for now unless I find the time to write something custom or find a product on the Asset Store that works better.  I will leave you with a screenshot of the night shadows and a sneak-peek at some of the new features I am currently working on.

Tree Shadows

Until next time...

Sunday, October 25, 2020

Sound Randomizations and Asset Store Considerations

In my last post I discussed the sound effects that have been added when the character is running, walking, wading, swimming, and treading.  When animations are only a second or so in length, sounds can get very repetitive.  Using a Unity Store product to manage sounds does save time.  However, I couldn't figure out how to implement a certain feature I wanted.  Since the sounds were spawned from a script attached to a Mechanim state, they only randomized when state changed.  For example, when using WASD to maneuver the character in water, the sound randomization features of the product I purchased only performed the randomization when the state of the animation switched from say, swimming to idle or vice-versa.  They would not change until the player state was different.  I wrote the developer to ask if there was something to be done to fire the randomization event within the current state's animation. He politely responded that I would have to write my own behavior for that.  So, that is exactly what I did.

State Machine Sound Controller

Digging around, I found I could attach a script to the state if it inherited from StateMachineBehaviour.  This would enable me to attach my new script in place of the store asset's script which was controlling the sounds.  There are three events that are necessary to override in order to manage the randomizing of sounds mid-state.  Take a look at my new script.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
using UnityEngine;

public class STStateMachineSoundController : StateMachineBehaviour
    {        
        public AudioClip[] AudioClipArray;
        private AudioSource _audioSource;
        private bool _isVariety;
        private int _loopCount;

        private void Awake()
        {      
            //assign _audioSource
            _audioSource = STGameManager.Instance.PlayerObject.GetComponent<AudioSource>();
            //determine if variety will be used
            _isVariety = AudioClipArray.Length > 1;
            //initialize loopCount
            _loopCount = 0;
        }

        public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {
            //This is a bit counter-intuitive to stop the sound OnStateEnter.
            //But this ensures the previous state's sound stops playing and doesn't overlap events.
            //This state's sound will get picked up on the first OnStateUpdate
            _loopCount = 0;
            _audioSource.Stop();
        }

        public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {                        
            if (!_audioSource.isPlaying)
            {
                PlayClip();                
            }                        
            if (_isVariety)
            {
                //only change the current variety at the end of the current animation loop
                //get the integral portion of the normalizedTime
                int thisLoop = Mathf.FloorToInt(stateInfo.normalizedTime);
                float progress = stateInfo.normalizedTime;
                //get the decimal portion of normalizedTime;
                progress = Mathf.Repeat(progress, 1.0f);
                //at the end of the current state's loop
                //select a new random variety clip when the loop cycles
                if (_loopCount != thisLoop && progress >= .99f)
                {
                    _loopCount++;
                    PlayClip();
                }
            }
        }

        public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {            
            //stop audio in case next state does not use audio
            _audioSource.Stop();
        }

        private void PlayClip()
        {
            if (_isVariety)
            {
                //randomly pick a new clip to play from the array
                int randomClip = Utils.MathHelpers.GetRandomInteger(0, AudioClipArray.Length - 1);
                _audioSource.clip = AudioClipArray[randomClip];
            }
            else
            {
                _audioSource.clip = AudioClipArray[0];
            }
            _audioSource.Play();
        }
    }

When dragging this script onto a Mechanim animation state, the AudioClipArray[] allows me to establish the number of sound variations desired for that particular state.  If there is only to be a single sound, I set the number to 1.  Regardless, all that is needed to make it work is to drag sound clips onto the AudioClipArray Elements in the inspector.  That's it!

The code is heavily commented, but still pretty easy to follow.  The _isVariety bool is used to randomly select from the array list if the list has more than one sound clip in it.  When any particular state is entered, I found it best to stop the audio source from playing an existing sound.  I introduced a loop counter to help ensure a complete cycle before changing to a different sound clip.  The Mathf.Repeat() method is very nice for getting the decimal portion of the state's elapsed time.  When the decimal portiono is >= .99f it means the animation loop is almost completed.  I only wanted the sound to change at the end of the loop. 

There are two additional things I might need to mention for any new developers.  First, the _audioSource assignment in the Awake() simply identifies the game object in my scene that has the  necessary AudioSource component attached.  And second, the Utils.MathHelpers.GetRandomInteger method is part of my own Utils class that contains many helper methods I have written to use in my game.  Simply replace that with something like the following to generate a random selection between 0 and the number of sounds in the AudioClipArray[].

1
2
Random r = new Random();
int rInt = r.Next(0, AudioClipArray.Length - 1);

I dragged this script onto all my movement Mechanim states, set the number of sound clips for each one, and dragged the appropriate clips onto each script's elements.  This allowed me to remove the store product's script altogether.  It's fast, easy to configure and use, and most importantly, I know what the code does.  

Asset Store Considerations

When purchasing Asset Store packages for use in my game, I keep the following considerations in mind.  First, using someone else's code means I am introducing unfamiliar code into my game.  I don't have time to read every line of code in every Asset Store purchase I use in my game.  I have to accept this as a risk.  There could be bugs in that code.  There could be added fluff (parts I will not be using) in the code that may bog down my game.  There could also be future updates to that code which may break my code after it is updated.  Of course, all developers must understand that every library we are "using" brings with it a certain level of trust we place in the developers who made them.  But I think the most important thing to consider is being willing to step back and write something ourselves if the need arises.  This is why learning C# is so important for Unity developers.  Not everything can be purchased from the Unity Store.  And while, yes, there are non-programming tools such as Bolt that can assist people in creating games, there is a whole lot of satisfaction in knowing you have the knowledge and skills to venture into the coding world yourself, if and when the need arises.  

I will hold off on a video until I have a little more to show you.  As you can see, I am in the very early stages of development of this game of mine.  I appreciate you following along.  I hope something from my experiences will help other developers get inspired.  Until next time...

Sunday, October 11, 2020

Simulating Water Depth With Animations and Sounds

The past few weeks of development work have been focused around simulating tile depth for watery tiles.  Since the game is meant to be a survival game, it seemed only fitting the player's character should be able to wade into and even swim in water.  For this, I needed to create a few systems for establishing water depth, character sprite masking to hide parts of the character that are submerged, animation and speed transitions, and all of the proper sounds to complete the sensory experience.

Tile Depth

Establishing a depth value for water tiles was the first step.  The map is generated from Perlin noise, with each tile being assigned a terrain type based on the weight of each type compared to the other terrain types and the desired order (fixed or random) based on the player's selections.  I added a new float property to my class that stores tile information.  Since all tiles have an X and Y position, I thought it proper to name the depth variable using the letter Z.

During the map creation process, all top tiles are initialized with a Z value of 0.  Once the entire map has been created from the Perlin noise values, and after all special tiles have been cluster-spawned onto their respective tile types, the tile array is passed to a method to establish tile depth for types brackish water, salt water, fresh water, swamp, and marsh.  The process of setting the Z depth requires obtaining information about every adjacent tile to the target tile.  Consider this small map of tiles.

Tile Depth

The green tiles are land and the blue tiles are water.  The technique I used was to make several passes through the tilemap array for each tile type that needed depth.  The first pass of each tile type only checks to see if any adjacent tiles do not match the target tile.  If any tile surrounding the target tile doesn't match, it means the target tile is next to land.  Consider the blue tiles marked with a 1.  They all have at least one adjacent tile that is land.  Each of these target tiles get a depth of 1.  In the next pass through the array, all of the tiles with a Z value of 0 that have at least one adjacent tile with a value of 1 get assigned a Z value of 2.  The next pass checks and sets for 3, then 4 and so on.  The last pass, or the pass with the highest value, checks for its own value and sets any remaining 0's to the same value.  This essentially spreads out the remaining tiles to be the deepest.

Sprite Mask Positioning

Next, I added a sprite mask (the orange box below) to my character prefab (his name is Boomer), setting all of the sprites that make up the character to only be visible outside of the mask.  When Boomer is on land, the mask sits beneath him, out of view.  But as he descends into liquid tiles, based on the Z value of the tile he is one, the sprite mask is adjusted up to cover his lower body.

Boomer and His Sprite Mask

In the player movement script, the Z value of the tile Boomer is on is checked.  The transform of the sprite mask is then adjusted based on that value.  Here is what it looks like when Boomer is in deep water, just before it is deep enough for him to start swimming.

Boomer Wading Out into a River

Animations and Sounds

Sometimes it is prudent and even necessary to reinvent the wheel.  But most of the time it isn't.  I have relied on several third-party assets from the Unity Store, and I can say that I am mostly pleased with what I have purchased and used so far.  Boomer is a scruffy kind of survivalist that I created using Character Creator 2D.  I really have no hesitation plugging for this great asset.  The developer is extremely interested in customer feedback and satisfaction.  He has helped me several times and even implemented suggestions I have made.  That said, the animations provided out-of-the-box have been great for my development, saving me precious time and effort.  My animation controller was very easy to setup with dozens of included animations for Boomer.  

The sound system I have been using is also from the Unity Store.  It is called Master Audio.  The product seems to be much more than I could possibly use in this project, and the learning curve is practically a U-Turn.  That said, it is a solid product and I do recommend it.  One thing I have utilized for this part of the project is a script that enables me to link a sound with an animation.  The Mechanim State Sounds component script from Master Audio can be added directly to an animation.  Once configured, I was able to play the appropriate sound effect for each of the animations: slow walk, walk, run, swim, idle swim.  I believe this step took me the longest to figure out.

With depth established for tiles, it was very easy to adjust the speed of the character's movement based on tile depth.  Wading out into deep water slows the character's movement, but once swimming the speed is increased.  Eventually, speed will also be adjusted by character skill, health, age, etc.

These few dev tasks took me much longer than I expected.  It was especially frustrating to try and sync animations with sounds.  When I finally got the bright idea to trim the sounds to be the same length of time as the animations, everything began to line up.  Overall, I am satisfied with the results enough to move on to other parts of the game.  

My wife watches me work on this and she made a comment to me the other day that game development looks like hard work.  As always, she is absolutely right.  Feel free to have a look at the video below where I demonstrate all of these features in game.



Until next time...

Sunday, September 20, 2020

Tile Workflow and Sub-Distributions

I have been rather busy this week in my professional work, so I have not spent as much time working on the game as I would have liked.  However, I did manage to tweak a few things and add a few new features to the tile engine that make things a little more interesting.  For starters, I was able to change up my spritesheet workflow to be more productive.  Take a look at this grass tilesheet.

Buffalo Grass

Spritesheet Workflow

I use a plugin tool in Paint.NET named Outline Object to create the black edges of each tile.  But due to the way in which the tool works, the only way to get the edges correct was to copy each single tile into another image, run the tool on it, and then copy and paste it back onto my original tilesheet.  I decided it was time to create a template image that would allow me to stamp the entire tilesheet at once - saving a ton of manual work.  There are essentially four steps involved in making or changing one of my top-level tilesheets.  First, I start with a base texture (top-left square).  Once I am happy with the look, I create an empty image that can hold the 16 variations of my texture.  Since my tiles are 100x100 pixels, the tilesheet image is 400x400 pixels.  Next, I duplicate my base texture to fill the first row, then duplicate that row to fill in the remaining three rows.  When that is done, I can really see whether the texture is tiling properly.  The less I can notice the edges of each tile, the better!  Then, I use a 400x400 pixel template image to stamp in the transparent edges of the tilesheet (all the parts in white above).  Finally, I stamp in all of the black edges to create a sense of elevation to the edges of the grass.  This new process is much faster than before.  Introducing the new tilesheet into the game is also now very easy thanks to the Scriptable Object changes I have recently made.  Feel free to read my previous posts if you are interested in Scriptable Objects.

Custom World Terrain Tab

Cluster-Spawned Special Tiles

Next on my list of changes this week was the distribution of the Special Tiles.  I use a Perlin noise algorithm to generate the game map.  But the Perlin engine only distributes the first two columns of tile types you see in the Terrain tab above.  The special tiles were intended to be distributed within the other tiles based on some hard-coded relationships between the regular tiles and the special tiles.  For example, Moss only spawns within certain grasses.  The result of the original distribution was not very organic, just a random Moss tile here and there.  I decided to write a method to distribute the special tiles in clusters rather than sprinkled around the map.  The cluster logic basically chooses a random number of cluster spawns and then determines how many tiles each cluster should have based on the total tile count of all possible compatible tiles on the map.  A random spot on the map is then checked to see if the special tile can spawn at the location, and if so, that tile becomes the spawn anchor point.  Another method randomly determines the placement of the next adjacent tile, resetting the spawn point, and the loop continues.  Notice in the next screenshot how the Brackish water has spawned clustered  together without looking like a simple circle or rectangle.

A Brackish Water Cluster

Once the spawn-point is checked, the method doesn't care what type the next tile is unless it is an edge-of-the-map tile, then it switches to another random 8-direction choice for the next tile.  This allows the special tiles to cross-over into other adjacent tile types creating a very nice organic feel.

Shuffling Distribution

Notice in the second screenshot above that some of the tile types are named in green.  I thought it would be more interesting to allow some of the tile types to be shuffled in order when they are distributed based on their Perlin values.  By checking the box, the green tile types will have their order on the map shuffled randomly.  For anyone interested, I created a list of tile-type id's and populated the list with the tile type id's in their original order.  To shuffle, I pass the list into an extension method which shuffles the order of the integers and returns the list in a random shuffled order. Then, looping through the random list allowed the distribution to be shuffled.  Here is an extension method which does the shuffling.  By the way, this method can shuffle any List<T>, not just integers.  I am sure I will use it in the future to randomize NPC names, and many other things.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public static void Shuffle<T>(this IList<T> list)
        {  
            var rand = new System.Random();          
            int n = list.Count;
            while (n > 1)
            {
                n--;
                int k = rand.Next(n + 1);
                T value = list[k];
                list[k] = list[n];
                list[n] = value;
            }
        }

The following is a small map generated in the original un-shuffled order.  Then, the next map is one where the shuffle has been turned on.  Notice how the tile types have changed in order.  This creates some very nice differences between maps generated from the exact same Perlin data.  

Un-Shuffled Map

Shuffled Map
The keen eye will notice subtle differences between the distribution of tiles in the two maps.  This isn't a bug.  Each tile type has a weight value (the sliders on the Custom World screen).  The weights determine how much of the map each tile type takes. It is my goal to provide a way to re-generate the exact same map terrain given a seed value and the weight values from the map builder.  Now, I can add to this the shuffle list included in the map template.  This will permit creating identical maps given these values.  Well, that is all for now.  Until next time...

Sunday, September 6, 2020

A Look At Scriptable Objects Part 2

In my last post, I introduced my recent journey into Unity's Scriptable Objects.  Since then I have been working to convert the tree objects and the tile engine to use them in place of Singleton classes that store tree and tile data in static lists.  I have now completed parts of this refactoring and am quite impressed with the new workflow, the added benefits, and the surprising performance boost I have seen moving to scriptable objects.  From here onward, I will abbreviate the term Scriptable Object to simply (SO) for brevity.

The Refactoring Process

I decided to refactor my tree game objects first since the tree code was less work than the tile engine. I started by creating an abstract base class which derived from (SO).  All my other (SO) classes derive from my base class.  I used this base class to store properties and fields that were common to all the (SO) classes I would eventually create.  The class needed to be abstract because I had no intention of instantiating this base (SO) class by itself.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
    public abstract class STBaseObject : ScriptableObject
    {
        public string Id { get; set; }
        public string Name;
        public string FriendlyName;
        public STBaseType BaseType;        
        public GameObject DefaultPrefab;
        public GameObject InventoryPrefab;
        public float AdjustedXPos;
        public float AdjustedYPos;
        public int HighestMapLevel;
        public int LowestMapLevel;
        [TextArea(1, 20)]
        public string Description;        
    }    

The next (SO) class was my STTreeObject class.  This class would hold fields and properties that were unique to my trees.  Up to this point, all my tree data was stored in a public class that held the properties for my trees.  This class was accessed by another class which existed to simply to instantiate the list of trees at runtime.  That class was instantiated once as a public property on one of my Singleton classes, which also was only instantiated once.  What I am describing is a Singleton data storage mechanism.  Getting back to the (SO) alternative, have a look at the top portion of the new class showing the CreateAssetMenu attribute.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
[CreateAssetMenu(fileName = "TreeObject", menuName = "SurviveAndThrive/Flora/TreeObject", order = 1)]
    public class STTreeObject : STBaseObject
    {
        public int AssetId;
        public GameObject Sapling;
        public GameObject Ripe;
        public string FellingSound;
        public float FellSoundTimer;
        public STFloraAssetType AssetType;
        public STFloraAssetDistributionType DistributionMethod;
        public STFloraAssetAgeType MinimumAge;
        public STFloraAssetAgeType MaximumAge;
        public STFloraAssetAgeType MaturityAge;
        public STFloraAssetGrowthRateType GrowthRate;
        public STFloraAssetGrowthRateType FlowerRate;
        public STFloraAssetGrowthRateType SeedRate;

The class attribute is important because it allowed me to create a (SO) file in the Unity Editor by creating a menu item for it under Unity's Assets => Create menu.  Notice the fileName is the name of the (SO).  The menuName is the location within the menu structure where the (SO) will appear. 

Scriptable Object Menu

At first, I thought my tree class and (SO) tree class would be basically the same.  However, once I started refactoring, I realized some of the string values I was using to locate my tree objects were no longer needed because I could directly reference them as a GameObject on my (SO) class.  Here is what a new TreeObject looks like from the Inspector.
A New TreeObject

The New Workflow

There is much to be appreciated in the screenshot above.  For one, it is a lot easier to keep track of my tree data using the Project and Inspector tabs in Unity as opposed to scrolling down 1600+ lines of code in the class I was using to store this data before.  In a glance, I can see how many trees I have and I can compare the property settings of each one to the others.  Plus, by selecting a tree (SO) and locking the inspector, I can navigate to my prefabs and drag-and-drop them onto the (SO).  This means I no longer have to load those prefabs from the disk each time I need to instantiate a new tree in the game.  Yes, it is tedious to transfer all the information from the class to the (SO) files, but it is no less work than using the classes.  

The Added Benefits

This brings me to the benefits I have noticed using (SO) files instead of static classes.  Like I mentioned earlier, I was able to switch from storing and using strings in the tree class which held paths and filenames to using hard references in the (SO) file for things like the DefaultPrefab, InventoryPrefab, and Sapling and Ripe variants.  Another amazing, and I do mean AMAZING benefit over static class storage is the fact that all of the tree data can be altered at runtime.  For example, if I need to increase the number of logs that are produced from chopping down a particular type of tree, all I have to do is change the value in the inspector.  The next time I chop down that type of tree, it will produce the altered count of logs automatically.  This is perhaps the best overall benefit I have seen to using (SO) over other methods of storage of static data.

The Performance Boost

Once I had converted to using (SO) for the trees, I proceeded to build out the tile engine to use (SO) as well.  There is a nice method in my MapManager class which creates the preview map in the game.  I chose that as the first step to refactoring the tile engine.  What follows are comparisons between the pre-scriptable-object version of the game and the current using the new (SO) system.  This test involved loading an existing map template and rendering the preview of it.  The map is 1 MILLION tiles, being 1000 x 1000 tiles in size, rendered on a single Unity tilemap.  Notice in the Console the Start and Stop times.  These times captured how long it took to generate the preview map using the old and new systems.

Pre-SO-Test
35 Seconds

I didn't think 35 seconds was all that bad considering the map engine had to generate a million value noise map, distribute the tile types based on the user's chosen weight for each type, and then loop through to actually create the tilemap tiles using a custom rule-tile method.  But I was not really prepared for the results of the (SO) run.

SO Test
23 Seconds

The same template using (SO) files took only 23 seconds to create and render.  Doing the math, the pre-SO test processed 28,571 tiles per second.  The SO test processed 43,478 tiles per second.  That is a whopping 35% increase in performance! Now to be fair, there is a critical optimization that I used for the (SO) refactoring.  With the old classes, I had to build out a file path and filename to the required tile and then instantiate it with a call to Resources.Load<Tile>().  This was necessary simply because of the huge amount of tiles to manage.  But with the (SO) solution, I chose instead to reference all 15 tiles of each terrain type in their respective (SO) files.  This enabled me to reference all of the tile variations in a clean and organized manner. I believe the majority of the performance boost was due to this optimization as loading from a file system is always more costly than from memory.  The Task Manager didn't show significant differences, but here they are. 

Pre-SO-Test

SO Test

I am very happy that I decided to learn about (SO) and how to use them for static game data storage.  There are many other uses for them that I am sure I will discover.  And, I still have quite a bit of refactoring left to do.  The preview map is just a small part of the map engine.  I will likely follow-up this post with a part three to summarize any future findings.  But one thing is sure.  I will definitely be using Scriptable Objects as much as I can.  That real-time tweaking feature is just fantastic!  

Until next time...

[The Next Day UPDATE!]

I have spent pretty much all day working on refactoring the entire map engine to use (SO).  After some debugging, I ran some tests and I want to update this post to show the dramatic results of moving from Singleton class stores with resource loading to (SO) managed data.  I created a new test map and placed a stopwatch on the initial chunk loading, which loads all 9 chunks around the player in the initial load.  Here are the results.

Pre-SO Chunk Load Times

Post-SO Chunk Load Times

The above comparison shows before the (SO) refactoring, my chunk engine loaded the 9 chunks around the player in 2.91 seconds.  After implementing the (SO) solution, the 9 chunks loaded in only .73 seconds or 730 milliseconds!  I noticed also that the (SO) solution was extremely consistent across almost all the chunks after the initial chunk loaded.  Whereas the pre-SO load times are wildly different, ranging from 870 milliseconds down to 15 milliseconds.  Needless to say, I am incredibly impressed with the results of this work.  The (SO) path has been very rewarding thus far.  I will definitely be looking for every opportunity to use Scriptable Objects in as much of my game as possible.  I hope this has been as interesting to you as it has been rewarding for me.  

Until next time...

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.