Как заставить SKTextureAtlas, созданный из словаря, не изменять размер текстур?

В моем проекте текстуры генерируются процедурно методом, предоставляемым PaintCode (код-краски).

Затем я создаю SKTextureAtlas из словаря, созданного с помощью UIImage, сгенерированного этими методами:
myAtlas = SKTextureAtlas(dictionary: myTextures)

Наконец, текстуры извлекаются из атласа с использованием textureNamed: var sprite1 = SKSpriteNode(texture:myAtlas.textureNamed("texture1"))

Но отображаемые узлы на симуляторе iPhone4S имеют двойной размер. И тройной размер на симуляторе iPhone 6 Plus.

Кажется, что при инициализации atlas вычисляет изображения с разрешением устройства. Но сгенерированные изображения уже имеют правильный размер и не нуждаются в изменении. См. раздел Метод рисования ниже.

Вот описание сгенерированного изображения:
<UIImage: 0x7f86cae56cd0>, {52, 52}

И описание соответствующей текстуры в атласе:
<SKTexture> 'image1' (156 x 156)

Это для iPhone 6 Plus с использованием изображений @3x, поэтому размер x3.

А для iPhone 4S с использованием изображений @2x, как и ожидалось:
<UIImage: 0x7d55dde0>, {52, 52}
<SKTexture> 'image1' (156 x 156)

Наконец, scaleproperty для сгенерированного UIImage установлено правильное разрешение устройства: 2.0 для @2x (iPhone 4S) и 3.0 для @3x (iPhone 6 Plus).

Вопрос

Итак, что я могу сделать, чтобы избежать изменения размера изображений в атласе?

Метод рисования

PaintCode генерирует следующие методы рисования:

public class func imageOfCell(#frame: CGRect) -> UIImage {
    UIGraphicsBeginImageContextWithOptions(frame.size, false, 0)
    StyleKit.drawCell(frame: frame)

    let imageOfCell = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()

    return imageOfCell
}

Обновление 1

Сравнение двух подходов к созданию SKTextureAtlas

// Some test image
let testImage:UIImage...


// Atlas creation
var myTextures = [String:UIImage]() 

myTextures["texture1"] = testImage
myAtlas = SKTextureAtlas(dictionary: myTextures)

// Create two textures from the same image 
let texture1 = myAtlas.textureNamed("texture1")
let texture2 = SKTexture(image:testImage)

// Wrong display : node is oversized
var sprite1 = SKSpriteNode(texture:texture1)

// Correct display
var sprite2 = SKSpriteNode(texture:texture2)

Кажется, что проблема заключается в SKTextureAtlas из словаря, так как инициализация SKSpriteNode не использует свойство scale из UIImage для правильного размера узла.

Вот описания на консоли: - текстура1: '' (84 x 84) - текстура2: 'текстура1' (84 x 84)

texture2 скучаю по data! Это может объяснить отсутствие информации об масштабе для правильного определения размера узла:

размер узла = размер текстуры разделить на масштаб текстуры.

Обновление 2

Проблема возникает, когда свойство масштаба UIImage отличается от единицы.

Таким образом, вы можете использовать следующий метод для создания изображения:

func imageOfCell(frame: CGRect, color:SKColor) -> UIImage {
     UIGraphicsBeginImageContextWithOptions(frame.size, false, 0)

     var bezierPath = UIBezierPath(rect: frame)
     color.setFill()
     bezierPath.fill()
     let imageOfCell = UIGraphicsGetImageFromCurrentImageContext()!
            UIGraphicsEndImageContext()

     return imageOfCell

    }

введите здесь описание изображения


person Dominique Vial    schedule 22.07.2015    source источник
comment
Проблема все еще здесь, в iOS 9/Xcode 7...   -  person Dominique Vial    schedule 30.09.2015


Ответы (2)


Проблема возникает из-за использования SKTextureAtlas(dictionary:) для инициализации atlas.

SKTexture, созданный с использованием этого метода, не встраивает данные, связанные со свойством scale изображения. Таким образом, при создании SKSpriteNode с помощью init(texture:) отсутствие информации о масштабе в текстуре приводит к выбору размера текстуры вместо размера изображения.

Один из способов исправить это — указать размер узла во время SKSpriteNode создания: init(texture:size:)

person Dominique Vial    schedule 22.07.2015
comment
Разве вы не создаете атлас текстур больше, чем нужно (в 3 раза на 6+)? - person 0x141E; 22.07.2015
comment
Нет. Единственная разница заключается в способе создания текстур. Кстати, спасибо за помощь! - person Dominique Vial; 23.07.2015
comment
Вы можете попытаться получить такое же поведение, создав другой спрайт, используя то же сгенерированное изображение, но текстуру, созданную с этим изображением без атласа. И не забудьте вызвать imageOfCell со значением шкалы 0. - person Dominique Vial; 23.07.2015
comment
Если вы извлекаете текстуру из атласа, а затем печатаете ее размер с помощью println (texture.size()), я почти уверен, что это размер текстуры в атласе. Я подозреваю, что вы изменяете размер спрайта, когда создаете его с помощью init(texture:size). - person 0x141E; 23.07.2015
comment
Проблема в размере SKSpriteNode, а не в размере текстуры. Просто попробуйте модификацию, которую я предоставил вам выше, и вы увидите, что в зависимости от того, как создается текстура, SKSpriteNode отображается по-разному. - person Dominique Vial; 23.07.2015
comment
Если вы установите scale = 1.0, размер текстуры будет правильным, верно? И тогда ваш спрайт будет нужного размера. - person 0x141E; 23.07.2015
comment
Гум. Похоже, мои прецедентные комментарии не были поняты. Итак, другими словами. Проблема в том, что в зависимости от того, как генерируется текстура, свойство scale элемента UIImage используется или нет. - person Dominique Vial; 23.07.2015
comment
Таким образом, изменение свойства scale не имеет смысла, поскольку эти данные актуальны и важны. Этот ответ дает обходной путь к ошибке генерации атласа текстур. - person Dominique Vial; 23.07.2015
comment
Кстати, я добавляю обновление 2 к вопросу со скриншотом. - person Dominique Vial; 23.07.2015
comment
Метод imageOfCell генерируется PaintCode и непосредственно вставляется в код каждый раз при изменении рисунка PaintCode. То, как он обрабатывает scale, нельзя изменить, его можно настроить вручную после каждого поколения. Непрактично. - person Dominique Vial; 23.07.2015

Из документации по параметру scale для UIGraphicsBeginImageContextWithOptions,

Масштабный коэффициент, применяемый к растровому изображению. Если указать значение 0,0, масштабный коэффициент будет равен масштабному коэффициенту главного экрана устройства.

Поэтому, если вы хотите, чтобы текстуры были одинакового «размера» на всех устройствах, установите это значение на 1.0.

ИЗМЕНИТЬ:

override func didMoveToView(view: SKView) {        
    let image = imageOfCell(CGRectMake(0, 0, 10, 10),scale:0)

    let dict:[String:UIImage] = ["t1":image]

    let texture = SKTextureAtlas(dictionary: dict)
    let sprite1 = SKSpriteNode(texture: texture.textureNamed("t1"))
    sprite1.position = CGPointMake (CGRectGetMidX(view.frame),CGRectGetMidY(view.frame))
    addChild(sprite1)
    println(sprite1.size)

    // prints (30.0, 30.0) if scale = 0
    // prints (10,0, 10,0) if scale = 1
}


func imageOfCell(frame: CGRect, scale:CGFloat) -> UIImage {
    UIGraphicsBeginImageContextWithOptions(frame.size, false, scale)

    var bezierPath = UIBezierPath(rect: frame)
    UIColor.whiteColor().setFill()
    bezierPath.fill()
    let imageOfCell = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()

    return imageOfCell
}
person 0x141E    schedule 22.07.2015
comment
Проблема заключается в генерации текстуры, а не в генерации изображения с использованием UIGraphicsBeginImageContextWithOptions. - person Dominique Vial; 22.07.2015
comment
UIGraphicsBeginImageContextWithOptions выполните задание, установив правильное значение, соответствующее главному экрану устройства. - person Dominique Vial; 22.07.2015
comment
Из документации UIImage: Если вы загружаете изображение из файла, имя которого содержит модификатор @2x, масштаб устанавливается равным 2,0. Вы также можете указать явный коэффициент масштабирования при инициализации изображения из изображения Core Graphics. Предполагается, что все остальные изображения имеют масштабный коэффициент 1,0.... - person Dominique Vial; 22.07.2015
comment
Если вы умножите логический размер изображения (хранящийся в свойстве size) на значение этого свойства, вы получите размеры изображения в пикселях - person Dominique Vial; 22.07.2015
comment
SKTextureAtlas использует свойство scale объекта UIImage при создании текстуры. Если изображение имеет размер 10x10 и его масштаб равен 3, результирующая текстура будет иметь размер 30x30. - person 0x141E; 22.07.2015
comment
SKTextureAtlas, похоже, не использует свойство scale, когда оно генерируется с использованием словаря. - person Dominique Vial; 22.07.2015
comment
Какую версию Xcode вы используете? Текстура и атлас текстур используют scale так же, как и в 6.4. - person 0x141E; 22.07.2015
comment
Давайте попробуем на вашей стороне использовать код, который я публикую в Обновлении 1. - person Dominique Vial; 22.07.2015
comment
А затем отобразите размер нового спрайта. - person Dominique Vial; 22.07.2015
comment
Кажется, SKTextureAtlas(dictionary:) уничтожить данные в словаре! Таким образом, вы должны создать sprite2 перед вызовом ``SKTextureAtlas(dictionary:)`! - person Dominique Vial; 22.07.2015