Instantaneous hit detection with SpriteKit - sprite-kit

What is an effective method to perform instantaneous hit detection in SpriteKit? Using SpriteKit's built in physics engine I want to collect affected objects during a single frame for situations such as an explosion's blast radius.
I have considered adding an SKNode with SKPhysicsBody to the explosive object and accessing its allContactedBodies property when it explodes but this would add unnecessary collision detection to other frames.
I have also considered adding an SKNode with SKPhysicsBody to the scene or altering an existing one at the time of explosion and using an action to remove or restore the node after a short duration but this feels unnecessarily complex and may delay the hit detection until the next frame.
I have wondered if there is a means to extend SKPhysicsWorld to allow for enumerating bodies with a circle in addition to a rectangle - or to extend SKPhysicsBody to allow manual collision detection with another test body.
However, I do not see how to access an SKPhysicsBody's shape data to do either of these.

If I were to tackle this problem I would do it by creating a node with a physics body and then messing with its dynamic, categoryBitMask, and collisionBitMask properties. I would set dynamic to false since theoretically the collision/explosion doesn't move because it hits other objects. I would set its categoryBitMask so that every object generated a "didBeginContact" report when colliding with it. And I would set its collisionBitMask so that it never caused impulses because of collision. This way the physicsBody is more of a probe then an actual physics object, just being what is their.
At the end of that the physics body should cause collision reports but not repel any objects. Obviously you can create this body and just not have it added to the scene until it is actually time to detect things. Then after that frame is over and you know all the physics bodies it touched you can remove it again.
Then I would set the scene as a physicsContactDelegate and impliment the did begin contact method.
class GameScene: Scene, SKPhysicsContactDelegate {
func didBeginContact(contact: SKPhysicsContact)
{
var AName:NSString = NSString(string: contact.bodyA.node!.name!)
var BName:NSString = NSString(string: contact.bodyB.node!.name!)
}
}
Then I would have if statements checking if either AName or BName were the name of your "probing" node/physicBody. Then after that you can add code to do whatever you want as a result of that collision.
Good luck! If you are having troubles with the bit mask properties I would strongly suggest checking out this.

Related

SKLabelNode PhysicsBody Issue

The game Supertype is incredible.
I was thinking about making an app similar to it.
Is there any easy way to turn the text of a SKLabelNode into a Physics Body?
for example, I want to have the letter 'A' become a literal Physics Body
 - Is there any simplistic way to achieve this without having to actually define each cgPath?
- I guess I could make the 'A' a SKSpriteNode, but this seems poor.
Also,
I've had many problems turning SKSpriteNodes into Physics Bodies. I can easily turn a single image into a physicsBody, but if it's an image broken apart by invisible segments, things brake. Swift chooses a specific segment to become a physicsBody.
• I'm also so upset, since you can't define a concave shape and make a Physics Body. But you can have concave contiguous images become physics bodies nicely.
Last,,
It would be very nice to have a word turn into a Physics Body, and with a Boolean, define it to fall apart or stick together. Is there a way to do this?
(I'm thinking I can do it by clumping an array of them in the initialization: [SKPhysicsBody])
A SKLabelNode doesn't have a physics body, so you can not apply physics directly to it. What you could do is create them as a set of SKSpriteNodes and assign an image of each letter to the body texture.
To "turn the physics body on and off", you should toggle the node's property isDynamic. For other effects, you can play with affectedByGravity property and the collision and the contact bitMasks, depending on what you want.
To make the collision feel more real, you can use SKPhysicsContactDelegate, so you can automatically "draw" a physics body with the exact shape of the letters. Something like this:
letterNode.physicsBody = SKPhysicsBody(texture: letterTexture, alphaThreshold: 1.0, size: letterTexture.size())
To handle the collisions and contacts, don't forget to set the contactDelegate in your scene, like this:
self.physicsWorld.contactDelegate = self

SKEmitterNode explosion with force to another Object

I've an explosion in my SKScene and I want this Explosion even effect another objects close to its , Object are in all sides .I want to give them force to run away with this explosion .
It sounds like you need an SKFieldNode implementing a vortexField():
https://developer.apple.com/reference/spritekit/skfieldnode#1654415
SKFieldNode:
A node that applies physics effects to a portion of the scene.
vortextField()
Creates a field node that applies a perpendicular force to physics bodies.

Small, "bouncy" lags when pushing an SKShapeNode with another shape moved by an SKAction

I am creating a 2D autoscroller and when my player (with a circle physicsBody) collides with an SKAction moveTo-powered obstacle which is moving towards it, it makes small, almost unnoticable lags. I tried lowering the friction and the density of the objects, but nothing helped. Any ideas?
You have chosen to use a circle with phycsicsBody. An SKPhysicsBody object is used to add physics simulation to a node. When a scene processes a new frame, it performs physics calculations on physics bodies attached to nodes in the scene. These calculations include gravity, friction, and collisions with other bodies. After the scene completes these calculations, it updates the positions and orientations of the node objects.
You have also chosen to use SKActions, when using the actions to move a body such changes don't go through the physics engine, indeed usually you could add unwanted actions and unexpected events as "bouncy lags".
So, if you're using physics to move a body, you shouldn't use move actions, apply an impulse or force, or set it's velocity directly
Setting restitution to zero may help. This controls the elasticity, or bounciness of a sprite.

SKPhysicsContact when object collides with more than 1 object, handle first, ignore all others.. how?

Okay I have some physics objects, and they are all nicely categorized, and my didBeginContact is properly fired, and does what it is supposed to do. The problem is this:
I have two categories of objects, say ball and paddle.... When the ball touches a paddle the ball should explode... simple enough.. The problem lies in that the ball could touch 2 paddles at the same time... So, more than one didBeginContact gets called, and as such more than 1 explosion happens (1 per paddle the ball contacted with).. So the problem I am trying to figure out is, how do I remove/ignore all subsequent contacts with paddles from being handled if the code has already handled a collision involving the original ball? Removing the SKSpriteNode from parent before starting the explosion does not nullify the other contacts, they still get handled... so how do I tell it.. HEY PHYSICS CONTACTS STUFF... that body is no longer in the picture... so just throw those contacts away and don't worry about them?
I suppose I could explicitly check that the parent still has the SKSpriteNode available in the contact code before doing the explosion etc, but that seems kludgy at best, though I suppose it would work... Is there another/better way to handle this? I have to believe there is.
The moment you get the contact, set the contactBitMask of the ball node's physicsBody as 0. I am assuming the node needs to be destroyed and does not need to be reused.
This should prevent multiple contacts from appearing for the same node.
If the above doesn't work, you can try the following methods:
Removing the node from parent within the contact delegate, and triggering the explosion animation subsequently.
Subclass SKSpriteNode or SKPhysicsBody for the ball nodes and add a property for eg. alreadyTouched. Then you can check and set the property from within the contact delegate.
If you have three physicsBodies (ball, paddle1 and paddle2) and ball is set to collide with paddles (but paddles do not collide with each other) and the ball collides with 2 paddles during the same update cycle, then the game engine will generate 2 separate SKPhysicsContact objects - ball & paddle1 and ball & paddle2.
didBegincontact (Swift2) or didBegin(contact:) (Swift 3) (dBC for brevity) will then be called twice during the same update() cycle i.e. there will be 2 calls to dBC between calls to update()- firstly for the SKPhysicsContact between ball & paddle1 and then for the contact between ball & paddle2. You can do ball.removeFromParent() during the first contact, and the ball will be removed from the node tree but this will not prevent the second call to dBC for the ball & paddle2 collision and you will likely perform the same actions against ball (exploding, removing the score etc).
The simplest thing to do is to create a subclass of SKSPriteNode for your ball (ballNode?) and add a single property isActive, initialised to true. This is very easy.
In dBC, when the ball hits the paddle, check its isActive property and if false, simply return. If it's true, perform the normal collision actions and then set ball.isActive to false. When dBC is called the second time, the check against isActive will prevent any duplicate actions.
You could also use the ball's userData (part of SKSpriteNode) to store the isActive values to prevent subclassing, if you'd prefer that.
There are other ways to handle this issue, but so far I've found this one to work and to be the simplest.

SKshapenode is not responding to Physicsbody

I have created a SKShapeNode and I have assigned a physicsBody to it. However, it is not being triggered when there is contact.
Creation of SKShapeNode code:
-(SKShapeNode*)gravityline{
//SKSpriteNode *lolo=[[SKSpriteNode alloc]init];
SKShapeNode *lolo = [[SKShapeNode alloc] init];
CGPoint fff=CGPointMake(ray1.position.x, ray1.position.y);
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, fff.x, fff.y);
CGPathAddLineToPoint(path, 0,rayoriginpoint.x,rayoriginpoint.y );
CGPathCloseSubpath(path);
lolo.path = path;
lolo.name=#"gravityline";
lolo.strokeColor=[SKColor greenColor];
lolo.glowWidth=.1;
CGPathRelease(path);
lolo.physicsBody=[SKPhysicsBody bodyWithEdgeFromPoint:fff toPoint:rayoriginpoint];
//lolo.physicsBody=[SKPhysicsBody bodyWithEdgeLoopFromPath:path];
//lolo.physicsBody=[SKPhysicsBody bodyWithPolygonFromPath:path];
lolo.physicsBody.categoryBitMask=raylightCategory;
lolo.physicsBody.collisionBitMask=batCategory;
lolo.physicsBody.contactTestBitMask=batCategory;
lolo.physicsBody.usesPreciseCollisionDetection=YES;
lolo.physicsBody.linearDamping=0;
lolo.physicsBody.restitution=1.0;
lolo.physicsBody.dynamic=NO;
return lolo;
}
Here is the trigering code :
- (void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
if (firstBody.categoryBitMask == raylightCategory && secondBody.categoryBitMask==batCategory)
{
NSLog(#"Contact with bat have been made");
[secondBody.node removeFromParent];
}
}
If anybody has a clue what I did wrong, why the SKShapeNode is not activating the physicsBody, please let me know.
This certainly won't work:
lolo.physicsBody=[SKPhysicsBody bodyWithEdgeFromPoint:fff toPoint:rayoriginpoint];
If anything this will return a body already assigned to a different node. But I guess it simply returns nil.
This commented line will not work either:
//lolo.physicsBody=[SKPhysicsBody bodyWithEdgeLoopFromPath:path];
Edge shapes will create static (as in: immovable) bodies. Hence this node won't move through physics and if I'm not mistaken you also won't get contact response from contacts with static bodies, only dynamic bodies.
This one should work:
//lolo.physicsBody=[SKPhysicsBody bodyWithPolygonFromPath:path];
But then you're setting the body to a static body here:
lolo.physicsBody.dynamic=NO;
Hence the same rules apply as if you were creating a body with an edge loop.
Your code doesn't quite show what your "gravityLine" is colliding with. I can only speculate from its description that it's not detecting a collision with a round shaped object (a ball and a bat).
The "gravityLine" method seems to be returning a SKShapeNode that is defined by an edge-based shape:
lolo.physicsBody=[SKPhysicsBody bodyWithEdgeFromPoint:fff toPoint:rayoriginpoint];
When it comes to collisions, it is important to read Apple's Sprite Kit Programming Guide, specifically it's explanation of three types of shapes.
1) A dynamic volume simulates a physical object with volume and mass that can be affected by forces and collisions in the system. Use dynamic volumes to represent items in the scene that need to move around and collide with each other.
2) A static volume is similar to a dynamic volume, but its velocity is ignored and it is unaffected by forces or collisions. However, because it still has volume, other objects can bounce off it or interact with it. Use static volumes to represent items that take up space in the scene, but that should not be moved by the simulation. For example, you might use static volumes to represent the walls of a maze.
While it is useful to think of static and dynamic volumes as distinct entities, in practice these are two different modes you can apply to any volume-based physics body. This can be useful because you can selectively enable or disable effects for a body.
3) An edge is a static volume-less body. Edges are never moved by the simulation and their mass doesn’t matter. Edges are used to represent negative space within a scene (such as a hollow spot inside another entity) or an uncrossable, invisibly thin boundary. For example, edges are frequently used to represent the boundaries of your scene.
The main difference between a edge and a volume is that an edge permits movement inside its own boundaries, while a volume is considered a solid object. If edges are moved through other means, they only interact with volumes, not with other edges.
Based on the above info, if you read the documentation for the method that you used bodyWithEdgeFromPoint:toPoint:, you will see that you are creating an "Edge-based" physics body.
Return Value
A new edge-based physics body.
Discussion
An edge has no volume or mass and is always treated as if the dynamic property is equal to NO. Edges may only collide with volume-based physics bodies.
To make your collision work, you have to make sure that your edge is colliding with a volume-based physics body. Every physics body shape creation method documents what type if shape it's creating.
If you are using a volume-based physics body that is colliding with your edge, then another possibility may be due to the size or speed of the involved objects. Again, reading Apple's docs makes it clear.
Specify High Precision Collisions for Small or Fast-Moving Objects:
When Sprite Kit performs collision detection, it first determines the locations of all of the physics bodies in the scene. Then it determines whether collisions or contacts occurred. This computational method is fast, but can sometimes result in missed collisions. A small body might move so fast that it completely passes through another physics body without ever having a frame of animation where the two touch each other.
If you have physics bodies that must collide, you can hint to Sprite Kit to use a more precise collision model to check for interactions. This model is more expensive, so it should be used sparingly. When either body uses precise collisions, multiple movement positions are contacted and tested to ensure that all contacts are detected
ship.physicsBody.usesPreciseCollisionDetection = YES;
Other possibilities might mess up collisions as well, such as wrong position info for the path that you're assigning to the physics body. It's important to understand that when you set a path to a node, that the shape is being set using the local coordinate system of the node. It's important to remember that the origin in Sprite Kit is located bottom left corner (not UIKit's top left corner), and when you assign a path to a physics body of a node, that the path is placed relative to the anchor point of the node.
For example:
SKShapeNode *ball = [[SKShapeNode alloc] init];
CGRect ballFrame = CGRectMake(-25.0, -25.0, 50.0, 50.0);
[ball setPath:[UIBezierPath bezierPathWithOvalInRect:ballFrame].CGPath];
[ball setPosition:CGPointMake(100.0, 450.0)];
[ball setFillColor:[UIColor redColor]];
[ball setPhysicsBody:[SKPhysicsBody bodyWithCircleOfRadius:25.0]];
If I had set the origin of "ballFrame" at (0,0), then the circle of the physics body with radius of 25.0, would not coincide with the shape of the ball, as the bottom left corner of the physics body will be placed at the anchor point of the ball, which is at points x = 0 and y = 0, in the local coordinate system of the ball.