问题
So I have 3 different obstacles which spawn randomly and get removed after 3 seconds, but if for example Obstacle1 is already spawned and the random Number tells the program to spawn another Obstacle1, I get a Error that says
"Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attemped to add a SKNode which already has a parent"
heres the code of Obstacle1, the other two are almost the same just the names are different:
func createObst1() {
self.addChild(obst1)
path = UIBezierPath(arcCenter: CGPoint(x: 0, y: -170), radius: circle.frame.size.height / 2 + obst1.frame.size.width / 2 - 4, startAngle: CGFloat(rad) * 2, endAngle: CGFloat(rad) + CGFloat(M_PI * 2.5), clockwise: true)
let follow = SKAction.follow(path.cgPath, asOffset: false, orientToPath: true, duration: TimeInterval(roundDuration))
obst1.isHidden = false
let fadeOutInstant = SKAction.fadeOut(withDuration: 0)
let fadeIn = SKAction.fadeIn(withDuration: 0.3)
let fadeOut = SKAction.fadeOut(withDuration: 0.3)
let pause = SKAction.wait(forDuration: 2.4)
let remove = SKAction.removeFromParent()
let sequence = SKAction.sequence([fadeOutInstant, fadeIn, pause, fadeOut, remove])
let group = SKAction.group([follow, sequence])
obst1.run(group)
}
Do I have to create multiple Obstacle1 functions with the same Code in it or is there a other way do solve my problem?
Here's also the Code how I randomly spawn the Obstacles in case it helps: func gameStarted() {
Timer.scheduledTimer(timeInterval: TimeInterval(randomDelay), target: self, selector: #selector(GameScene.obstSwitch), userInfo: nil, repeats: true)
}
func obstSwitch() {
let rand = arc4random_uniform(3)
switch rand{
case 0:
createObst1()
case 1:
createObst2()
case 2:
createObst3()
default:
break
}
}
回答1:
Try this code:
class GameScene: SKScene, SKPhysicsContactDelegate {
let obstacles = [
SKSpriteNode(color:.brown, size:CGSize(width: 50, height: 50)),
SKSpriteNode(color:.white, size:CGSize(width: 50, height: 50)),
SKSpriteNode(color:.black, size:CGSize(width: 50, height: 50)),
]
func getRandomObstacle(fromArray array:[SKSpriteNode])->SKSpriteNode{
return array[Int(arc4random_uniform(UInt32(array.count)))]
}
func spawnObstacle(atPosition position:CGPoint) {
if let obstacle = getRandomObstacle(fromArray: obstacles).copy() as? SKSpriteNode {
obstacle.position = position
//apply action that fade out and remove the node after 3 seconds
let fadeOut = SKAction.fadeOut(withDuration: 3)
let remove = SKAction.removeFromParent()
let sequence = SKAction.sequence([fadeOut,remove])
obstacle.run(sequence, withKey: "aKey")
addChild(obstacle)
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
spawnObstacle(atPosition: location)
}
}
}
This code basically spawn a copy of an random obstacle on every tap. Because you make copies, there are no worries about the error you are getting currently.
To use SKAction
to spawn nodes, as you said, as an alternative to Timer you would create an action sequence with two elements, a wait action, and a block that spawns a node. This is how action sequence might look like:
override func didMove(to view: SKView) {
//Setup scene and nodes
let wait = SKAction.wait(forDuration: 0.5)
let spawn = SKAction.run({[unowned self] in
//calculate obstalce initial position here, eg. randomize it
let spawnLocation = //...
self.spawnObstacle(atPosition: spawnLocation)
})
let sequence = SKAction.sequence([wait, spawn])
let loop = SKAction.repeatForever(sequence)
run(loop , withKey:"spawning")
}
来源:https://stackoverflow.com/questions/41784340/how-to-spawn-multiple-of-the-same-spritenode-at-once