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...

No comments:

Post a Comment

Feel free to leave a comment. All comments are moderated.