Автоматизація створення GUI (Photoshop + Stalring)

Стала завдання автоматизувати збірку GUI для одного проекту X.
 
 1 Поточна реалізація
Артист робить всі свої справи в photoshop і віддає їх програмістам. Програмісти, в свою чергу, самі якось витягають текстури або атласи і за допомогою Улити позиціонують кожен окремий елемент на stage флешки, далі ручкам додають всі текстові поля, ефекти, прописують всім цим елементам координати, які отримали завдяки утиліті. У підсумку виходить дуже довга і болісна робота, а якщо врахувати, що це робить ще й програміст — це АТ.
 
 1.1 Поточні проблеми
Програмісту доводиться:
 
     
  • довго і болісно виганяти всі текстури з проекту
  •  
  • позиціонувати всі елементи
  •  
  • створювати екземпляри класів і задавати їм потрібні властивості
  •  
  • підбирати колір тексту, розмір
  •  
  • підбирати ефект, якщо такий є
  •  
  • по 3 мільйони разів запускатися і перевіряти
  •  
Я вже не кажу про те разі, коли щось поміняється.
 
 2 Рішення
 
     
  • Скрипт, який з psd виганяє JSON з усіма настройками і параметрами + текстури всіх елементів на сцені.
  •  
  • Збираємо в атлас
  •  
  • Движок для клієнта (flash), який все це справа збирає в купу.
  •  
 
 2.1 Обмеження
 
     
  • Невелика підготовка psd перед експортом, а саме об'єднання всіх верств і ефектів об'єкта. Тобто якщо є кнопка і над нею шар з відблиском, то ці шари потрібно об'єднати.
  •  
  • Всі вихідні коди psd повинні бути з розширенням проекту на flash
  •  
  • Необхідно растеризувати всі шари (можливо далі пофікшу)
  •  
 
 2.2 Що нам знадобиться
 
     
  • Adobe Photoshop CC
  •  
  • Adobe ExtendScript Toolkit CS5
  •  
  • TexturePackerGUI
  •  
  • intel ideja + (starling)
  •  
  • Трохи знань JS && AS
  •  
 
 3 Підготовка psd
* Враховується, що кожен окремий шар — це 1 елемент на сцені.
Ось так виглядає заготовка самого psd:
 image
 
 3.1 Написання скрипта jsx
Відкриємо Adobe ExtendScript Toolkit і поміняємо target:
 
 image
 
Якщо у вас немає такої вкладки, тоді раджу оновити photoshop. Я боровся з цим поки не знайшов збірку з Adobe Application Manager в комплекті.
 
 3.1.1 Скрипт
Основа основ:
 
 
#target photoshop //это нужно для информирования с чем мы работаем
app.bringToFront(); //чтобы не открывать уже открытый экземпляр приложения

 
Далі нам необхідно підключити лібу для роботи з JSON, оскільки стандартний js не хоче працювати.
Сама Ліба немаленька, тому поклав окремо. Все що всередині потрібно просто скопіювати в документ скрипта, оскільки ми будемо робити один документ, щоб його з легкістю переносити.
 jam_lib
Константи необхідні для ідентифікації типу з яким працюємо
 
var TYPE_SPRITE = "sprite";
var TYPE_TEXT = "text";
var TYPE_COLLECTION = "collection";

 
Основні змінні в скрипті
 
 
var arts = app.activeDocument.artLayers//все Нормальные слои в документе
var sets = app.activeDocument.layerSets;//все группы слоев
var respont  = new Object();//обж. который будет подвержен сериализации и сохранению
var arr = new Array();//обж. всех листов для сохранения
var u = 0;//индекс в обж. сохранения
var width_document = app.activeDocument.width.value;
var heigth_document = app.activeDocument.height.value;

 
Далі говоримо Старт, і просимо вибрати місце збереження результату у форматі JSON (туди ж додадуться і текстури).
 
 
alert("Старт!");
    var defaultInFolder = new Folder ("C:\\");
    var jsonFilter ="JSON Text Files:*.json,All Files:*.*";
    var inFile = defaultInFolder.saveDlg ("Open JSON text file:", jsonFilter);

 
Перевірка на валідність обраного шляху і запуск скрипта.
 
 
if(inFile)
    main();

 
Функція main — наша точка входу виконання.
 
 
function main()
{
    for(var j=0; j<arts.length; j++) {//Проверяем все обычные листы
        respont[u] = setLayers(arts[j]);    //положить в обж. результата парса листа
        u+=1;
    }
    for(var i = 0;i<sets.length;i++)//проверяем все группы листов
    {
        respont[u] = setSets(sets[i]); //положить в обж. результат парса группы
        u+=1;
    }
    for(var i = 0;i<arr.length;i++)//проходим все листы и сохраняем их
    {
        visible(false);//прячем все
        visibleElement(arr[i],true);//делаем видимым один нужный лист
        saveLayer (arr[i]);//сохраняем документ
    }
    visible(true);//делаем видимым все листы
    
        jamUtils.writeJsonFile (inFile, respont,  4);//сохраняем в документ наш обж.
        alert ("Готово!");
}

 
Функція парса групи листів
 
 
function setSets(set)
{
 var res = new Object();
 res["type"] = TYPE_COLLECTION;
 res["name"] = set.name;
 for(var j=0; j<set.artLayers.length ; j++) {
 res[j] = setLayers(set.artLayers[j]);    //вызываем парс как у обычного листа
 }
return res;
}

 
Парсинг самого листа.
 
 
function setLayers(el)
{
 var res= new Object();
 var x;
 var y;
 x = el.bounds[0].value;
 y = el.bounds[1].value;
 res["x"] = x;
 res["y"] = y;
 res["alpha"] = Math.round(el.opacity)/100;//альфу к диапазону 0-1
 res["name"] = el.name.replace(/\s+/g, '');//удаляем все пробелы с имени

  try{//нехитрая конструкция проверки на текстовое поле 
   var textItem = el.textItem 
   res["type"] = TYPE_TEXT;//если тут еще не вылетел эксепшен, значит, это текст
   res["color"] = textItem.color.rgb.hexValue;
   res["font"] = textItem.font;
   res["size"] = Math.round(textItem.size.value);
   res["contents"] = textItem.contents;   
   res["width"] = Math.round(textItem.width.value);
   res["height"] = Math.round(textItem.height.value);         
   }catch(e)
   {
   res["type"] = TYPE_SPRITE;
   }
arr.push(el);
    
return res;
}

 
Сам парсинг збереження текстури ви дивиться так:
 
 
function saveLayer(layer)
{
    try{
     var textItem = layer.textItem //это текст, то пропускаем его сохранение как текстуры
     return;
    }catch(e){
    }
    
    activeDocument.activeLayer = layer;
    
    var o = layer.opacity;
//получаем координаты и размеры текстур
    var xx = (layer.bounds[0].value);
    var yy = (layer.bounds[1].value);
    var ww = (layer.bounds[2].value);
    var hh = (layer.bounds[3].value);

    var shape = [
    [xx, yy],
    [ww,yy],
    [ww,hh],
    [xx,hh]
    ];
    app.activeDocument.selection.select(shape);//выделяем область текстуры

    app.activeDocument.selection.translate(new UnitValue(-xx,"px"),new UnitValue(-yy,"px"));//сдвигаем ее в 0 0

    app.activeDocument.selection.deselect();//уберем выделенную область 

    app.activeDocument.resizeCanvas(new UnitValue(ww-xx, "px"), new UnitValue(hh-yy, "px"), AnchorPosition.TOPLEFT);//изменяем размер документа к размеру текстуры

    activeDocument.activeLayer.opacity = 100;//альфу текстуре 100

     var saveFile= File(inFile.path+"/"+layer.name.replace(/\s+/g, '')+".png");
    SavePNG(saveFile);//именно сохранение
    activeDocument.activeLayer.opacity = o;//возвращаем альфу слоя
    
    app.activeDocument.resizeCanvas(new UnitValue(width_document, "px"), new UnitValue(heigth_document, "px"), AnchorPosition.TOPLEFT);//возвращаем размеры слоя
    app.activeDocument. activeLayer.translate(new UnitValue(xx,"px"),new UnitValue(yy,"px"));//возвращаем позицию слоя
}

function SavePNG(saveFile){
    var pngOpts = new ExportOptionsSaveForWeb; 
    pngOpts.format = SaveDocumentType.PNG
    pngOpts.PNG8 = false; 
    pngOpts.transparency = true; 
    pngOpts.interlaced = false; 
    pngOpts.quality = 100;
    activeDocument.exportDocument(new File(saveFile),ExportType.SAVEFORWEB,pngOpts); 
}

 
І 2 фун. для відображення шарів.
 
 
function visibleElement(sprite,flag)
{
    sprite.visible = flag;
}

function visible(flag)
{
    for(var i = 0;i<arr.length;i++)
    {
            arr[i].visible = flag;
     }
}

 
 3.2 Результат
Після натискаємо F5 в редакторі, вказуємо шлях і чекаємо написи «Готово», також можна бачити як скрипт щось робить на екрані (постійно все змінюється).
 
Результат можна спостерігати в каталозі, який вказали при старті:
 
 image
 
Сам JSON файл:
 
 
{
    "0":
    {
        "x": 205,
        "y": 53,
        "alpha": 1,
        "name": "magic",
        "type": "text",
        "color": "0006FC",
        "font": "Verdana",
        "size": 30,
        "contents": "Тут у нас волшебный текст",
        "width": 417,
        "height": 180
    },
    "1":
    {
        "x": 458,
        "y": 281,
        "alpha": 1,
        "name": "el5",
        "type": "sprite"
    },
...
    "6":
    {
        "type": "collection",
        "name": "btn",
        "0":
        {
            "x": 300,
            "y": 537,
            "alpha": 1,
            "name": "btn_art",
            "type": "sprite"
        }
    }
}

 
Далі збираємо звичайний атлас текстур, Я роблю це за допомогою TexturePackerGUI (не забуваємо вказувати формат Sparrow / Starling)
і кидаємо в зібраний на швидку руку движок. Сам результат видно через N секунд компіляції:
 
 image
 
Стаття вийшла чималенька, тому я вирішив винести повний код скрипта в ісходник (starling міні движок, АСЕТ, скрипт photoshop). Доступно за посиланням: https://www.dropbox.com/s/8jurdz2ze4qzz18/pr.zip

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.