Search Unity

A good workflow to smoothly import 2D content into Unity, Part II: Importing

, 五月 23, 2013

Recently I wrote about authoring your 2D content in Photoshop and then setting it up for exporting. Today’s post will cover importing the files into Unity.  You can find a link to download all the files at the end of the post.

Picking up where we left off

Unity will happily import our images and XML file, but the images will be imported as normal textures by default, and the XML file will be imported as a text asset. So we need to write an importer within Unity to process the files for our game.
You might think that writing an AssetPostprocessor would be the way to go and you’d be forgiven for thinking that. The problem with using AssetPostprocessor is that we’re limited in what we can do within that context and it won’t allow us to create a new material which we need to do. So the answer is to simply write an editor script to do what we want.

Our importer will need to:

  • Load and deserialize the exported XML meta-data,
  • Change the texture import settings for our images so that we can process them,
  • Create and texture atlas and pack all our textures into it,
  • Create mesh assets for each of our images,
  • Create GameObjects that reference the meshes,
  • And create a scene to hold our GameObjects.

Writing Editor scripts
As with the exporter, you can download the importer script here (). It should be placed in a folder called “Editor” in order for it to work.
If you haven’t written editor scripts before you’re not taking advantage of one of Unity’s most powerful features. Editor scripts are a superset of normal scripting, so if you already know how to write game scripts you already know the basics of editor scripting too.

Editor scripts are distinguished from regular game scripts in essentially three ways.
Firstly, editor scripts must be placed within a folder named “Editor”, or one of it’s sub-folders. Secondly, at the top of the scripting file you must place the following line:

Finally, in order for the editor script to be called, it must derive from one of the editor sub-classes such as Editor, EditorWindow, ScriptableWizard, etc. You can use classes that don’t derive from these, but if you want to receive events and be interactive, these classes do so in a way similar to MonoBehaviour. For our purposes we’ll use the general Editor base class.

As with the exporter we won’t go over every line as the file is well commented, but let’s look at some of the more interesting bits.
To start, we need some way to invoke our importer, so we’ll put it on the Assets menu inside Unity like this:

So now we are sitting in the Assets menu, and when chosen we can execute a script, so now all we have to do is do something useful.

The first thing we’ll do is convert the XML file to something more useable such as a data class. When we wrote the exporter, we organized our data into a logical set of elements so that it will map easily into a data class. Our data class looks like the following and matches our XML file in structure and data types:

To actually convert the XML file into the data class, we use the built-in .NET serialization methods.

Since we’ll be creating a scene using code, it’s possible that the user already has that scene open, or another, and we need to be sure they get a chance to save changes. To do this we call:

Then we simply create a new scene, deleting the old one first if it already exists:

Next up is to fix the texture import settings. By default textures aren’t readable and we need to read them in order to put them into an atlas. While we’re at it, we also need to change some of the other settings.

Now we’re ready to create the new atlas material, texture and pack everything into it.

Since we have created all the basic parts we need, we can now loop over the images, create meshes, create the GameObjects and put them into the scene. It’s a fair amount of code so it isn’t repeated in it’s entirety, but certainly the most interesting part has to be creating the 2D sprites in Unity. You might be surprised to find out how simple and easy it is. I use a simple function to do it for all my 2D stuff within Unity and it looks like the following.

That may seem like a lot of code, but it’s actually quite straightforward and simple.
Finally, after we built all our 2D sprite assets, we simply fix-up the camera so that it will render our scene pixel perfect like it was originally in Photoshop.

If you look through the importer you should be able to follow it pretty easily.

If everything went well, after running the importer script you should see the scene fully composed and running in Unity.

You can download a Unity package with all the files for this tutorial.

20 评论




    Sorry about the delay in replying. If you’re targeting a platform with unsupported texture type, just change the editor script that modifies the atlas format to one that it supports. This solution is a bit similar to 2D toolkit and they both solve a similar problem in different ways. You can use this method along with 2D Toolkit, but they don’t directly work together.


    Hmm, strange. Is there any errors? Can you email your XML file so I can take a look? It’s brettb (at)

  2. I tried to import a previously exporting scene and nothing happened. I click import HOG scene, then find my XML, pick it up and … silence.
    what am I doing wrong?

  3. Also, I’m having this error:

    Unsupported texture format – needs to be ARGB32, RGBA32, RGB24 or Alpha8

  4. How does this process work with another tools like 2D toolkit? This one creates his own atlas

  5. @yohan

    Sure. This tutorial is for a static screen. But it would be easy to modify and optimize for an endless runner too. Just put all the blocks into a different layer, tag them with whatever meta-data you need, export. On import, create the scene along with prefabs of the individual pieces, hide them into a pool, then spawn them out of there. I have made such types of things before using the same process outlined in the tutorial. The point is to modify it and use it in ways that works for you. Have fun!

  6. @Brett
    Yes correct, you can move the camera, but this technique do not work in all kind of games. Take for example a 2D infinite runner where you also need all of the screen (at least horizontally), or games like Candy Crush were you can’t do that.

  7. @richard

    There are a lot of things you can’t do in an AssetPostprocessor that I need such as generating a mesh, making a prefab of it and getting the material assigned all within a single postprocessor. It’s something we’re looking at changing in the future, but for now, you have to run the importer at a time when you have available all the editor functions for asset creation and modification.

  8. Richard Harrington

    五月 23, 2013 9:00 下午

    “it won’t allow us to create a new material which we need to do”
    Not sure what you mean by that – I have several AssetPostprocessor setups that create and assign materials. Just use the OnAssignMaterialModel method:

  9. @mark

    Yes, thanks for posting this! You can fix it in script too by doing this at the beginning of the main function (but AFTER any code that can return) so it doesn’t mess with the user settings:

    var savedRulerUnits = app.preferences.rulerUnits;
    var savedTypeUnits = app.preferences.typeUnits;
    app.preferences.rulerUnits = Units.PIXELS;
    app.preferences.typeUnits = TypeUnits.PIXELS;

    And then restoring it when done at the end of the main function:

    app.preferences.rulerUnits = savedRulerUnits;
    app.preferences.typeUnits = savedTypeUnits;

    I have updated the package with a modified exporter that adds the above code to fix the issue. Thanks!


  10. Thanks for the great tutorial!
    FYI, something I ran into that should be mentioned is that Photoshop’s units need to be changed to pixels. By default the setting is inches, which on export will screw up all the positions in the xml doc.
    Perhaps that could be checked (and possibly changed) in the Photoshop script.
    Thanks again!

  11. Even if we aren’t working in a 2D project, those tips are really welcome to anyone willing to jump into the 2D side of Unity. The only thing missing is the GUI system! :)
    Good job!

  12. @yohan

    Right, if you want to support odd sizes, then what I did was add pan/zoom to the controller. I stripped this out of the code. Basically if I pinched zoom/scrollwheel or pan the scene I call an update bounds and position routines. The sizes in the routines are based on the size of the authored art, and this will let it move within the bounds. Of course, if it was a web page, you should change the player size to be the same size as the authored content.

    private void UpdateCameraBounds()
    // given the orthographic size, calc the camera bounds
    cameraBounds.x = ((Camera.main.pixelWidth/Camera.main.pixelHeight) * Camera.main.orthographicSize) – 512;
    cameraBounds.y = Camera.main.orthographicSize – 384;
    cameraBounds.width = -cameraBounds.x * 2;
    cameraBounds.height = -cameraBounds.y * 2;

    private void UpdateCameraPosition()
    // clamp to cameraBounds
    Vector3 newCameraPosition = Camera.main.transform.position;

    // drag the scene within the bounds
    if(newCameraPosition.x < cameraBounds.x) { newCameraPosition.x = cameraBounds.x; } if(newCameraPosition.x > cameraBounds.x + cameraBounds.width)
    newCameraPosition.x = cameraBounds.x + cameraBounds.width;
    if(newCameraPosition.y < cameraBounds.y) { newCameraPosition.y = cameraBounds.y; } if(newCameraPosition.y > cameraBounds.y + cameraBounds.height)
    newCameraPosition.y = cameraBounds.y + cameraBounds.height;
    Camera.main.transform.position = newCameraPosition;

  13. @brett

    Yup thanks, but doing that will “crop” your image and scene.

    Check out here :

  14. @yohan

    It’s a good question, glad you asked. In this case I set the orthographic size to 768/2 as the default for the camera so it looks correct in the editor window. In actuality the camera is set to Screen.height/2 on Start in the HOGController (line 60) like this:

    Camera.main.orthographicSize = (Screen.height/2.0f);

    So the line you’re talking about is simply to set it up in the editor, but the game will adapt to the screen it’s running on.

  15. Hi,

    Thanks for the articles!

    Looks good, I’ll try to make articles to detail my 2D workflow because it’s different from yours, and I want people to tell me how I can improve it.

    But one question though, I think this is a webplayer game so you don’t have the issue, but you’ve set the camera orthographic size, to half the size of the height (768) :
    Camera.main.orthographicSize = (768.0f/2.0f);

    What if your game is running in a 960×640 window for example ? You will have some borders with nothing on them at the left and right… and if you change the orthographic size, you will lose the pixel perfect.

    Thanks !

  16. This workflow is generally really nice even for GUIs. The free range games has some asset on asset store which allows making GUIs from PSD files and these kind of solutions are really nice cause they allow artists to do much without relying on devs and also without doing time consumming tasks twise.

    Microsoft’s blend product does the same thing for code and interface separation in a so much powerful way.

  17. @David I feel both your pain and joy, been there many times until we switched to Unity. Glad you found it helpful. We saved so much time and money in development, it allowed us a lot of upside opportunity. Good luck!

  18. Amazing. I’m used creating Casual Adventure Games for BFG using C++ and some f… millions lines of code. I was planning to write another heavy engine to switch our production to Unity. The PSD workflow is amazing, and your post is just amazing. Thanks Brett.

  19. i had a question about tranparent PNGs. what is the best way to create and import them?

  20. thanks brett!