SpriteKit: как добавить / удалить узел и избежать исключения мутации при перечислении?

Я представляю SKScene как простую сетку узлов, где я проектирую структуру данных, созданную большей сеткой журналов.

При запросе визуализации узла в (i,j) позиции в сетке, код ищет доступность медиа (текстуры), и в этом случае добавляет SKSpriteNode. Но если не сразу, запрашивает текстуру с удаленного сервера (в ветке, конечно). Как следствие, поскольку он не доступен сразу, код создает запасной SKShapeNode, чтобы сделать его видимым для пользователя при загрузке текстуры.

Когда и если получена текстура, замещающий резервный SKShapeNode заменяется на SKSpriteNode на вновь созданный SKSpriteNode, сделанный из полученной текстуры.

Каждый узел назван против своей позиции в сетке, иначе. "((Я),(к))".

Кроме того, я работаю с жестом щепотки, который отвечает за перестановку узлов в зависимости от масштаба, образованного щепоткой (гайка не применяет шкалу к сцене).

На бумаге все в порядке, но, к сожалению, я не нашел способа избежать изменения списка дочерних узлов спрайта при перечислении.

Что я должен делать?

    for i in 0..<tilesh {
        for j in 0..<tilesw {
            let row = i
            let col = j + band * tilesw

            if let journal = Journal.journalForLevel(level, row: row, col: col) {

                // --- ask for the journal cover, but if not straght availble, fetch from cache or remote server
                let _ = journal.getCover {

                    // If completion block is invoked, that mean the cover has been fetch from a cache level
                    (journal: Journal) in

                    // If cover is now available, we simply creat the tile as a SKNode
                    createteOrUpdateJournalNode(panel, journal: journal, row: i, col: j)
                }

                // --- Draw journal at each tile if cover already avail, just draw it
                createteOrUpdateJournalNode(panel, journal: journal, row: i, col: j)
            }
            // --- No journal available
            else {
                createteOrUpdateJournalNode(panel, journal: nil, row: i, col: j)
                print("No journal avail for band=\(band) at row=\(row), col=\(col)")
            }
        }   // each j
    }   // each i






   func createteOrUpdateJournalNode(parent: Panel, journal: Journal?, row: Int, col: Int) {

        var node : SKNode

        let alpha:CGFloat = 1 //0.5 + 0.5 * CGFloat((band+1)/panels.count)

        if let journal = journal {

            if let _ = journal.cover {

                let cover   = prepareCover(journal)
                let texture = SKTexture(image: cover)
                let n       = SKSpriteNode(texture: texture)
                node = n

                n.anchorPoint   = CGPoint(x: 0, y: 0)
            }
            else {
                let n       = SKShapeNode(rect: CGRect(x: 0, y: 0, width: w, height: h))
                node = n

                let c = 0.5 + CGFloat(arc4random() % 127) / 127
                n.fillColor     = UIColor(red: c, green: c, blue: 1, alpha: alpha)
                n.strokeColor   = UIColor.blueColor()
                n.lineWidth     = 4
            }
        }
        else {
            let n       = SKShapeNode(rect: CGRect(x: 0, y: 0, width: w, height: h))
            node = n

            let c = 0.5 + CGFloat(arc4random() % 127) / 127
            n.fillColor     = UIColor(red: 1, green: c, blue: c, alpha: alpha)
            n.strokeColor   = UIColor.redColor()
            n.lineWidth     = 4
        }

        let name = "\(band)-\(row)-\(col)"
        node.name = name
        node.position       = CGPoint(x: CGFloat(col) * w * timeScale, y: CGFloat(row) * h)

        if let n = parent.childNodeWithName(name) {
            //n.runAction(SKAction.removeFromParent())
            print ("---- should prune node from band \(band) at \(name)")
        }

        print(">>>> Creating node \(name)")
        parent.addChild(node)
        print("<<<<")
    }

2 ответа

Возможно, вы могли бы избежать проблемы мутации в целом, используя SKSpriteNode в качестве временного заполнителя и назначая новую текстуру только при получении данных. таким образом, используется один и тот же объект / экземпляр, и ваше перечисление не будет затронуто

Я решил проблему, обнаружив, что можно безопасно изменить список узлов в функции обновления SpriteKit следующим образом:

   // SpriteKits gameloop function
    override func update(currentTime: CFTimeInterval) {

        // Mutate list
        for p in panels {
            p.addNodes()
        }
    }

p относится к массиву Panel (см. класс Panel позже в ответе).

Я готовлю дополнительные узлы из цикла рендеринга в моей слегка измененной версии createOrUpdateJounralNode() следующим образом:

    let node = createOrUpdateJournalNode(nil, row: i, col: j)
    panel.addNode(node)

Где panel.addNode () просто ссылается на фрагмент кода следующим образом:

class Panel {
    var addNodesList = [SKNode]()

    func addNode(node: SKNode) {
        addNodesList.append(node)
    }

    func addNodes() {

        if addNodesList.count > 0 {
            for n in addNodesList {
                if let name = n.name {
                    if let n = childNodeWithName(name) {
                        n.removeFromParent()
                        print("---- removing node \(name)")
                    }
                }
                addChild(n)
                print("++++ adding node \(name)")
            }

            addNodesList = [SKNode]()
        }
    }
}

Немного измененная версия createOrUpdateJournalNode() выглядит следующим образом:

    func createOrUpdateJournalNode(journal: Journal?, row: Int, col: Int) -> SKNode {
       // same code than before except we don't add child to parent, but simply return the node

       return node
    }
Другие вопросы по тегам