Unity2D – Sprites 2

25 10 2013

Hey what do you know, sprites are catching up.

Alright, where did we leave off on?

public static class NewSpriteObject
{
  public static GameObject Make(Texture2D texture)
  {
    throw new NotImplementedException(); 
  }
}

Remember my definition of a sprite object? no? oh well, if I could re-read the previous post so can you 😛

Back?
let’s continue.

It’s really not as simple as I’ve portrayed it and off the top of my head I can think of several paths to take to achieve the end goal. First of which, the simplest, is to generate a node which contains all the information required to render a mesh and texture/material and manipulate its data through Unity’s normal transform. The second approach is to generate the same node with the same data but reroute the transform information to update the underlying mesh. Third we could create a parent node (let’s call it the sprite node) and a child node (let’s call that one the graphic node) which would contain the mesh/material data and manipulate them via the sprite itself. We could also combine the approaches and mix’n’mash between transform manipulation and direct vertex updates.

But which road to choose??
Heck, it’s not like I’ve got a word count here, let’s go with all of ’em and see where we end up.

Ok. First approach. Let’s create a node, attach the correct components and call it a day. The sequence of method calls would look something like this;

public static GameObject Make(Texture2D texture)
{
  var sprite_node = new GameObject("Sprite");
  var mesh_filter = sprite_node.AddComponent<MeshFilter>();
  var mesh_renderer = sprite_node.AddComponent<MeshRenderer>();

  var mesh = NewSpriteMesh.Make(Vector2.one);
  mesh_filter.sharedMesh = mesh;

  var material = NewSpriteMaterial(texture);
  mesh_renderer.sharedMaterial = material;

  sprite_node.transform.localScale = new Vector3(texture.width, texture.height, 1.0f);
  return sprite_node;
}

So, what’s right about this approach and what’s less right?
It’s pretty simple, pretty easy and gives use what we need at the most basic level. A node that renders our texture in game. The main drawback of this approach is our lack of a pivot point control over the mesh (not to mention having negative scale if we want to flip it).

But wait! This code does not work. What is this magic called NewSpriteMaterial? (I’m going to assume you know what the NewSpriteMesh class is by now).

The NewSpriteMaterial isn’t any more complicated.

public static class NewSpriteMaterial
{
  public static Material Make(Texture2D texture)
  {
    var material = new Material(Shader.Find("SpriteShaderName");
    material.name = "sprite-material";
    material.mainTexture = texture;
    material.mainTextureOffset = Vector2.zero;
    material.mainTextureScale = Vector2.one;
    return material;
  }
}

The shader used doesn’t have to be any more complicated than Unity’s transparent shader if you want.

One thing I would like to point out, before finishing this post, is the usage of ‘shared’ mesh and material. This can lead to subtle issues like copy/pasting sprites where all the sprite nodes actually share the same material (Unity does this as an optimization – which I really wish I had more control over) and one change to the material will lead to it propagating to all other nodes. You’d think that using the regular, ‘mesh_renderer.material’ will solve this but at edit-time Unity will emit an error/warning and leak memory. On the same note, because we’ve not actually saved either mesh or material if we delete all nodes which reference them we’re looking at another memory leak (this can be fixed by pressing play and stop which forces Unity to garbage collect unreferenced objects). I will post code later for simple asset serialization.

And that’s it for the first approach. Not too difficult, very rewarding and easy to implement.

“Land of immortals I wait for my day, to reach the wisdom of your skies”





Unity2D – Sprites 1

17 10 2013

Heh, nothing to do with the official release of Unity’s 2D support (which I found somewhat underwhelming to be honest, but that’s what you get from trying to add more features instead of making a proper new editor, anyway I digress). There are a lot little tricks and improvements I’ve employed during the last few months in order to constrain Unity3d to 2d while making a 2d game (the budding website of which you can find here). One of the important things I wanted to address first was sprite creation and manipulation by the designer. As we all know using Unity’s in-built plane mesh is kind of rubbish and while I know there’re plenty of other ways to generate a simple quad mesh I’ve opted to generate my own (as many others have before me).

The code for this is contained in the following factory [SpriteMesh];

/// <summary>
/// This factory class is responsible for manufacturing quad meshes for use with sprite objects in the game.
/// </summary>
public static class SpriteMesh
{
  public static Mesh Make(Vector2 size)
  {
    var mesh = new Mesh
      {
        name = "sprite-mesh",
        vertices = MakeVertices(size),
        triangles = MakeTriangles(),
        uv = MakeUV(),
        normals = MakeCameraFacingNormals(),
      };
    
    mesh.RecalculateBounds();
    return mesh;
  }
  
  public static Vector3[] MakeCameraFacingNormals()
  {
    var normals = new Vector3Collection();
    for (var index = 0; index != 4; ++index)
      normals.Add(0.0f, 0.0f, -1.0f);
    return normals.ToArray();
  }

  public static Vector2[] MakeUV()
  {
    var uv = new Vector2Collection
      {
        {0.0f, 0.0f},
        {0.0f, 1.0f},
        {1.0f, 1.0f},
        {1.0f, 0.0f},
      };
    return uv.ToArray();
  }
  
  public static int[] MakeTriangles()
  {
    var triangles = new TriangleIndexCollection
      {
        {0, 1, 3},
        {3, 1, 2},
      };
    return triangles.ToArray();
  }

  public static Vector3[] MakeVertices(Vector2 size)
  {
    var half = size/2.0f;
    var vertices = new Vector3Collection();
    vertices.AddXY(-half.x, -half.y);
    vertices.AddXY(-half.x, +half.y);
    vertices.AddXY(+half.x, +half.y);
    vertices.AddXY(+half.x, -half.y);
    return vertices.ToArray();
  }
}

Ok, few things to notice before continuing on, since, if you just copy/paste this code it won’t work.

First, I’m using three specialized containers called, Vector2Collection, Vector3Collection and TriangleIndexCollection. These are just classes which wrap around a List<Vector2>, List<Vector3> and List<int> respectively and provide an easier interface for managing those cases.

Secondly, read the code again. The mesh is generated in a clockwise direction and with a very specific triangle and UV space orientation.

And lastly I want to mention that the underlying methods making the mesh are also marked public because they can be useful later.

Alright. We have an easy way to generate a mesh for use with our sprites. Now what?
I’m glad you asked. Because even if you didn’t I’m going to answer anyway.

Next we need to figure out exactly what is a sprite in Unity and how to create one…
The following definition is my own interpretation of Unity’s component model and how it can be used (your mileage may vary).
“A Sprite is a [GameObject] node which renders a specific material/texture across a [quad] mesh while allowing mesh control”.

And finally, creating a sprite. If you read the previous definition you’ll see that my sprite requires first and foremost a mesh and a material/texture. Since we’re creating the mesh ourselves we need some way to accept a material/texture. Let’s start with the simple case of having a single texture we want to use for our sprite (we’ll continue from there to create sprite objects from texture atlases). Finding and selecting textures for making sprites is easy! we have them in our asset folder (under /Textures/ if you’re like me). So let us make a menu item for creating a sprite from a selected texture.

/// <Summary>
/// This menu item allows the designer to generate a sprite object from the currently selected texture.
/// </Summary>
public static class MakeSpriteFromTexture
{
  [MenuItem("Found Cake Studio/Utilities/Make Sprite From Texture" + Hotkey.Alt.S)
  public static void Perform()
  {
    var selected_texture = Selection.activeObject as Texture2D;
    if (selected_texture == null)
      throw new UnityException("No texture was selected");
    
    SpriteObject.Make(selected_texture);
  }
}

If you’re wondering where that “Hotkey.Alt.S” came from then read the previous post. If you’re also wondering what’s that SpriteObject.Make() then have some patience, that’s our factory for making the sprite object. Let’s stub that factory class/method.

public static class SpriteObject
{
  public static GameObject Make(Texture2D texture)
  {
    throw new NotImplementedException(); 
  }
}

Annnnnd that’s it for today; part 2 to come in the next couple of days, complete with making the graphic components as well as the sprite mesh control.

“See the falls of Erloria, The grey mountains are near, Dark shadows falling, Daylight’s end is here…”