Alternative method for performSelector in SKAction in Swift - ios

I'm converting a Sprikit App to Swift. But I have a problem to convert this method:
SKAction *releaseBalls = [SKAction sequence:#[[SKAction performSelector:#selector(createMyNode) onTarget:self],[SKAction waitForDuration:1] ]];
Is there any alternative code in Swift ? Thanks

Try this out
class MyScene: SKScene {
func doAction() {
let releaseBalls = SKAction.sequence([
SKAction.runBlock(self.createMyNode),
SKAction.waitForDuration(1)
])
// run action
}
func createMyNode() {
// create the nodes
}
}

Although other solutions are generally preferred, future readers might like to know that performSelector and other members of the same family are available as of Swift 2.

Related

Extend SKAction to override timingMode

I have many SKActions in a SpriteKit project. The default timingMode for SKActions is "linear". Is it possible to use an extension to override this timingMode default to e.g. "easeInEaseOut" so ALL SKActions have timingMode = easeInEaseOut?
I have tried various "extension" styles but none will compile - normally returning "'timingMode' used within its own type" or "Initializer 'init()' with Objective-C selector 'init' conflicts with implicit initializer 'init()' with the same Objective-C selector"
The docs don't seem to give any examples of this, but surely this would be a useful thing to be able to do? Especially when you have hundreds of SKActions in your game?
Pick your poison, one extends the action to allow you to quickly call .easeInEaseOut timing mode, the other extends SKNode to allow you to run using a specific timing mode.
There is no way to change default behavior, the only other way is to create your own static methods for every action that exists, which can become cumbersome.
extension SKAction
{
//e.g. SKAction.move(to: CGPoint.zero, duration: 10).easeInEaseOut()
func easeInEaseOut() -> SKAction
{
self.timingMode = .easeInEaseOut
return self
}
}
extension SKNode
{
func runWithEaseInEaseOut(action:SKAction,withKey key: String = "")
{
action.timingMode = .easeInEaseOut
if key != ""
{
self.run(action,withKey:key)
}
else
{
self.run(action)
}
}
}

SpriteKit addChild(node: SKNode) plural?

Is there a plural version of this command, so I can type in a list of children to add?
Kind of like:
addChildren(myNodeABC, myNodeXYZ, myNode123)
Write an extension to do it: (I wrote it in Swift 3 style, I do not have XCode available right now to verify this works)
extension SKNode
{
func add(children: SKNode...) {
for child in children{
addChild(child)
}
}
}
usage:
node.add(children:node1,node2,node3)
note:
... is called variadic parameters in case you want to know more about them: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html
If you want to use less lines, do:
extension SKNode
{
func add(children: SKNode...) {
children.forEach({addChild($0)});
}
}

Swift 2.x: searching the node tree for multiple hits

I want to search my SKScene class for childNodes that begin with "spaceship". Basically I have several spaceship nodes named "spaceship1", "spaceship2", "spaceship3", etc...
However I'm not getting the syntax right. This:
self.subscript("spaceship[0-9]")
results to :
Expected ',' separator
And this:
self.objectForKeyedSubscript("spaceship[0-9]")
Results to :
'objectForKeyedSubscript' is unavailable: use subscripting
There's a better approach to this problem than assigning tags like "spaceship" + counter.
The Spaceship class
Yes, for a number of reasons you should create a Spaceship class, like this
class Spaceship: SKSpriteNode {
init() {
let texture = SKTexture(imageNamed: "spaceship")
super.init(texture: texture, color: .clearColor(), size: texture.size())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Retrieving all the spaceships
class GameScene:SKScene {
var spaceships: [Spaceship] {
return self.children.flatMap { $0 as? Spaceship }
}
}
Why a custom class is better than "numbered tags"
For several reasons
You don't get crazy assigning a new tag-with-counter value to each new sprite that should act like a Spaceship
You can add behaviour to your spaceship entity simply adding methods to the Spaceship class.
The compiler will block you if you erroneously use another node as a spaceship
You code is cleaner
This is a great handy reference for working with Strings in Swift.
http://useyourloaf.com/blog/swift-string-cheat-sheet/
As per that site
let spaceshipString = "spaceship1"
spaceshipString.hasPrefix("spaceship") // true
spaceshipString.hasSuffix("1") // true
With that in mind, you can just enumerate through all your nodes to find the ones with spaceship by doing the following.
func findSpaceShipNodes() {
self.enumerateChildNodesWithName("//*") {
node, stop in
if (( node.name?.hasSuffix("spaceship") ) != nil) {
// Code for these nodes in here //
}
}
}

How to make sequence in Swift?

I'm trying to make a simple sequence of two actions but Xcode is saying that I have an extra argument in call. I tried to translate Apples Obj-C example into Swift and it's not going very well. What am I doing wrong?
func dead() {
let animateAction = SKAction.animateWithTextures(self.catArray, timePerFrame: 0.09)
let ending = SKAction.runBlock(self.gameOver)
let sequence = SKAction.sequence(actions: animateAction, ending)
self.cat.runAction(sequence)
}
The sequence method of SKAction requires an Array of AnyObject. To fix this, you will need to call the method with the two actions you declared earlier in an array like this:
let sequence = SKAction.sequence([animateAction, ending])
self.cat.runAction(sequence)

Check if node has actions being processed?

I'm executing the SKAction rotateToAngle on one method. On another method I want to know in whether the action is still being executed or if it has ended.
I could save the time the action started in a property and check it however I was wondering whether there is an easier method.
private func rotate(motionManager: CMMotionManager, gravity: CGVector) {
let rotate = SKAction.rotateToAngle(CGFloat(M_PI * 5), duration: 5.0, shortestUnitArc: true)
self.runAction(rotate)
}
private func actionRunning() --> Bool {
}
I tried using self.hasActions() but it always return true. Any ideas on how to do this on Swift?
You could always use the withKey parameter when using runAction. Then do a actionForKey: method to determine if it still exists.
In Objective-C it looks like this:
[self runAction:[SKAction waitForDuration:0.1] withKey:#"waitTimer"];
[self actionForKey:#"waitTimer"];
I'm not familiar with Swift, but the Apple Docs do show it supported for the Swift Language as well.
https://developer.apple.com/library/prerelease/ios/documentation/SpriteKit/Reference/SKNode_Ref/index.html#//apple_ref/occ/instm/SKNode/runAction:withKey:
func runAction(_ action: SKAction!,
withKey key: String!)

Resources