Category Archives: Code Snippets

ST2U2DNavMeshImporter (Oops!… I did it again♪)

For the tilemaps, I’m using Thorbjørn Lindeijer’s Tiled and Sean Barton‘s SuperTiled2Unity. Since I just made a project to bake 2D colliders into NavMeshes, I went ahead and created a SuperTiled2Unity importer that automatically creates a NavMeshSurface with the correct position, rotation, size and all the necessary components.

All that’s left to bake your NavMesh after importing the tilemap is the press of a button!

You can find the project here: https://github.com/SharlatanY/ST2U2DNavMeshImporter

Here’s an example of an imported tilemap. All I did after the import was, to press the “Bake 2D”-button:

The project is published under the MIT license, so, you’re practically allowed to use it in whatever way you wish.

NavMeshSurface2DBaker

Currently, Unity only offers the ability to generate NavMeshes (the objects telling the path finding system where NPCs can move) from 3D obstacles. Since I’m developing a 2D game and would like to make use of Unity’s inbuilt path finding, this posed a problem.

That’s why I developed a solution that worked for my case. When everything worked, I thought to myself that it wouldn’t take too much additional effort to polish this a little, make it more multi purpose (e.g. I only needed it to detect polygon colliders and had no personal need for also handling circle colliders, etc.) and easy enough to use so it might be useful to other people as well.

What I ended up with is a solution to easily bake 2D colliders into a NavMeshSurface.

Supported colliders:

  • BoxCollider2D
  • CircleCollider2D
  • PolygonCollider2D
  • CompositeCollider2D
  • TilemapCollider2D (For those to work, you have to make them part of a CompositeCollider2D, though!)

You can find the end result here: https://github.com/SharlatanY/NavMeshSurface2DBaker

Here’s an example of a baked NavMesh for a hex tilemap (red tiles have a collider):

The project is published under the MIT license, so, you’re practically allowed to use it in whatever way you wish.

Unfortunately, as is often the case, I was very, very wrong about the effort it would take to make the project more useful to others as well.
Getting it to work for my specific use case took about a day of work, making it more general purpose, easier to use ,cleaning up the code, setting up the GitHub repository, adding instructions and a project example, writing this blog post, etc. took at least two more full days of work.

Now, I’m torn about how I should handle such things in the future.

On one hand, I’m really very happy to have made something that might be of use to others (I’ve seen people looking for exactly this functionality multiple times) and to give something back to the community. After all, there’s lot’s of people out there that helped me out in one way or another and even if it’s not always possible to pay them back, this feels like a way to at least pay it forward.
On the other hand, polishing and releasing this made the task more than 3 times as long as it would have been if I just stopped at the point where it was good enough for my own purposes.

If we’re honest, the project I’m trying to create is way out of scope and a monumental task that will still take me years to finish.
I only work on this in my spare time and when I feel like it because I know I’ll never finish it as soon as I have the feeling that I must work on it.
Currently, I’m on vacation and make quite good progress. But during the rest of the year, I maybe get to work on this project somewhere between 1 and 2 full days a week (likely closer to 1..) on average. Which means that making the NavMeshSurface2DBaker presentable and releasing it cost me about half a month of normal (=non vacation) development time.
With all the things that I already know still have to be done and the rate at which I discover new things that will also have to be done, I’m not really sure if I can afford and justify such escapades.

 

Code Snippets #2: Not Quiet So Simple Pooling Manager

A while back, I posted my simple pooling manager. Unfortunately, it turned out to be too simple for me.
It didn’t cover a common use case I have: requesting objects but not using them immediately. At the core of the problem was the fact that the pools relied upon if the object was active or not to determine if it was in use or could be handed out by the pool. When handing out the object, the pool then set the object to active so it wouldn’t be handed out twice incidentally.

This however presented a problem in a common use case like this:

  • A ship fires its cannons.
  • Since the ship has 20 cannons, I need 20 cannonballs.
  • To add some more realism, not all cannons are fired exactly at once.
  • However, it is way more effective to get all 20 cannonballs in one call to the cannonball pool and not make 20 separate calls for one ball each.
  • Since the pool needs to set objects to “active” when handing them out, the cannonballs immediately became visible. That was ok for those that were initialized and fired directly. But those that weren’t fired in the same frame as the cannonballs were requested, just spawned in the place they were at when they were when they were put back into the pool/disable the last time. They just floated there, motionless, until the frame they were fired in came up, then they were teleported to the correct spot and started moving/were shot.
  • To prevent that problem, I had to write a lot of code that was triggered on initialization and also when the objects were disabled (turning colliders, sprite renderers, etc. on and off). It was a lot of work, prone to errors and something that needed to be done separately for every kind of pooled object that wasn’t immediately used.

That’s why I wrote a new pooling system that doesn’t rely upon if an object is active or not to determine if it can be handed out or is currently in use. That way, objects can be handed out inactive by the pool.
Since there are major changes, I decided against silently updating the old post and to write this new one. That way, people interested in using a simpler pooling manager or comparing the two, will be able to do so.

Here’s my new version of the pooling system:

Class to manage multiple pools:

Class representing a pool:

Class that is attached to pooled objects by the pool. This meta info helps the pooling manager to return objects to the correct pool:

Remarks

  • In my old pooling manager post, I mentioned that it was a lot more efficient to fetch n objects in one call than to fetch 1 object n times.
    While it’s still more effective to get all objects in one call, the differences in performance in this new pooling manager are a lot smaller
  • In some places the code looks a bit inelegant (using for instead of foreach, saving the amount of objects in a list to a variable instead of using Count(), etc.
    This design decisions are intentional. I did quite a bit of profiling and came to the conclusion that writing the code this way leads to much better performance when dealing with a lot of objects.

Code Snippets #1: Simple Pooling Manager

Warning

I wrote a new pooling manager in the mean time and recommend using that. This first version won’t be improved upon anymore. However, if you just need a very simple pooling manager, feel free to use it anyway. There’s one small bug (documented further down) but that should be easy to fix. If you have problems doing so, contact me anytime. I’ll be happy to help!

Original Post

I was in need of a simple pooling manager and doing some research, I stumbled upon this helpful blog post by Dave Crook. It wasn’t exactly what I was looking for but a good starting point which inspired me to do my own thing. I’ll share it here and you can feel free to use it in any way you see fit. It still some a features one might need but for now does everything I need it to do. I’m pretty sure I’ll make some adjustments in the future and should that happen, I’ll update the code here. So, you should probably subscribe to the blog 😉

Class to manage multiple object pools:

Class representing an object pool:

Remarks

  • In some places the code looks a bit inelegant (using for instead of foreach, saving the amount of objects in a list to a variable instead of using Count(), etc.
    This design decisions are intentional. I did quite a bit of profiling and came to the conclusion that writing the code this way leads to much better performance when dealing with a lot of objects.
  • If you need a large number of objects from the pool, it is advised to fetch them in one go with GetObjects(int) instead of calling GetObject() multiple times.
    I can’t say anything about the performance scaling (I would have to do more tests for that). But as a small test I fetched 2400 objects from the pool, one time all at once with GetObjects(int) and once I fetched each of them individually in a for loop. Fetching them all at once took about 19.8ms, fetching every one of them separately took 877.5ms.
  • There’s a bug when detecting that a pool object was accidentally destroyed: In that case, a new object is created and returned but the destroyed object isn’t removed from the pool. The bug is also documented in the code but I won’t fix it because I rewrote the pooling manager anyway.

Code Snippets #0: Circular Sectors

When doing research for programmatically creating the circular sectors used for the cannon firing range, I encountered a few instances of other people having questions about this topic. So, I thought it might hopefully prove useful to others to share the code I wrote to solve this problem.

I mainly used an enum and two functions, one to calculate the points that define the circular sector and one that takes those points and generates a mesh from them.

Enum:

Function to calculate points defining the defining points of a circular vector:

Function to generate a mesh from those points:

Making things work in an arbitrary plane

As the code is right now, it only works with circular sectors that lie on the plane defined by the x- and y-axis (z -axis component of 0). There are mainly two reasons for this:

  1. I need the points defining the circular sector not only for mesh generation but also for generating a 2DPolygonColider, which expects the collider path to be defined in by a series of Vector2 values. To avoid unnecessary data/space translations and data sanity checks, it was easiest to work with Vector2 to begin with
  2. A more generic approach would cause input computation/conversion to be a little more complex which in turn means more room for errors. And if we can avoid that without any drawbacks, why not?

However, it would be very easy to make this code more generic so it works with arbitrary planes. If you are in need of that and don’t know how to go about it, feel free to drop me a message in the comments.

Words of caution

This code isn’t meant to be the solution to this problem. I’m certain there are more efficient ways to get the same results if you have a deeper knowledge of trigonometry, linear algebra, etc.
But for me, it’s good enough. It’s not the worst way you could solve it and more importantly readable to me. It’s good enough until it isn’t and should that moment ever come, I’ll change it then. I’m repeating myself but”Done is better than perfect” 😉

So, please, apply critical thinking when using this code and keep in mind that there are certainly other and probably more elegant solutions to this problem.