Extend SKAction to override timingMode - ios

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)
}
}
}

Related

Elegant way to pass a "partial closure"?

In my ViewController, I am setting up multiple so-called PanelControls.
These PanelControls are initialized with a title, UISlider and information, what property of another UIView called ViewToEdit to change with that slider (ControlPanel has a reference to it).
It is always one single property of type CGFloat or UIColor.
I want to be able to pass a property (not a value) of ViewToEdit when initializing a PanelControl.
So I wanna use it like this:
PanelControl(title: "doesnt matter", propertyToEdit: ViewToEdit.propertyToEdit)
And PanelControl would implement it like this:
class PanelControl: UIView {
...
func sliderChanged(slider: UISlider) {
propertyToEdit = slider.value
}
}
please not that the above code is just my fantasy and doesn't actually work. It just illustrates my desired usage.
This way I could create many instances of PanelControl and pass each one different information on which property of ViewToEdit they control.
I have tried:
Using a closure, but that does not fit because it is not a complete statement I want to pass. Rather a part of a statement. So viewToEdit.propertyToEdit = ... with the right side of that statement set by PanelControl when executing it.
Literally passing it ViewToEdit.propertyToEdit but that obviously makes no sense as well.
What to I do?
How about using key paths:
class PanelControl: UIView {
let changer: (Float) -> Void
init<T: AnyObject>(title: String, object: T, property: ReferenceWritableKeyPath<T, Float>) {
changer = { object[keyPath: property] = $0 }
}
func sliderChanged(slider: UISlider) {
changer(slider.value)
}
}
You can use it like this:
PanelControl(title: "doesnt matter", object: myViewModel, property: \ViewToEdit.propertyToEdit)

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)});
}
}

Accessing sprite nodes added in loop to call class inherited method?

I have created a number of SKSpriteNodes, adding them to the scene in a for loop.
func addBlocks(number : Int) {
for _ in 1...number {
let block = Obstacle()
addChild(block)
}
}
Since they're created in this loop, I can't call block.myMethod(). I can do of course callchildNodeWithName("block") to perform things it inherits by being a node, like .removeFromParent().
What should I do if I want to call the method implemented in the Obstacle class (subclass of SKSpriteNode).
Obstacle subclass of SKSpriteNode
Let's say you have Obstacle defined as follow.
class Obstacle: SKSpriteNode {
func explode() { }
}
Obstacles with names
If you add an Obstacle to the scene you can add a name to the node like
func addBlocks(number : Int) {
for i in 1...number {
let block = Obstacle()
block.name = "block_\(i)" // <-- like here
addChild(block)
}
}
Retrieving an SKNode by name and casting it to Obstacle
Next when you invoke self.childNodeWithName(...) you get something like this SKNode?.
It means you can receive nil or something that is an SKNode. If you believed (like in this case) that the returned object is something more specific of an SKNode (like Obstacle) the you can try to cast it to your custom class type.
func explodeObstacle(i: Int) {
guard let obstacle = self.childNodeWithName("block_\(i)") as? Obstacle else {
debugPrint("Could not find an Obstacle with name block_\(i)")
return
}
obstacle.explode()
}
In the code below, if the cast does fail (which mean the returned value is not an Obstacle, then an error message is printed on the console).
Otherwise if the value is successfully casted do Obstacle, you can invoke any method you added to Obstacle.

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

Alternative method for performSelector in SKAction in Swift

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.

Resources