Calling method when a node is removed from an SKScene - ios

Is there a way to hook into the SKNode lifecycle in Sprite Kit? Specifically I would like to perform some code when the node gets removed from the scene.
The use case I would like to solve in a bit more detail :
I have some nodes that interact with each other, and I would like them to be notified of certain events that happen to the other nodes. For example, imagine a game where you can tap a node on the scene, and the node's details would appear on a HUD. I would like the HUD to disappear when the node gets removed from the scene.
I plan to use NSNotificationCenter as the notification engine.
Whenever a node gets removed from the scene I would like to post a notification. The easiest way would be to tie into a lifecycle method on SKNode (my nodes are subclasses of SKSpriteNode) like nodeWasRemovedFromParent, but I didn't find any such method.
How can this be done?
I put some thought into coding my own solution by overriding the removeFromParent method in my SKSpriteNode subclass, and posting a notification before calling the super implementation. I am not sure that the removeFromParent method will always be called though. For example, does it get called when I change scenes?
Thanks.

You need to subclass each node class. Override the removeFromParent method as you said. Use only the subclassed versions, otherwise your code won't take effect.
In addition you will want to override removeAllChildren and removeChildrenInArray: or just never use them.
The removeFromParent method will not be called when the scene changes. Instead, override the scene's willMoveFromView: method and send a message to registered observers or simply all child nodes recursively. Use the scene's enumeration function to do so. Note that I'm not 100% sure whether on willMoveFromView the scene's children are still attached, I assume they will.
Unfortunately it's impossible to just subclass SKNode and then expect the subclass' code to work for all other node classes, because those subclass from SKNode directly and not your custom SKNode subclass. Hence you need to subclass and add this code to every SK*Node subclass as well if you need it to be notified on removal.
See KoboldKit node classes for an example, which uses a macro to inject this "override" code into SK*Node subclasses to avoid duplicating the code. The actual functionality is in KKNodeShared: https://github.com/KoboldKit/KoboldKit/tree/master/KoboldKit/KoboldKitFree/Framework/Nodes/Framework

Related

init() vs didMove vs sceneDidLoad in SpriteKit

I understand that there are 3 ways to create a scene in SpriteKit, init(), didMove, and sceneDidLoad.
But I can't understand to separate the 3 ways. Reading other questions I understood the order to call is init -> SceneDidLoad -> didMove.
How can I use them to use effectively?
You're right about the order those functions are called in. But only init(size:) actually creates a scene.
init(size:) initializes a new scene object with the given CGSize as its bounds. Anything that must be set up prior to the scene becoming visible should happen here. That's the important bit because a newly initialized scene isn't visible to the user until it's presented by a view.
sceneDidLoad() is called as a result of init(size:) and can be used to do any more setup required before the scene is presented. init(size:) can be called from wherever you want to make a new scene, but sceneDidLoad() happens in the scene itself. This is useful for any setup that you want all scenes of this class to use.
didMove(to:) is different because it doesn't have to do with the initialization. This function is called when the scene is presented by a view. Basically, when it becomes visible to the user. UI adjustments and layout for elements inside the scene are typically handled here.

Calling a function upon contact with barrier, but not colliding. Is it possible?

I would like to know if it is possible to call a function when a UIView comes into contact with a collision boundary, but not to have them actually collide.
Can a function be called when they come into contact and have the view go right through the boundary?
If so, how?
Yes, UIKit framework provides a nice collision detection facility.
To respond to UIKit dynamic item collisions (e.g., views) configure a custom class to adopt the UICollisionBehaviorDelegate protocol. Then, in a collision behavior (an instance of the UICollisionBehavior class), set the delegate to be an instance of your custom class.
In the aforementioned UICollisionBehavior add your view to it and configure your desired boundary using one of the addBoundary methods. Your delegate will then be notified of a collision in the collisionBehavior(_:beganContactFor:withBoundaryIdentifier:at:) protocol function.
(The default collisionMode is everything, so you can safely ignore this property for now.)

SKNode touching other SKNode recognition

I'm creating a new game project, and this requires some basic interactions between sprites. I was wondering how to implement the SKPhysicsContact class to detect when two SKSpriteNodes touch. Here is the SKPhysicsContact class reference:
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKPhysicsContact/#//apple_ref/occ/instp/SKPhysicsContact/contactNormal
I'm not sure how to use the bodyA and bodyB methods to detect touches
Whenever I try to use it, I can't seem to figure out how to actually use the class. Any ideas on how to use the class to detect touches between two bodies?
Once your scene is a contact delegate, you will get the calls didBeginContact, and didEndContact.
These methods uses both Body A and Body B to tell you whats actually colliding.
From there you decide what you want to happen when they collide, for example make BodyA = nil.

Is it safe to override the SKNode's removeChild undocumented/private method?

SKNode has a removeChild undocumented method which is called on parent object when deleting its child by removeFromParent method. Is it safe to override removeChild?
Probably yes, but no.
The problem is you don't know whether this method will be called in all instances. For example I noticed some SK*Node classes route some messages directly to the underlying C++ objects rather than passing the message on to the super implementation in SKNode.
Furthermore I wager that overriding a private method constitutes a case of private API use that might get your app barred from publishing on the App Store.
Technically though just try it and see if it works. It probably will. But for all other reasons you certainly shouldn't do it.
Instead override removeFromParent and access self.parent in your override in case you need to do something with the parent. Note that you will have to do this in every SK*Node subclass. This is because you can't override a method in a category, and you can't subclass SKNode and expect the subclass methods to be called from other SKNode direct subclasses such as SKSpriteNode (because they are parallel to your subclass, not a subclass of your SKNode subclass).

how to pause DynamicAnimator

i keep seeing these methods,
– dynamicAnimatorDidPause: required method
– dynamicAnimatorWillResume: required method
But i've not found a way to call them. Ive set up an animator and called <UIDynamicAnimatorDelegate> in the .h, but I cannot seem to call pause on the self.animator for some reason.
Anyone have any tips for me?
Thanks!
Those are delegate methods, which means that you don't call them, they get called for you. You implement a delegate to get informed about certain events
The dynamic animator pauses by itself when all movement stops. Try adding a gravity effect with no collision boundaries. The animator should pause when all dynamic items fall offscreen.

Resources