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.
Related
In SpriteKit development, how is one supposed to get access to the SKView?
For example; lets say I create a custom SKNode class called Player for use throughout my game:
class Player: SKNode {
/*...*/
}
In this class, if I wanted to generate a SKSpriteNode from an SKShapeNode (in order to improve performance) I need to use the SKView.texture() method. But I don't have access to the SKView that my Player is residing in unless I pass the view to the Player from whoever is instantiating it.
Is there any way to get the current view that an SKNode is attached, so that I'm not having to pass references to the view around my application? Is this just an anomaly of the framework? What is the preferred way of dealing with this problem?
I'm using the Swift language.
Update
Ron Myschuk's answer is correct and I've accepted it as it is probably most useful to the community. However my use-case for wanting to access the view from within a node was in order to use the texture method to create an SKSpriteNode from an SKShapeNode.
Here's a description of the method I wanted to gain access to:
Renders a portion of a node’s contents and returns the rendered image as a SpriteKit texture.
https://developer.apple.com/documentation/spritekit/skview/1519994-texture
This method seems useful outside of the view. For example, when initialising/populating SKNode's that have not yet been added to the display tree.
My solution to this specific problem was simply to create an SKView on the fly and use it for the purposes of my needs, allowing it to be garbage collected by the engine.
let view = SKView()
let texture = view.texture(from: someSkShapeNode)
let sprite = SKSprite(texture: texture)
// we don't need view anymore
Hope this helps someone.
providing that you've added an instance of player to your scene and providing that you are not doing his in the initializer of Player you could use the .scene property
self.scene?.view
You can build a SKTexture from UIImageor CGImage and you can create images from every complex path/drawing you want (see SKTexture from path for example or Drawing and Creating images).
So I have objects that in turn have sprites associated to them. a snippet of my Object class:
import SpriteKit
class Block {
var sprite : ColourSprite
}
So as you can see, it has a variable that is in fact a SKSprite (ColourSprite is my custom class that inherits from SKSpriteNode).
Now, at some points during the game, these sprites are deleted (i.e sprite.removeFromParent()), but the objects are obviously still somewhere.
I want to be able to send the objects to garbage collection once their sprites are gone. I am thinking that I can do something like sprite.getOwner() but I can't seem to find it. Is this possible?
The only other option I can think of is to manually check all objects and check each one's sprite but I feel that is long and wasteful.
You can check whether the Blocks are still in memory by using Xcode 8.3's new debug panel.
Just after you remove your sprites pause the program and go to that panel. See if there is any Block instances in the left panel. If there is, click on it to check what is retaining it.
If for example your GameScene is retaining the Block, you go to your GameScene and find the property. Then you can just set that to nil after you remove your sprite.
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.)
I'd like to switch things up a bit with my enemies in my SpriteKit game. They're just so... predictable. I've been inspired by the sorts of enemies that are encountered in The Binding of Isaac: Rebirth.
For example, let's say I'd like to implement enemies which split into two smaller enemies when enemy.health < 50.0f. In a simple scenario, this could be done from my main scene update loop by iterating through all my enemies. Something like:
if(enemy.health < 50 && enemy.type == kEnemyTypeSplitsIntoTwo) {
CGPoint position1 = CGPointMake(enemy.position.x+20,enemy.position.y);
CGPoint position2 = CGPointMake(enemy.position.x-20,enemy.position.y);
[enemy killEnemy];
[self spawnNewEnemyWithType:enemyType andHealth:50 atPosition:position1];
[self spawnNewEnemyWithType:enemyType andHealth:50 atPosition:position2];
}
This will probably work for simple cases. But this is something that I definitely want to keep out of my main scene update loop. The problem is that my enemy class is effectively a subclass of SKSpriteNode.
For situations like this, ideally I'd like a new class such as a ComplexEnemy or EnemyGroup class to handle this so I can implement more complex enemy behaviours and patterns (think epic boss fights with multiple phases). I'd imagine this new class would likely derive from SKNode (or even NSObject?) and generate multiple Enemy instances at certain points.
Am I thinking down the right path? I'd be interested to hear what means people have used to achieve this sort of behaviour, preferably with a couple of examples.
-DC
tl;dr - Groups of enemies, bosses with multiple phases. How do you guys do it?
There's nothing wrong with using SKSpriteNode as an enemy class. I do it all the time. If you want to add a health bar, for example, you can simply add it as child.
Your question is primarily opinion based so expect it to be closed soon enough. However, it is a good question nonetheless. I suggest you use one or more enemy SKSpriteNode classes to handle your workload as it can make things easier for a beginner. You can of course get away with just using one enemy class and have its update method handle various aspects based on properties. For example:
-(void)update:(NSTimeInterval)currentTime {
if(self.enemyType == kDragon) {
// do big stuff
}
if(self.enemyType == kDwarf) {
// do small stuff
}
}
I am currently working on a project which has 10 different kinds of enemies and 4 behavior options for each one. It is all being handled from one class (SKSpriteNode).
It all boils down to what you want and how you arrange your code to do the job. I suggest you think about exactly what it is you are trying to do and if you get stuck, ask for help on a more specific issue.
In your situation, I would subclass the ComplexEnemy as a simple object. This object would be responsible for the instantiation and the adding and removing to the scene of the various enemies that could appear.
Then in your scene update method, I'd call a checking method in that object. The checking method would do the rest of the heavy lifting. You can put that call (the one in the update method) in an array of methods and when the ComplexEnemy gets instantiated, it would register itself in that array so it can be checked. It would remove itself from the array before being removed from memory..
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