My original plan was to have an implementation of A* pathfinding in my character's movement code, but as I started to implement it I realized that something was wrong with how I set up my Tiles. I had set up my Tile class and PhysicalWorld class to both extend MonoBehaviour.
Normally this wouldn't be an issue, but to implement A* I will need to have a separate data class to represent a Tile. This unfortunately wouldn't be as easy as just stripping out the : MonoBehaviour
after my class name, which will remove the inheritance from MonoBehaviour, I instead would have to refactor a good portion of my code.
My plan from here was:
- Remove extension of
MonoBehaviour
from Tile class - Change any references to a Tile using MonoBehaviour functionality.
- Create a new WorldData class to act as a data class for the game world.
- Move TileMap reading from PhysicalWorld to WorldData
- Squash bugs
First Steps
To start myself off all I had to do was go to my Tile class and remove the MonoBehaviour extension from the class name, then wait to see what breaks. I took a bit of the same approach with creating a WorldData class, I created the file and created a simple ReadMap function. Then I cut all of the code in PhysicalWorld's GenerateMap relating to reading the map and placed it in the new function I created.
I passed in the tilemap so that I wouldn't need to keep a reference to it in the data class.
This function grabs the object attached to the passed in tile map that holds the tiles, and loops through the tiles found inside. To try and cut down on the overhead of searching for tiles, I create a Dictionary<string, Tile>
that I can search through to find tiles by name, this dictionary will be the main data store for my tiles. I store the necessary information from the TerrainTile into a new Tile. The new tile is then saved to the dictionary.
This still left me with the issue that the PhysicalWorld needs to generate the graphics based on the tile map. Since the reading of the tile map now happened in the WorldData I had to get the tile information from the data class into the graphics class. Luckily I thought ahead enough to make this fairly easy. Because I already have a dictionary containing all of the tiles I could just return this.
Instead I chose to go with an accessor function GetTiles. This function returns an array of the tiles in the dictionary instead of the actual dictionary. This is because I would like the dictionary to only be edited from within the WorldData class.
The WorldData function that returns an array of all of the tiles.
With this I could get the full list of tiles from the WorldData into the PhysicalWorld. This was how I managed to get the necessary information about the Tiles to BuildGround.
I ended up looping through the array using a foreach
loop. I used each Tile to generate a new Vector3 world position, set the _currTerrainTile to that of the tiles, and then it would call BuildGround and as long as the _currTerrainTile.models field was not null.
Refactored GenerateWorld function
Side-quest !
While editing the TileMap code I decided to try and tackle the issue of why my tile coordinates weren't using ints, they were using floats which looked ugly.
After a little bit of debugging, and a lot of frustration, I finally was able to track down the cause of the bug. The TileMaps pixelToUnit variable was set too high so instead of setting the tiles 1 unit apart, they were getting set a fraction of 1 apart.
So I made it set the pixelsToUnit property to just be the same as the tile width.
Seemed too easy to be true !
This made all of the tile coordinates be 1 apart from each other, but because their pivots were on the center point they also started at 0.5. To fix this I switched the pivots on the sprites to be the top left.
It's a Small World After All
This was able to solve my tile coordinate problem so that my tiles now used _int_s for their positions. This did cause another issue I hadn't thought about though, it ended up changing how the tile texture scaling worked. This meant the tile objects were in the correct location, but the visuals looked like this:
Tiles have to correct spacing, but incorrect texture size.
This issue actually took me a long time to diagnose and eventually solve. At first I thought it might be that the tile sprites weren't set to the correct Pixels Per Unit amount, so I went through and set them all to 16. That didn't have the desired effect though. I spent a couple of days trying to solve this one issue, searching through classes again and again. I followed the textures from the beginning right up until they were drawn and still couldn't find what was causing the issue.
Then as I was looking through the object and it's relationships I noticed that there was a reference set to a sprite that either had escaped my first tirade of changing the Pixels To Unit ratio or had somehow gotten reset. Because it was the sprite the default tile got it's dimensions from it altered the size of all of the tiles to be placed.
Back to the Beginning
With that all that was left was to clean up and comment my code. I did a few sweeps and got rid of errant Debug statements, removed unused code lines, and fixed any minor bugs that popped up while testing the running of the WorldGeneration. After that I ended up back where I started ! Which is exactly where I wanted to be functionality wise.
Until Next Time !
If you have any suggestions for improvements, questions, or links to resources that would help me out feel free to leave a comment. I am using Unity as a game engine and Blender for any 3D modeling I may have to do during this project. Upvote if you liked the post or found it helpful, and follow me for future updates on NaughtScape, sometimes other programming, and occasionally other stuff. All of the source code can be found on my Github.
You could also add me in game on OSRS if you play !
My ign is MapleThunder.