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
}