swift - use an integer for loop in a variable's name - swift

I' like to doe something like this :
for i in 0...3 {
let skspritenode\(i) = SKSpriteNode(imageNamed: "Layer_\(i)")
self.addChild(skspritenode\(i))
}
that doesn't work because my skspritenode(i) is a let and not a string
and I have tried this:
for i in 0...3 {
let skspritenode = SKSpriteNode(imageNamed: "Layer_\(i)")
self.addchild(skspritenode)
}
which doesn't work because skspritenode already has a parent.
Is there a way of doing this in a for loop or do i have to declare each let separately? Thank you in advanced

Why would you need to name each node differently? skspritenode variable is scoped in for loop, thus the following code should work just fine:
for i in 0...3 {
let skspritenode = SKSpriteNode(imageNamed: "Layer_\(i)")
self.addchild(skspritenode)
}
Your point
which doesn't work because skspritenode already has a parent
is incorrect, since you're creating new SKSpriteNode on each iteration.

Related

Assigning texture to SKSpriteNode is not working

Changing the texture of a SKSpriteNode is not working
I'm trying to learn SpriteKit and am running into something that is puzzling me. I am trying to display a sprite. I try two methods that should work, but only one is actually causing the sprite to be displayed on the screen! I would really like to actually understand why. Can anybody help?
This works:
func loadLevel() {
let point: CGPoint = CGPoint(x:300,y:300)
var block = SKSpriteNode(imageNamed: "Block_blue")
block.position = point
self.addChild(block)
}
This does not:
func loadLevel() {
let point: CGPoint = CGPoint(x:300,y:300)
var block = SKSpriteNode()
block.position = point
block.texture = SKTexture(imageNamed: "Block_blue")
self.addChild(block)
}
I would expect both functions to result in displaying the sprite, but only the first works? I would like to understand what is going on here.

Naming Sprites compared to i in for loop(swift)

I was running into this problem that instead of writing this:
let enemy1 = SKSpriteNode(imageNamed: "enemy")
let enemy2 = SKSpriteNode(imageNamed: "enemy")
let enemy3 = SKSpriteNode(imageNamed: "enemy")
over and over again and making a thousand enemies, I would just make a for loop like this:
for i in 1...1000{
let enemy(i) = SKSpriteNode(imageNamed: "enemy")
}
but as you would know just doing enemy(i) doesn't work.
If you know the answer to this problem or can help me in any way by sugesting any other technique please reply.
Any help would be much appreciated :)
for statement is like this:
for value in range {
statements
}
I've written a simple example:
As #ColGraff mentiond. for the name of the enemy you can code like this: SKSpriteNode(imageNamed: "enemy\(i)"). but not as the name of the variable.
var enemy = [SKSpriteNode]()
for _ in 1...1000 {
enemy.append(SKSpriteNode(imageNamed: "enemy"))
// or use this:
// enemy[i] = SKSpriteNode(imageNamed: "enemy")
}

Init subclass of SKSpriteNode using .sks file Xcode 8

In my GameScene.sks I have a SKSpriteNode which represents a "ball".
I need to subclass SKSpriteNode as Ball:
class Ball:SKSpriteNode {
//custom init
}
In my scene, I would like init my Ball using the SKSpriteNode in the .sks.
As I'm using Xcode 8, I tried to use the custom class in my SKSpriteNode:
self.ball = self.childNode(withName: "ball") as! Ball
But my app crashes at this line...
Also I'm not sure how to create a custom initializer for my subclass.
FYI I would prefer to avoid having something like:
class Ball:SKNode {
var sprite:SKSpriteNode!
}
let ball = Ball()
ball.sprite = self.childNode(withName: "ball") as? SKSpriteNode
I think the problem here is that you are not assigning a name to the sprite you add in the scene editor.
You could retrieve your sprite searching for type instead that searching for name
Try this
self.ball = self.children.flatMap { $0 as? Ball }.first!

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 !!!!

How to copy SKSpriteNode with SKPhysicsBody?

I am curious about a situation that I came across today when trying to unarchive and copy a SKSpriteNode from one SKScene to another. In the output from the playground below you can see that both linearDamping and angularDamping or not being maintained after the copy (they seem to be dropping back to default values)
// PLAYGROUND_SW1.2 - SKSpriteNode Copy
import UIKit
import SpriteKit
// ORIGINAL
let spriteNode = SKSpriteNode()
spriteNode.name = "HAPPY_NODE"
let size = CGSize(width: 55.0, height: 150.0)
let physics = SKPhysicsBody(rectangleOfSize: size)
physics.linearDamping = 0.123
physics.angularDamping = 0.456
spriteNode.physicsBody = physics
// COPY
let spriteCopy = spriteNode.copy() as! SKSpriteNode
// ORIGINAL
spriteNode.name
spriteNode.physicsBody?.linearDamping
spriteNode.physicsBody?.angularDamping
spriteNode.physicsBody?.area
// COPY
spriteCopy.name
spriteCopy.physicsBody?.linearDamping
spriteCopy.physicsBody?.angularDamping
spriteCopy.physicsBody?.area
PLAYGROUND OUTPUT
I am not sure that I am copying this correctly, both SKSpriteNode and SKPhysicsBody conform to NSCopying If you look at the output above the area property is maintained after the copy and to my knowledge this is based on the size specified when the SKPhysicsBody was created.
Can anyone cast some light on this and maybe provide me with a pointer as to how I should be deep copying an SKSpriteNode?
I take one way to resolve your problem, probably is not the best way, but
//COPY
let spriteCopy = spriteNode.copy() as SKSpriteNode
let physicsCopy:SKPhysicsBody = spriteNode.physicsBody!;
...
//COPY PHYSICS BODY HARD MODE
spriteCopy.physicsBody = physicsCopy;
To fix this problem, I created one extension, and #mogelbuster suggested override default copy(), ant it sounds great.
extension SKSpriteNode
{
override open func copy() -> Any {
let node = super.copy() as! SKSpriteNode;
node.physicsBody = super.physicsBody;
return node;
}
}
With this extension you can do it, the default copy() method return Any because this you need cast to SKSpriteNode.
// COPY
let spriteCopy = spriteNode.copy() as! SKSpriteNode;
// ORIGINAL
spriteNode.name
spriteNode.physicsBody?.linearDamping
spriteNode.physicsBody?.angularDamping
spriteNode.physicsBody?.area
// COPY
spriteCopy.name
spriteCopy.physicsBody?.linearDamping
spriteCopy.physicsBody?.angularDamping
spriteCopy.physicsBody?.area