7/24/2014

Sprite/multiply или расширенная работа с атласами в Unity

Всем привет. Глубоко извиняюсь, что забросил свой блог. Руки дошли все же написать что-нибудь полезное за эти пару свободных часов, поэтому минимум воды и перейдем сразу к сути заметки. Поехали!
 Все кто щупал Unity 4.3 или выше, наверняка, заметили мощнейшие возможности работы как с одиночными спрайтами, так и с атласами спрайтов. Unity позволяет атлас разбивать на спрайты как вручную, так и в автоматическом режиме, а еще разбивать по сетке с различными настройками разбиения. Казалось бы этого вполне достаточно для работы с набором спрайтов записанных в одну текстуру, но на этом не все! Есть и более тонкие возможности работы с этим делом, правда без кода тут не обойтись. Я говорю об автоматическом разбиении атласа, согласно вашему упаковщику атласов, дабы сохранились его настройки координат и именования спрайтов. Тонкости парсинга того или иного упаковщика атласов я рассматривать не буду, это реализовать сможет каждый желающий. А так же нюансы разворота текстур в атласе даже не берусь затрагивать ибо в Unity это не сработает со стандартными спрайтами(есть много других решений).
Создадим класс, хранящий информацию о расположении спрайта в атласе. Что-то подобное этому. Только не стоит упираться в сию реализацию, каждый должен его делать по своему усмотрению. В моем примере класс хранит координаты прямоугольника спрайта и его имя, а так же имеет ужасный конструктор и две функции возвращающие этот прямоугольник и имя.
       
public class SpriteData
{
 private string name_;
 private int x_, y_, w_, h_;

 public SpriteData(string name, int x, int y, int w, int h)
 {
  name_ = name;
  x_ = x;
  y_ = y;
  w_ = w;
  h_ = h;
 }

 public Rect GetRect()
 {
  return new Rect(x_, y_, w_, h_);
 }

 public string GetName()
 {
  return name_;
 }
}
Идем дальше! Ниже представлен рабочий код, нюансы, которого описаны в коментариях к коду.
       
using System.IO;
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

// я использую кастомное окошко, можно реализовать, как команду контекстного меню
public class SpritePacker : EditorWindow
{
    // наш атлас со спрайтами
    private Texture2D texture_; 
    // текстовый файл информации о расположении спрайтов
    //  обычно упаковщики их генерируют в том или ином виде
    private TextAsset atlas_;
    // массивчик данных спрайтов в который будем парсить координаты и прочее
 private static List<SpriteData> data_ = new List<SpriteData>();

 [MenuItem("Unity2D/Sprite packer")]
 static void GenerateAtlas()
 {
  var window = ScriptableObject.CreateInstance<SpritePacker>();
  window.Show ();
 }

 private void OnGUI()
 {
  texture_ = (Texture2D)EditorGUILayout.ObjectField(
  "Atlas texture:", texture_, typeof(Texture2D), true);
  atlas_ = (TextAsset)EditorGUILayout.ObjectField(
  "Atlas info:", atlas_, typeof(TextAsset), true);
  
  if(GUILayout.Button("Generate atlas"))
  {
   string path = AssetDatabase.GetAssetPath(texture_);
   List<SpriteMetaData> sprites = Parse(atlas_, texture_.height);
   // Ранее я рассказывал, что такое TextureImporter и как это дело юзать 
   TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
   textureImporter.spritesheet = sprites.ToArray();
   textureImporter.textureType = TextureImporterType.Sprite;
   textureImporter.spriteImportMode = SpriteImportMode.Multiple;
   AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
  }
 }
 
 private static List<SpriteMetaData> Parse(TextAsset text, int height)
 {
  using (StringReader reader = new StringReader(text.text))
  {
   ParseAtlasInfo(text);
   List<SpriteMetaData> spriteAtlas = new List<SpriteMetaData>();
   foreach(SpriteData spr in data_)
   {
    SpriteMetaData smd = new SpriteMetaData();
    smd.name = spr.GetName();
    Rect r = spr.GetRect();
    smd.rect = new Rect(r.x, height - r.y - r.height, r.width, r.height);
    smd.pivot = Vector2.zero;
     // выбор пивота спрайта в данном случае по центу
    smd.alignment = 1;
    spriteAtlas.Add(smd);
   }
   data_.Clear();
   return spriteAtlas;
  }
 }
 
 // мой парсер недописанный читает атласы сгенерированные программой 
 // Cheetah Texture Packer - http://www.gamedev.ru/projects/forum/?id=161714
 private static void ParseAtlasInfo(TextAsset text)
 {
  using (StringReader reader = new StringReader(text.text))
  {
   reader.ReadLine();
   while (true)
   {
    string line = reader.ReadLine();
    if (line != null)
    {
     string[] sprData = line.Split(new char[]{' ', '\t'});
     string[] name = sprData[0].Split ('.');
     SpriteData sprite = new SpriteData(
      name[0], 
      int.Parse(sprData[1]), 
      int.Parse(sprData[2]), 
      int.Parse(sprData[3]), 
      int.Parse(sprData[4])
     );
     data_.Add(sprite);
    }
    else
     break;
   }
  }
 }
}
Ну вот и все! В итоге получаем небольшую утилиту подобную этой:
image
Все вопросы, замечания, комментарии и предложения направлять в комментарии или лично в соц.сети или скайп.

Комментариев нет:

Отправить комментарий