天天看點

UI制作中的九宮格批量輸出

遊戲的UI制作中,出于對資源的壓縮和重用,會對一些有規則的圖檔劃分九宮格,例如下圖

UI制作中的九宮格批量輸出
UI制作中的九宮格批量輸出
UI制作中的九宮格批量輸出

中間部分如果是純色的話,完全可以挖去,然後在U3D的圖集精靈的border中設定對應的top,right,bottom,left,這樣既可以節省資源,又可以重用到不同大小的但具有一樣顔色的地方。

傳統的photoshop可以挖去中間部分,但是項目中的美術大哥覺得還是批量處理比較有效率,是以用Air寫了一個批量處理九宮格圖檔的工具。

在這個工具中,美術需要把九宮格的四個border值填充到名字,順序為top, right, bottom, left例如

UI制作中的九宮格批量輸出
UI制作中的九宮格批量輸出
UI制作中的九宮格批量輸出

top=20px, right=20px, bottom=20px, left=20px,那麼圖檔的命名為Button#21#22#23#24.png

(Button#top#right#bottom#left.png)

這樣多個圖檔導入工具中,導出來的效果

UI制作中的九宮格批量輸出
UI制作中的九宮格批量輸出
UI制作中的九宮格批量輸出

這樣資源和可重用度都得到很好的提升。

工具是用air來做的,核心源碼如下

private static var file:File = new File();
private var fileOutput:File = new File();
private static var fileStream:FileStream = new FileStream();
private var loader:Loader = new Loader();
private var bit:Bitmap = new Bitmap();
private var bytes:ByteArray = new ByteArray;
private static const SLICE_LIST_LENGTH:int = 9;
private static const SPLIT_ROW_COUNT:int = 3;
private static const SPLIT_COL_COUNT:int = 3;
private static var _transformMatrix:Matrix;
private static var _splitRow:Vector.<int>;
private static var _splitCol:Vector.<int>;
private static var _rect:Rectangle;
private static var _dstPoint:Point;
private var m_iIndex:int = 0;
private var m_arrFile:Array;
private var fileAns:File;

//mxml OnInit初始化函數
private function init():void
{
    _splitRow = new Vector.<int>(SPLIT_ROW_COUNT + 1, true);
    _splitCol = new Vector.<int>(SPLIT_COL_COUNT + 1, true);
    _rect = new Rectangle();
    _dstPoint = new Point();
    _transformMatrix = new Matrix();

}
//點選按鈕後彈出檔案夾選擇對話框,目前隻支援png檔案
private function onImport(e:MouseEvent):void
{
    file.addEventListener(FileListEvent.SELECT_MULTIPLE, this.onSelect);
    file.browseForOpenMultiple("請選擇png檔案(可選擇多個)", [new FileFilter("png檔案","*.png;")]);
}
//選擇圖檔,可以多個以及輸出檔案夾
//檔案的命名規範是 abc#top#right#bottom#left.png
private function onSelect(e:FileListEvent):void
{
    m_arrFile = e.files;
    fileOutput.addEventListener(Event.SELECT, this.OnDoIt);
    fileOutput.browserForDirectory("請選擇輸出的檔案夾");
}
//串行順序處理圖檔
private function OnDoIt(e:Event):void
{
    fileAns = e.target as File;
    m_iIndex = 0;
    DealFile(m_arrFile(m_iIndex));
}
//讀取圖檔,轉成二進制
private function DealFile(file:File):void
{
    var fileByte:ByteArray = new ByteArray();
    var fs:FileStream = new FileStream();
    fs.open(file, FileMode.READ);
    fs.readBytes(fileByte, 0, fs.bytesAvailable);
    fs.close();
    var myLoader:Loader = new Loader();
    myLoader.unload();
    myLoader.loadBytes(fileByte);
    myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, image_completeHandler);
}
//把圖檔的二進制轉成bitmapData
private function image_completeHandler(event:Event):void
{
    bit.bitmapData = Bitmap(event.currentTarget.content).bitmapData;
    Export();
}
//把圖檔的中間部分挖空,導出圖檔
private function Export():void
{
    var pngSource:BitmapData = new BitmapData(bit.width,bit.height,true,0);
    pngSource.draw(bit.bitmapData);
    var sName:String = m_arrFile[m_iIndex].name.replace(".png","");
    trace(sName);
    var arrName:Array = sName.split("#");
	//width = left + right; height = top + bottom;
    var ansPng:BitmapData = createScaleBitmapData(pngSource,int(arrName[2])+int(arrName[4]),int(arrName[1])+int(arrName[3]),int(arrName[1]),int(arrName[2]),int(arrName[3]),int(arrName[4]));
	//用到PNGEncoder庫,把bitmapData轉成二進制
    var ba:ByteArray = PNGEncoder.encode(ansPng);
	//重命名,後面添加_Nine
    var ff:File = fileAns.resolvePath(m_arrFile[m_iIndex].name.replace(".png","_Nine.png"));
    //寫入檔案
	var fileStream:FileStream = new FileStream();
    fileStream.open(ff, FileMode.WRITE);
    fileStream.writeBytes(ba);
    fileStream.close();
	//順序執行後面的圖檔
    if(++m_iIndex < m_arrFile.length)
    {
        DealFile(m_arrFile[m_iIndex]);
    }
}

private static function createScaleBitmapData(link:BitmapData, width:int, height:int, top:int, right:int, bottom:int, left:int):BitmapData
{
    if(width < left + right)
    {
        width = left + right;
    }
    if(height < top + bottom)
    {
        height = top + bottom;
    }
    var result:BitmapData = new BitmapData(width, height, true, 0);
	//擷取九宮格9個小塊的BitmapData
    var gridList:Vector.<BitmapData> = createBitmapDataGridList(link, top, right, bottom, left);
    updateSplitData(width, height, top, right, bottom, left);
    for(var i:int = 0; i < SPLIT_COL_COUNT; i++)
    {
        for(var j:int = 0; j < SPLIT_ROW_COUNT; j++)
        {
            var grid:BitmapData = gridList[i * 3 + j];
            if(_splitRow[j + 1] - _splitRow[j]==0 || _splitCol[i + 1] - _splitCol[i] == 0)
            {
                continue;
            }
			//把原點平移到對應slice的左上角
            _transformMatrix.a = (_splitRow[j + 1] - splitRow[j]) / grid.width;
            _transformMatrix.b = 0;
            _transformMatrix.c = 0;
            _transformMatrix.d = (_splitCol[i + 1] - _splitCol[i]) / grid.height;
            _transformMatrix.tx = _splitRow[j];
            _transformMatrix.ty = _splitCol[i];
            result.draw(grid, _transformMatrix);
        }
    }
    return result;
}
//存儲9個小圖檔的BitmapData
private static function createBitmapDataGridList(link:BitmapData, top:int, right:int, bottom:int, left:int) : Vector.<BitmapData>
{
    var source:BitmapData = link;
    if((top + bottom) > source.height || (left + right) > source.width)
    {
        throw new Error("圖檔九宮格參數設定錯誤:左右切片寬度之和不能大于圖檔寬度,上下切片高度之和不能大于圖檔高度!");
    }
    var result:Vector.<BitmapData> = new Vector.<BitmapData>(SLICE_LIST_LENGTH, true);
    updateSplitData(source.width, source.height, top, right, bottom, left);
    for(var i:int = 0; i < SPLIT_COL_COUNT; i++)
    {
        for(var j:int = 0; j < SPLIT_ROW_COUNT; j++)
        {
            _rect.x = _splitRow[j];
            _rect.y = _splitCol[i];
            _rect.width = _splitRow[j+1] - _splitRow[j];
            _rect.height = _splitCol[i+1] - _splitCol[i];
            if(_rect.width == 0 || _rect.height == 0)
            {
               continue;
            } 
            var bmpd:BitmapData = new BitmapData(_rect.width, _rect.height, true, 0);
            bmpd.copyPixels(source, _rect, _dstPoint);
            result[i * 3 + j] = bmpd;
        }
    }
    return result;
}
//劃分9個小塊的位置線
private static function updateSplitData(width:int, height:int, top:int, right:int, bottom:int, left:int):void
{
    _splitRow[0] = 0;
    _splitRow[1] = left;
    _splitRow[2] = width - right;
    _splitRow[3] = width;
    _splitCol[0] = 0;
    _splitCol[1] = top;
    _splitCol[2] = height - bottom;
    _splitCol[3] = height;
}
           

原理是先把九宮格的九個小塊的bitmapData記錄到一個數組裡面,然後根據劃分的top,right,bottom,left的大小取出對應的bitmapData拼成新圖。

特别地,如果是三宮格,如下圖

UI制作中的九宮格批量輸出
UI制作中的九宮格批量輸出
UI制作中的九宮格批量輸出

所有引用這張精靈圖的寬度都是原大小228px,固定不變的,不能拉伸水準方向,那麼需要把left和right設為width的一半,垂直方向可以拉伸,還是20px。同理如果是垂直方向不能拉伸,固定不變,則top和bottom設為height的一半。

繼續閱讀