Change texture of SKSpriteNode in SKScene - ios

I've been trying to change the texture of an SKSpriteNode in the scene, but XCode keeps giving me the error "Value of type 'SKNode' has no member 'texture'". However, I put an if is statement, so it should read it as an SKSpriteNode. I'm wondering if there are any other solutions in order to be able to change the texture of an SKSpriteNode in the scene. Thank you!
for child in children {
if child.name == "Enemy" || child.name == "FinalEnemy" {
if child is SKSpriteNode {
child.texture = SKTexture.init(image: #imageLiteral(resourceName: "Ninja58"))
}
}
}

You are asking the runtime environment “is this SKNode actually a SKSpriteNode” but the compiler is still treating child as an SKNode.
You need to get a reference to a SKSpriteNode to make the compiler happy.
You can do that by changing
if child is SKSpriteNode
To
if let child = child as? SKSpriteNode
This second one uses optional binding to create a new variable named child which is valid inside the if block and is a SKSpriteNode.
If child is not a SKSpriteNode then this will pass over this if statement and the code won’t be run so you will have the behaviour you want.
The rest of your code should work by making this change.
Edit after Knight0fDragon's comment
You could actually embed the condition into the for loop here to make it a bit more compact...
for child in children where child.name == "Enemy" || child.name == "FinalEnemy" {
guard let child = child as? SKSpriteNode else { continue }
child.texture = ...
}
Hmm... this isn't as nice as I wanted it and I would like the optional binding in the for loop condition...
Maybe that's possible but this will do for now :)
Edit By Knight0fDragon due to comments being limited:
Please give this a try, should only work in XCode 9+, and the cast may be unnecessary, I do not have access to XCode to test this.
for child in ArraySlice<SKSpriteNode>(children) as [SKSpriteNode]
where ["Enemy","FinalEnemy"].contains(child.name ?? "") {
child.texture = ...
}

Related

Impossible contact in SceneKit

The physicsWorld(_:didBegin:) function should only be called when some node's category bit is set in another nodes contactTest bit mask. However, in my code, there is an impossible situation happening where a contact is occurring when neither of the contacting bodies have each other in their contactTest bit masks. Eg:
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
if (contact.nodeA.physicsBody!.categoryBitMask & contact.nodeB.physicsBody!.contactTestBitMask) != 0 {
print("All good")
} else if (contact.nodeA.physicsBody!.contactTestBitMask & contact.nodeB.physicsBody!.categoryBitMask) != 0 {
print("All good")
} else {
fatalError("Impossible") // << Execution reaches here
}
// .. Code handling the contact ...
}
I noticed this started happening when I added this line to the physicsWorld(_:didBegin:) function:
// The ball is the only node in the game that has a contactTestBitMask other than 0
let ball = contact.nodeA.name == "ball" ? contact.nodeA : contact.nodeB
ball.physicsBody!.contactTestBitMask = PhysicsCategory.dynamic
Regardless of what code I add to the function, that fatalError should never be reached. I don't know if changing bit masks within the didBeginContact method leads to undefined behaviour, but I still don't think the physicsWorld(_:didBegin:) should ever get called if categoryBitMask & contactTestBitMask == 0.
If I change (changing in both nodeA and nodeB) nodeA/B.physicsBody!.categoryBitMask to nodeA/B.categoryBitMask then it never crashes, but according to the documentation, a SCNNodes category bit mask property doesn't affect the SceneKit physics simulation.
If anyone has any suspicious of why this could happen please let me know as I'm convinced it's a bug.
Thanks

How do I get at, and then remove the parent of an SKNode?

A SKSpriteNode has a parent, that's been randomly assigned, and isn't aware of who it is.
How does that SKSpriteNode find its parent, and then remove the parent from the scene, thereby removing itself and the parent?
If you read the documentation thoroughly, you would find a method called removeFromParent in the SKNode class. Just call it:
yourSpriteNode.removeFromParent()
Alternatively, if you want to remove the node from its parent after a sequence of SKAction, you can use SKAction.removeFromParent().
let removeFromParentAction = SKAction.removeFromParent()
let sequence = SKAction.sequence([yourOtherActions, removeFromParentAction])
yourSpriteNode.run(sequence)
EDIT:
After reading your comments, I can guess that you probably did something like this:
// "cherry pick" an action
let action = AllMyActions.action1
yourSpriteNode.run(action);
Well, the way to solve this is to create an action like this:
let removeParent = SKAction.run {
[unowned self] in self.yourSpriteNode.parent?.removeFromParent() }
Put the above code just after the line where you cherry picked the action, and
yourSpriteNode.run(SKAction.sequence([action, removeParent]))
suppose A is a SKSpriteNode , the you can check if it has a parent and if it has, you can remove it from view by removing from parent.
if let parentOfA = A.parent{
parentOfA.removeFromParent()
}

Why do I keep getting this error when I try to sequence my actions in Swift?

This is the error I get. I have a sequence that Im using so I could call the actions in order. I put the function addGameBall() in a runBlock so I could have that action finish last. Is that the correct way to do that. Here is the code I have: What am I doing wrong? Thanks!
Attemped to add a SKNode which already has a parent
//RightSide
if firstBody.categoryBitMask == GameBallCategory && fourthBody.categoryBitMask == WallCategpory {
println("GoalRight")
let waitBall = SKAction.waitForDuration(1.0)
let removeFromParent = SKAction.removeFromParent()
let respawnBall = SKAction.runBlock(self.addGameBall)
let sequenceThis = SKAction.sequence([waitBall, removeFromParent, respawnBall])
runAction(sequenceThis)
}
"Attempting to add a sknode which already has a parent" means you're adding a node twice. I can't see your addGameBall code but i'm pretty confident you have a line in their that says. self.addChild(ball)//or whatever your code is called.Everytime this functions runs, the line is executed so the same reference to the ball node is added multiple times which is why the compiler is complaining. The problem could be solved by declaring ball as a local variable so that when the function runs, a new reference to the node is being created. Happy coding.

How to create a pointer/pass parameter to function in Swift

I am struggling to find the answer for this simply because I am unsure what to search for.
In objective-C I would do something like this to get a pointer to an object of a specific class:
CustomLabelClass *detailLabel = (CustomLabelClass *)[sortCell.contentView viewWithTag:kSortDetailLabelTag];
I would like to do the same in Swift. Basically I detect collision in Sprite Kit Scene and then try and pass the Node (which I know is of a specific class to another function).
if ((contact.bodyA.node?.isKindOfClass(TapCircleIcon)) != nil) {
updateOnScreenStatusFor(...)
I need to pass the TapCircleIcon into the function replacing '...'. So in Obj-c I would do something like:
TapCircleIcon *tapCircle = (TapCircleIcon *)[contact.bodyA.node];
You no longer need isKindOfClass in Swift. I am assuming that node is an AnyObject? optional. You can cast it to TapCircleIcon and unwrap the optional using this if statement.
if let tapCircleIcon = contact.bodyA.node as? TapCircleIcon {
updateOnScreenStatusFor(tapCircleIcon)
} else {
// node is not a TapCircleIcon
}

SKSpriteNode subclass: method to remove all joints

I have created a subclass of SKSpriteNode. I connect instances of that class together with joints of type SKPhysicsJointLimit. I do this within my didEndContact(contact: SKPhysicsContact) in my GameScene:
var joint = SKPhysicsJointLimit.jointWithBodyA(contact.bodyA, bodyB: contact.bodyB, anchorA: pos1!, anchorB: pos2!)
self.physicsWorld.addJoint(joint)
This works well so far.
Then i come to the point where i want to release the node from the joint. According to the SKPhysicsBody docs there is a property called "joints" which is an array holding SKPhysicsJoint objects. I thought thats exactly what I need, but I am not able to iterate over an instance's joints and remove them from the physicsWorld. To do the job i added a method to my custom SKSpriteNode subclass.
func freeJoints(world: SKPhysicsWorld){
if let joints = self.physicsBody?.joints {
for joint in joints{
println("found a joint: \(joint)")
// example print:
//found a joint: <PKPhysicsJointRope: 0x7fbe39e95c50>
world.removeJoint(joint as SKPhysicsJoint)
}
}
}
Calling the method fails after the println() statement with the message "Swift dynamic cast failed". I would really appreciate your opinion in how to work with an SKPhysicsBody's joint property. More specifically: How to use (cast?) the items in the array to be able to remove them from a scene's SKPhysicsWorld.
I spent a little more time in investigating this. This is what I have come up with:
I decided to add an property to my SKSpriteNode subclass and manage the joints myself
var joints: [SKPhysicsJointLimit]
override init(){
...
self.joints = []
...
}
Everytime I add an joint to the scene's SKPHysicsWorld I also add it to the joints array of the SKNNode itself. Whilst iterating the SKPHysicsBody's joints-Array failed (see question) at the point I wanted to cast it to SKPhysicsJoint, removing items from the physics world works as intended when iterating the array of SKPhysicsJointLimit items:
func freeJoints(world: SKPhysicsWorld){
for item in self.joints{
println("removing item from physics world \(item)")
world.removeJoint(item)
}
self.joints.removeAll(keepCapacity: false)
}
}
This seems not to be the most elegant way to do the job, since there already is a framework managed array that promises to be same thing. But I was unable to utilize it and this works for now.

Resources