I am currently making a game where I need random enemies from my array, to spawn in a random location on repeat. This code seems to work okay other than the fact that it can only rotate through each Enemy once. It comes up with an error saying "Attemped to add a SKNode which already has a parent". Any help? Here is my current code:
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max - min) min
}
func spawnEnemy() {
let EnemyArray = [Enemy1, Enemy2, Enemy3, Enemy4, Enemy5, Enemy6]
let randomElement = EnemyArray.randomElement()!
self.addChild(randomElement)
var moveEnemy = SKAction.moveTo(y: -800, duration: 4.0)
let deleteEnemy = SKAction.removeFromParent()
let EnemySequence = SKAction.sequence([moveEnemy, deleteEnemy])
randomElement.run(EnemySequence)
}
func runEnemy() {
run(SKAction.repeatForever(SKAction.sequence([SKAction.run(spawnEnemy), SKAction.wait(forDuration: 2.0)])))
}
CodePudding user response:
as jnpdx suggested, you should spawn new instances of your Enemy class rather than starting with an array of them. you can introduce randomness inside the Enemy class -- for example a random start position or a random color. i would also put your movement and removeFromParent
code inside the class as well. You didn't post your Enemy code, but it might look something like this
class Enemy:SKNode {
var shape:SKShapeNode?
override init() {
super.init()
//how ever you want to graphically represent your enemy... using a SKShapeNode for demo
shape = SKShapeNode(ellipseOf: CGSize(width: 20, height: 40))
shape?.fillColor = .blue
addChild(shape ?? SKNode())
//randomize starting x position
position.x = CGFloat.random(in: -200...200)
position.y = 200
move()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//move and remove this node using SKAction
func move() {
let move = SKAction.moveTo(y: -200, duration: 4.0)
let delete = SKAction.removeFromParent()
let sequence = SKAction.sequence([move, delete])
self.run(sequence)
}
}
then you would simply activate your spawn point from didMove(to view: SKView)
like this
override func didMove(to view: SKView) {
runSpawnPoint()
}
func runSpawnPoint() {
run(SKAction.repeatForever(SKAction.sequence([SKAction.run(spawnEnemy), SKAction.wait(forDuration: 2.0)])))
}
func spawnEnemy() {
let enemy = Enemy() //a brand new Enemy object each time
addChild(enemy)
}
optional: save your spawned Enemy objects in an array if you want to access them later. alternately you can simply query self.children
from your SKScene
since they're all stored there as well. in which case you don't need an additional array for storage.
CodePudding user response:
I have found an answer. So originally my problem was trying to spawn multiple, different-looking enemies, at random. I realized that I could solve the same issue by changing the texture of the Enemy, instead of creating many different Enemy Nodes. In order to spawn enemies at random with an array of textures, it would look something like this:
var enemy1 = SKTexture(imageNamed: "Enemy1")
var enemy2 = SKTexture(imageNamed: "Enemy2")
var enemy3 = SKTexture(imageNamed: "Enemy3")
var enemy4 = SKTexture(imageNamed: "Enemy4")
var enemy5 = SKTexture(imageNamed: "Enemy5")
var enemy6 = SKTexture(imageNamed: "Enemy6")
let EnemyArray = [Enemy1, Enemy2, Enemy3, Enemy4, Enemy5, Enemy6]
let randomElement = EnemyArray.randomElement()!
let enemy = SKSpriteNode(imageNamed: "")
enemy.name = "Enemy"
enemy.texture = randomElement
enemy.size = CGSize(width: 30, height: 30)
enemy.zPosition = 2
self.addChild(enemy)
var moveEnemy = SKAction.moveTo(y: -800, duration: 4.0)
let deleteEnemy = SKAction.removeFromParent()
let EnemySequence = SKAction.sequence([moveEnemy, deleteEnemy])
enemy.run(EnemySequence)
}
func runEnemy() {
run(SKAction.repeatForever(SKAction.sequence([SKAction.run(spawnEnemy), SKAction.wait(forDuration: 2.0)])))
}
Thanks everyone for the help