Samstag, 22. November 2014

Procedural Sprites - Space Ships in Infinity Raider

I'm pretty bad at drawing so I tried to let the computer do the drawing of the space ships for Infinity Raider. The basic idea was to split up the space ships into multiple components. Each component would be drawn onto a seperate layer and in the end everything would be combined into a single sprite that could be used by the game. The code in this blogpost is not optimized in any way, use it at your on risk:) In the code shown below the "ship" texture is the texture holding the completed texture, while "cockpit", "weapons", "body" and "engine" hold the different parts. "OutlineTexture", "ClearTexture" and "CopyTexture" are helper functions. "ClearTexture" sets every pixel to transparent black (0f,0f,0f,0f). Unity's default for new created textures is a semitransparent white. "CopyTexture" copies opaque pixels from a source texture to a target texture. This is used to paint the different layers on top of each other. "OutlineTexture" simply draws a black outline around the opaque pixels of a texture.

public static Sprite GenerateShip()
{
  //Create the different ship parts as seperate layers,
  //then bake them into single texture
  Texture2D cockpit = GenerateCockpit ();
  cockpit = OutlineTexture (cockpit);
  Texture2D weapons = GenerateWeapons ();
  weapons = OutlineTexture (weapons);
  Texture2D body = GenerateBody ();
  body = OutlineTexture (body);
  Texture2D engine = GenerateEngine ();
  engine = OutlineTexture (engine);

  Texture2D ship = new Texture2D (width,height);
  ship = ClearTexture (ship);
  ship = CopyTexture (ship, body);
  ship = CopyTexture (ship, engine);
  ship = CopyTexture (ship, cockpit);
  ship = CopyTexture (ship, weapons);
  return Sprite.Create (ship, new Rect (0,0,width,height),
                        new Vector2 (0.5f, 0.5f));  
}
The "body" is maybe the most interesting part, as it can be one of several different shapes. The other parts only vary in color. The following code block shows the code for the body and the diamond as one example for a shape. "MirrorTexture" is another helper function used to mirror textures along the vertical axis.

private static Texture2D GenerateBody()
{
  Texture2D body = new Texture2D (width, height);
  body = ClearTexture (body);

  //generate a random shape
  int shape = Random.Range (1, 4);
  switch (shape)
  {
    case 1:{body = GenerateDiamond(body);break;}
    case 2:{body = GenerateTriangle(body);break;}
    case 3:{body = GenerateCircle(body);break;}
    default:{break;}
  }

  return body;
}

private static Texture2D GenerateDiamond(Texture2D input)
{
  Color bodyColor = new Color (Random.Range (0f, 1f),
                               Random.Range (0f, 1f),
                               Random.Range (0f, 1f), 1f);
  Vector2 seed = new Vector2 (width / 2 - 1, height / 2 - 1);
  int maxDistance = 31;
  for (int x = 0; x < width / 2; x++)
  {
    for(int y = 0; y < height; y++)
    {
      if(Mathf.Abs(x - seed.x)+Mathf.Abs(y-seed.y) <= maxDistance)
      {
        input.SetPixel(x,y,bodyColor);
      }
    }
  }
  input = MirrorTexture (input);
  return input;
}
The space ships created this way look really simple, but are still better than my hand drawings:) If you decide to create your own sprite generator (for space ships or whatever else you need) there's a lot of things that could be improved / played with. It would be more efficient to use a Color array instead of texture objects and to only create a texture when your sprite is complete. Also you could experiment with different sizes and offsets for the different layers. Another thing that could be done is adding fine details and lighting. Thanks fors reading and happy experimenting.

Keine Kommentare:

Kommentar veröffentlichen