2D in Unity, or Hobbling a Giant

Note: Most of this write-up from mid-2011 is now largely irrelevant. Unity is introducing a new 2D-specific engine (rumor is that it’s Box2D) in Unity 4.3, which is currently in closed beta. For more information, see our “Unite 2013″ conference post-mortem.

Our last game, Brainarang, was built using cocos2d, which is an awesome open-source 2D game engine for iOS. As happy as we were with it, we couldn’t resist the siren song of Unity. Unity is AMAZING. If you don’t know about it and are interested in game development, stop reading this and go bend your knee in fealty right now.

But for all its godlike virtue, Unity is still a 3D game engine (yes, I just compared Unity to a giant, a beautiful woman, a king and a deity in, like, 15 seconds. Love is a powerful thing.), so you have to do a little monkeying to do 2D. Below is a compilation of what I’ve learned for handling this, not only from Unity and my own experience, but from the use of third-party tools and from the awesome community of developers sharing what they’ve learned (see Props below).

Orthographic vs Perspective Camera

Orthographic is the easy choice for 2D games. With the orthographic camera, objects further away from the camera don’t reduce in size as they do in perspective. It essentially flattens the world, which makes sense for a 2D game. This makes positioning super simple. You can use z position for simple z-ordering, and any object with same x and y position will always be the same place on the screen. Also, because objects will always be the same size, pixel-perfect textures are easier to deal with (see Textures, below).

Perspective vs Orthographic

One reason you may want to use perspective instead is that you can get parallax scrolling without writing code, since objects in the distance will scroll more slowly than those in the foreground. If you choose to do this, Sprite Manager 2 has a pixel-perfect option that will automatically resize textures, although this runtime resizing can make manipulating objects in your scene confusing.

Also, there is a depth sorting issue when using perspective that can lead to objects appearing closer than objects in front of them. If you have this issue, checkout this video (halfway into Part 1) from Owlchemy Labs, who explain and have a solution for the problem.

I prefer using orthographic and write my own parallax code, but there are arguments for each method. Here is a forum thread that goes a little deeper on the issue.

Textures

Unless your game scales sprites or does zooming, you will probably want pixel-perfect textures, meaning textures that are rendered on screen at their native size. In 3D games, textures are resized constantly as they move closer or diminish in the distance. Unity, by default, imports textures with some optimizations for this. You should disable these for crisper textures. In the inspector, choose Filtering: Point, select Texture Type: Advanced and disable Generate Mip Maps.

Texture Settings

In order to size your meshes so they display textures pixel-perfect, you need to figure out the ratio of pixels to world units. This is based on orthographic size of your camera and the resolution of the screen. The size of the camera will be half the height, in world units, of the screen. So, if you have a camera size of 10, the world unit height of your screen will be 20. If your game pixel resolution is 480×320, each world unit will be equal to 320/20 = 16 pixels. You might be tempted to change the ortho size so that there is a one-to-one ratio. This might be ok if you aren’t using physics, but not if you are. The physics engine is tuned so that objects will behave most realistically when each world unit is equal to a meter in game.

Once you have determined this ratio, it can be tedious to resize all your meshes. Don’t soil your pretty little pants about it, Owlchemy Labs has a script to resize automatically and Sprite Manager 2 has a pixel-perfect option that will do this as well.

Remember to size the mesh itself, don’t change the scale in the transform component, otherwise you won’t be able to take most advantage of draw call batching. See Meshes and Batching Draw Calls below for details.

Another thing to consider is compression. You can select from three compression levels for each texture. For iOS, the tightest compression is PVRTC. These are very small memory-wise, but can be pretty crappy-looking for 2D games, especially with gradients and alpha edges.You have to weigh quality against performance here.

Meshes

You generally only need one type of mesh primitive in 2D games: a quad. Unity ships with a plane primitive but it has way too many triangles. You only need two. You can either create your own quad in a 3D modeling program or in code. Or Sprite Manager 2 creates them automatically.

Plane vs. Quad

Draw Call Batching

One of the most important things you can do for performance is to batch your draw calls as much as possible. There are two main ways to do this in Unity. The best and easiest way is static batching, but it has some limitations: it’s only available in Unity Pro, it can only be used on game objects that will not move, scale or rotate, and only objects that share the same material will be batched.Check the static flag at the top of the inspector of any game object to enable this.

The second is dynamic batching. This is done automatically for any objects that share the same material. It allows objects to move and rotate, but NOT scale. All objects to be batched must have identical scales. This is not well documented and took me a while to realize. It is why it is so important to resize meshes themselves instead of scaling with the transform. If you are using Sprite Manager 2, setting the sprite size is safe for batching.

Because of the need to share materials to achieve batching, and because there is only one texture per material, using sprite sheets becomes very important. Also, be aware that many manipulations of an object’s material in code will actually make a local copy of the material and destroy batching for that object. You can manipulate the shared material, but this will change the material for all objects.

Unity docs on batching

Use Statistics to Show Batched Draw Calls

Sprite Sheets

Use them! Packing multiple images onto one texture atlas helps performance in several ways. It cuts down on memory use and app size, and more importantly, it allows different sprites to share a material and thus a draw call. You can definitely roll your own solution for this, or (brace yourself) you can just use Sprite Manager 2.

A Sprite Sheet

Lighting and Shaders

Lighting is not needed for 2D games and it is expensive, especially for mobile games. Don’t put light sources in your scene. All of the shaders that ship with Unity use lighting and will cause unneeded processing. Owlchemy Labs has a pack of simple, unlit shaders available on their site for free. They’re the only ones you’ll need.

Physics

It’s super easy to set up physics for 2D. Just set the constraints on each of your rigidbodies to disallow z movement and x and y rotation. Then just leave your colliders with some depth, and you’re done! You just made the next Angry Birds.

Rigidbody Constraints

Tools

Sprite Manager 2

We use Sprite Manager 2, and it has been ridiculously helpful. It helps or solves many of the issues I’ve talked about here, and that is really just an awesome by-product of its main purpose, which is to be a very nice sprite frame animation tool. Currently its $150 on the Unity Asset Store. Its more basic ancestor, Sprite Manager, is free. My only criticism of it is that it sometimes doesn’t lay out atlases optimally, but I’m guessing that’s a difficult problem.

iTween

iTween is free animation tool for Unity with lots of coded-up examples for sale for a buck a pop.

Rage Spline

Rage Spline is a crazy looking unity tool that allows in-editor vector-style drawing of objects, including creation of meshes and colliders.

Props

If it seems like I’ve mentioned Sprite Manager 2 and Owlchemy Labs a lot, it’s because you’re paying attention. I’ve learned a lot and gotten a lot of utility from both. I definitely recommend checking them out, including Owlchemy Labs amazing game Snuggle Truck.

I hope this helps someone out there. Make it good and make it flat.

  • James Bond

    Thanks for putting this together bud, I have been working on a 2d game for awhile now and have ran into every issue you mentioned here.. Sprite manager 2 seems to be worth checking out, and these lab people i have never heard of. Again, thank you for taking the time. You just probably saved me hours of headache.

  • Detocroix

    Don’t forget about EX2D. Its another very good 2D kit with very good support and documentation. As far as I know it does the same as Sprite Manager 2 but costs 25-35 :)

  • Phil Plante

    For more optimal texture packing you might look into a specialized tool. My favorite is TexturePacker (texturepacker.com) as it does a great job of tightly packing every sprite, and also exports to everything including Unity. Best $25 I have ever spent on development tools.

  • Stan Kramer

    Hey, thanks man. I’ve been looking into Unity for doing both 2D and 3D projects, and you just answered more questions for me than hours of pouring over the resources on the Unity site did.

  • Sean Sanders

    A good rundown on the building blocks for a 2D Unity game. I wanted to offer a few other insights I’ve had developing a Unity Game, using EZGUI (which uses the same underlying tech as Sprite Manager 2).

    * Atlas packing — definitely annoying how inefficient this algorithm can sometimes be. Turns out SM2 just uses Unity’s Texture2D class’ PackTextures() method. It seems to particularly have fits spacing long vertically-oriented (portrait shaped) textures. We got better results after rotating the source textures so they laid horizontal. Hopefully Unity fixes this function up in the future. Otherwise, next time we have a Unity project, we’re writing our own =]

    * Batch draw calls statistic — I don’t know if it’s just me but the batched number being higher in the statistic of “Draw calls: 3 batched: 5″ confused me when profiling our own game at first. I then learned that the batched number is the count of how many draw calls were eliminated by batching, *not* the count of how many draw calls were being made which used batched objects.

  • MrPhil

    I’m struggling to using Unity as a 2D engine and this is the best walk through I’ve seen. Thank you. I was wondering if you’ve compared Sprite Manager 2 against seemingly more popular 2D Toolkit or ex2D and the free Orthello 2D.

  • squid808

    Here’s a question – what is the advantage to using Sprites and Sprite Sheets over a textured quad? Aside from memory, as you mentioned in the Sprite Sheets section.

  • Dave

    I have to say, my experience of using rigidbodies for 2D physics has not been super easy: with anything stronger than moon gravity, I get jittering when my character rigidbody falls onto a collider. Looking at the Unity forums/answers, this isn’t isolated to me either. I tried all the suggestions such as turning on interpolation, reducing the mass to 0.1kg, and changing collision detection to continuous dynamic but nothing would stop my main character from dancing around like he needed the toilet.

    My quest for the perfect Unity based 2D collision detection system continues. Tonight I’m going to try the Character Controller, though I hear that has its own issues when you want to move the character on moving platforms etc.

  • jake

    Sorry, everyone for not approving your comments. We were not receiving email notifications so we didn’t know they were there.

    Thanks for all the feedback and suggestions. I haven’t tried ex2D or any of the tools mentioned, but I have heard good things about some of them.

    @squid808 The main reason to use sprite sheets for me is to reduce draw calls.