How to randomly choose a SKSpriteNode? - swift

I want to choose from 4 enemies using random and present it on scene. For that purpose I've made this:
func enemyPicker() -> SKSpriteNode {
var enemyArray = [mouse, robot, drone, block, bird]
var countArray = UInt32(enemyArray.count)
var pickOneEneny = arc4random_uniform(countArray)
var randomElement = Int(pickOneEnemy)
return enemyArray.randomElement
}
But Xcode says to me that SKSpriteNode does not have a member named randomElement. And it surely doesn't, but how would I say to my function that I need it to pick and assign that random Int to an actual enemy from array?
I tried to use this answer but it's not working for me. I also tried to change -> SKSpriteNode to SKTexture, String and "T" and had not any luck with it.
My SpriteNodes are declared like:
var mouse = SKSpriteNode()
let mouseAtlas = SKTextureAtlas(named: "mouse")
var mouseArray = [SKTexture]()
mouseArray.append(mouseAtlas.textureNamed("mouse_0"));
mouseArray.append(mouseAtlas.textureNamed("mouse_1"));
mouseArray.append(mouseAtlas.textureNamed("mouse_2"));
mouse = SKSpriteNode(texture: mouseArray[0]);
self.mouse.position = CGPointMake(CGRectGetMaxX(self.frame), CGRectGetMidY(self.frame) - 138)
self.mouse.size = CGSizeMake(self.mouse.size.width, self.mouse.size.height + mouse.size.height / 2)
self.mouse.name = "mouse"
self.addChild(mouse)

func enemyPicker() -> SKSpriteNode {
let enemyArray = [mouse, robot, drone, block, bird]
return enemyArray[Int(arc4random_uniform(UInt32(enemyArray.count)))]
}

Related

I want to reduce the code in methods that are basically identical and pass in the parameters but don't know where to begin?

I have a spriteKit project where I have many characters across several scenes.
As a beginner I just built each one individually for each scene - which makes for a ton of extra code.
I know I could clean this up with a "Build character class" or something like that...
I am just not sure where to begin.
Here is code from two of the characters in one scene...but imagine 5-10 characters per scene?
Also is there a way a property list could be useful for storing these type of properties?
//BEAR
func buildBear() {
let bearAnimatedAtlas = SKTextureAtlas(named: "Bear")
var bearFrames: [SKTexture] = []
let numImages = bearAnimatedAtlas.textureNames.count
for i in 1...numImages {
let bearTextureName = "bear\(i)"
bearFrames.append(bearAnimatedAtlas.textureNamed(bearTextureName))
}
animatedBear = bearFrames
let firstFrameTexture = animatedBear[0]
bear = SKSpriteNode(texture: firstFrameTexture)
bear.size.height = 370
bear.size.width = 370
bear.position = CGPoint(x: 295, y: 25)
bear.zPosition = 1
bear.name = "bear"
isUserInteractionEnabled = true
addChild(bear)
}
//CAT
func buildCat() {
let catAnimatedAtlas = SKTextureAtlas(named: "Cat")
var catFrames: [SKTexture] = []
let numImages = catAnimatedAtlas.textureNames.count
for i in 1...numImages {
let catTextureName = "cat\(i)"
catFrames.append(catAnimatedAtlas.textureNamed(catTextureName))
}
animatedCat = catFrames
let firstFrameTexture = animatedCat[0]
cat = SKSpriteNode(texture: firstFrameTexture)
cat.size.height = 240
cat.size.width = 240
cat.position = CGPoint(x: 134, y: -38)
cat.zPosition = 2
cat.name = "cat"
isUserInteractionEnabled = true
addChild(cat)
}
How could I clean up something like this - I need different position/size per scene but I imagine I could just override that per scene?
I know I know how to do this! - just not where to start?
Gimme a nudge please!
One of the confusing things about the existence of so many languages is that they each have their own jargon, and their own conventions. The root of your problem, however, has nothing to do with Swift or Sprite Kit. When I read your question, I see code that could use some Abstract Data Types. In Java, you would create an Interface, in C++ you would create a "pure virtual" class. Well a rose by any other name still gets the job done. I recommend creating a Protocol, perhaps called Spritable, to define the types of objects that you intend to build into sprites. It would probably be as simple as this:
protocol Spritable {
var species: String { get }
var height: Int { get }
var width: Int { get }
}
The only other thing that differs between your two functions appears to be the starting position. Since this is not inherent in the meaning of a Spritable object, I would package that data separately. A tuple should do the job. With these revisions, your two functions can be merged into one:
func buildSprite(of creature: Spritable, at position: (x: Int, y: Int, z: Int)) {
let spriteAnimatedAtlas = SKTextureAtlas(named: creature.species)
var spriteFrames: [SKTexture] = []
let numImages = spriteAnimatedAtlas.textureNames.count
for i in 1...numImages {
let spriteTextureName = "\(creature.species.lowercased())\(i)"
spriteFrames.append(spriteAnimatedAtlas.textureNamed(spriteTextureName))
}
animatedSprite = spriteFrames
let firstFrameTexture = animatedSprite[0]
sprite = SKSpriteNode(texture: firstFrameTexture)
sprite.size.height = creature.height
sprite.size.width = creature.width
sprite.position = CGPoint(x: position.x, y: position.y)
sprite.zPosition = position.z
sprite.name = creature.species
isUserInteractionEnabled = true
addChild(sprite)
}
To build a bear, aside from a workshop, you will need to define a struct that implements Spritable:
struct Bear: Spritable {
var species: String { return "Bear" }
var height: Int
var width: Int
init(height: Int, width: Int) {
self.height = height
self.width = width
}
}
Then here would be your function call:
buildSprite(of: Bear(height: 370, width: 370), at: (295, 25, 1))
This is a pretty simple example, and could be solved in a few simpler ways than this. However, I find that the larger a project gets, the greater the benefits of organizing code around Abstract Data Types become, so it's worth taking that approach even in a simple case like this.
I ended up not using a protocol on this.
I simply built a method to construct the sprites - similar to what #TallChuck suggested.
func buildCharacter(name:String, height: CGFloat, width: CGFloat, position: CGPoint, zPosition: CGFloat) {
let animatedAtlas = SKTextureAtlas(named: name)
var animationFrames: [SKTexture] = []
let numImages = animatedAtlas.textureNames.count
for i in 1...numImages {
let textureName = "\(name)\(i)"
animationFrames.append(animatedAtlas.textureNamed(textureName))
}
animatedCharacter = animationFrames
let firstFrameTexture = animatedCharacter[0]
builtCharacter = SKSpriteNode(texture: firstFrameTexture)
builtCharacter.size.height = height
builtCharacter.size.width = width
builtCharacter.position = position
builtCharacter.zPosition = zPosition
builtCharacter.name = name
isUserInteractionEnabled = true
addChild(builtCharacter)
}
It works perfect for building and adding to the scenes - had some issues accessing the nodes names for touch detection but got it sorted. Now trying to figure out how to call actions on the nodes - its all different than the normal way. So I will prolly ask that question next. But overall reduced a ton of repeated code! Thanks for the help.
None of this has to be done in code. With Sprite Kit, you create your bear and your cat via the sprite kit editor, and then you load in the sks file by using the constructor that loads by filename.
This is a similar behavior to how game objects work in unity.

How to change node value

func createBall(){
ballS = SKNode()
let ball = SKSpriteNode(imageNamed: "Ball")
saw.zPosition = 2
ballS.addChild(ball)
self.addChild(ballS)
}
This creates a Ball node with z position 2. But while running the game, I want to create another ball that has a z position at 1. How can I do this, or do I have to make a whole new function that makes a ball node with z position 1?
If each ball sprite has to be a child of the ballS node, then you could do the following.
Define ballS as a property (i.e. after the class definition but before any functions)
let balls = SKNode()
in didMove(to:), add the ballS Node:
addChild(ballS)
Create a addBall function: (don't call it createBall unless all it does create the ball)
func addBall(withZPosition zPos: Int) {
let newBall = SKSpriteNode(imageNamed: "Ball")
newBall.zPosition = zPos
ballS.addChild(newBall)
}
Then simply call addBall(withZPosition:) to create and add a new ball:
addBall(withZPosition: 2)
If you want more control over the ball that is created (e.g. you want to change some of it's properties before adding it to the scene), you can make a createBall function that creates a new ball and returns the new node:
func createBall(withZPosition zPos: Int) -> SKSpriteNode {
let newBall = SKSpriteNode(imageNamed: "Ball")
newBall.zPosition = zPos
return newBall
}
and use this as follows:
let newBall = createBall(withZPosition: 2)
newBall.size = CGSize(x: 50, y:50)
ballS,addchild(newBall)
U can have a global variable so that whenever you want to change u set it and create the ball
so create a variable
var myZposition = 2
and create a ball at the start in didmove to view by calling createBall()
func createBall(){
ballS = SKNode()
let ball = SKSpriteNode(imageNamed: "Ball")
saw.zPosition = myZPosition
ballS.addChild(ball)
self.addChild(ballS)
}
then after these if you want to change the zPosition just set it at the end of didmove to view
myZposition = 1
Then whenever u create balls from here your zPosition would be 1. This would be an easy setup if you have lots of balls and implement a change in zPosition for reaching a specific area of the game
Hope this helped
I'd suggest to use an argument, like:
func createBall(z: Int) {
ballS = SKNode()
let ball = SKSpriteNode(imageNamed: "Ball")
saw.zPosition = z
ballS.addChild(ball)
self.addChild(ballS)
}
And use it as:
createBall(z: 2)

Adding multiple sprite nodes to the scene using single function/method in swift

Let's say I have 10 nodes, where all of the nodes are the dots image, which are node1 thru node10. I create node1 as the following:
func createNode1() -> SKNode {
let spriteNode = SKNode()
spriteNode.position = CGPointMake(CGRectGetMidX(self.frame)/1.35, CGRectGetMidY(self.frame)/1.32)
let sprite = SKSpriteNode(imageNamed: "dot_1")
sprite.zPosition = 3.0
sprite.name = "A1_Dot"
spriteNode.addChild(sprite)
return spriteNode
}
I create the rest of nodes by creating 9 more functions, where next one would be as func createNode2etc, all the way up to 10 functions, where the only difference between them is node's name and its location. Basically each node has different location in the scene and of course different image name. Is there a way to load of the 10 nodes to the scene at once and manipulate node's locations at the time of use.? I'm looking for a way to load all 10 nodes to scene using a single function or method and assign node's positions within this same function. Thanks.
You need to use a loop to iterate through an array of positions, and move your code that adds the node to the scene into the loop:
let positions = [CGPointMake(CGRectGetMidX(self.frame)/1.35, CGRectGetMidY(self.frame)/1.32), ... /*add your 9 other positions here*/]
positions.enumerate().forEach { (index, point) in
let spriteNode = SKNode()
spriteNode.position = point
let sprite = SKSpriteNode(imageNamed: "dot_\(index + 1)")
sprite.zPosition = 3.0
sprite.name = "A\(index + 1)_Dot"
spriteNode.addChild(sprite)
// Add spriteNode to the scene here
}
You can either use a loop as Jugale suggested or you could just pass the values you want into the method
For example
func createNode1(imageNamed imageNamed: String, name: String, pos: CGPoint) -> SKNode {
let spriteNode = SKNode()
spriteNode.position = pos
let sprite = SKSpriteNode(imageNamed: imageNamed)
sprite.zPosition = 3.0
sprite.name = name
spriteNode.addChild(sprite)
return spriteNode
}
And now in your scene you can add the nodes like so
let node1Pos = ...
node1 = createNode1(imageNamed: "...", name: "A1_Dot", pos: node1Pos)
let node2Pos = ...
node2 = createNode1(imageNamed: "...", name: "A1_Dot", pos: node2Pos)
I am saying ImageNamed twice in the create Node function because when you pass stuff into functions Swift by default does not require the first description to be typed when calling the method. (see below)
So if would say imageNamed only once than you would call it like so.
node1 = createNode1("...", pos: node1Pos)
Also your creating node function could be simplified, unless you specifically want to return a SKNode in the method.
func createNode1(imageNamed imageNamed: String, name: String, pos: CGPoint) -> SKSpriteNode {
let sprite = SKSpriteNode(imageNamed: imageNamed)
sprite.position = pos
sprite.zPosition = 3.0
sprite.name = name
addChild(sprite)
return sprite
}
Either way is acceptable solution. Here are full details. In baseScene we create function createNodes and call that function in didMoveToView where nodeA1 is given position and added to the scene as shown below.
override func didMoveToView(view: SKView) {
let nodeA1Pos = CGPointMake(CGRectGetMidX(self.frame)/1.5, CGRectGetMidY(self.frame)/2.0)
nodeA1 = createNodes(imageNamed: "dot_1", name: "A1_Dot", pos: nodeA1Pos)
addChild(nodeA1)
}
Then in Level1Scene which is subclass of baseScene we just give a new position to nodeA1 which will override position originally set in baseScene:
override func didMoveToView(view: SKView) {
super.didMoveToView(view)
nodeA1.position = CGPointMake(CGRectGetMidX(self.frame)/1.3, CGRectGetMidY(self.frame)/0.67)
}
This way of subclassing saves a lot of time and code as only one function is used to generate all common sprite nodes.
All thanks to crashoverride777 !!!!

Init multiple SKSpriteNodes

I'm trying to add multiple SKSpriteNodes to my SKSpriteNode subclass. In my case it is important that I can call the childNodes out of the Scene.
Is there a way to create such global available Sprites in a loop?
When you add your sprites to the parent node remember to assign them a name
let parent = SKSpriteNode()
let child0 = SKSpriteNode()
child0.name = "child0"
parent.addChild(child0)
let child1 = SKSpriteNode()
child1.name = "child1"
parent.addChild(child1)
let child2 = SKSpriteNode()
child2.name = "child2"
parent.addChild(child2)
Later on you can retrieve them this way using the name
func foo() {
guard let child1 = parent.childNodeWithName("child1") as? SKSpriteNode else { return }
child1.alpha = 0.5
}

Why is there multiple collision calls Sprite Kit Swift

I am building an iOS swift game with collision. The hero is being bombarded by small stars coming from the right side of the screen. Every time a star hits the hero, the score is iterated and the star is removed. But, more than one point is added to the score.
Node Set-up:
var star = SKSpriteNode(imageNamed: "star")
star.size = CGSizeMake(30, 30)
star.zPosition = 10
var starPhysicsRect = CGRectMake(star.position.x - star.frame.size.width / 2, star.position.y - star.frame.size.width / 2, star.frame.size.width, star.frame.size.height)
star.physicsBody = SKPhysicsBody(edgeLoopFromRect: starPhysicsRect)
star.physicsBody?.restitution = 0
star.name = "star"
star.physicsBody?.mass = 0
return star
didBeginContact Function:
func didBeginContact(contact: SKPhysicsContact) {
//collision variables
var firstBodyNode = contact.bodyA.node as! SKSpriteNode
var secondBodyNode = contact.bodyB.node as! SKSpriteNode
var firstNodeName = firstBodyNode.name
var secondNodeName = secondBodyNode.name
//other variables
var scoreLabel: SKLabelNode = constantsInstance.scoreLabel(position: CGPointMake(self.frame.size.width * 0.86, self.frame.size.height * 0.928))
//check if hero hit star
if firstNodeName == "hero" && secondNodeName == "star" {
println("star hit")
secondBodyNode.removeFromParent()
//iterate score
childNodeWithName("scoreLabel")?.removeFromParent()
scoreLabel.text = "\(counterForScore)"
counterForScore++
self.addChild(scoreLabel)
}
}
I get "star hit" printed to the console several times as well. Why would the didBeginContact function be called more than once if I remove the star on the first call of the function?
I have run simulations on a smaller scale and found that the star goes fairly deep into the hero (because of the SKAction), if there is no remove command. Could it be that the didBeginContact function is called many times while the star is going into the hero and before it is removed?
All help is greatly appreciated.
You can check if parent of the SKSpriteNode is nil before continuing.
func didBeginContact(contact: SKPhysicsContact) {
var firstBodyNode : SKSpriteNode!
var secondBodyNode : SKSpriteNode!
// Assuming that the contact test bit mask of star is greater than hero. If it's not you should reverse the condition.
if contact.bodyA.contactTestBitMask < contact.bodyB.contactTestBitMask {
firstBodyNode = contact.bodyA.node as! SKSpriteNode
secondBodyNode = contact.bodyB.node as! SKSpriteNode
} else {
firstBodyNode = contact.bodyB.node as! SKSpriteNode
secondBodyNode = contact.bodyA.node as! SKSpriteNode
}
var firstNodeName = firstBodyNode.name
var secondNodeName = secondBodyNode.name
if firstNodeName == "hero" && secondNodeName == "star" {
if secondBodyNode.parent != nil {
// Your code.
}
}
}
Could it possibly be due to the physicsBody:
var star = SKSpriteNode(imageNamed: "star")
star.size = CGSizeMake(30, 30)
star.zPosition = 10
var starPhysicsRect = CGRectMake(star.position.x - star.frame.size.width / 2, star.position.y - star.frame.size.width / 2, star.frame.size.width, star.frame.size.height)
star.physicsBody = SKPhysicsBody(edgeLoopFromRect: starPhysicsRect)
Have a look at the logic creating the starPhysicsRect:
(star.position.x - star.frame.size.width / 2, star.position.y - star.frame.size.width / 2, star.frame.size.width, star.frame.size.height)
From what I can see this translates to the following values:
(-15, -15, 30, 30) those negative position-coordinates seem odd. Are you sure you don't just want: SKPhysicsBody(rectangleOfSize: star.size) ?