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




No comments:

Post a Comment

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