Why doesn't SKPhysicsBody -bodyWithEdgeLoopFromRect react to collisions? - sprite-kit

I have a stack of blocks (think Angry Birds) and when a projectile hits them they fall over like you'd expect. However, this only works if I create the physicsBody like so:
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
If, however, I create the physics body like this:
CGRect r = CGRectMake(-self.size.width/2, -self.size.height/2, self.size.width, self.size.height);
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:r];
Then the collision is there, but the blocks are not affected by it. The projectiles bounce off of it, but do not cause the blocks to move at all. My categoryBitMask and collisionBitMask are correct - they work with -bodyWithRectangleOfSize, but -bodyWithEdgeLoopFromRect does not.
Is this a SpriteKit bug, or am I missing something?
Thanks,
-Brian

Have a look at the SKPhysicsBody Class Reference.
You will find that the various class methods for creating physicsBodies are listed under two categories, namely Volume-based and Edge-based physics bodies.
bodyWithRectangleOfSize: returns a volume-based physicsBody whereas bodyWithEdgeLoopFromRect returns an edge-based physicsBody.
In the overview of the same document, the difference between the two is explained as:
Sprite Kit supports two kinds of physics bodies, volume-based bodies
and edge-based bodies. When you create a physics body, its kind, size,
and shape are determined by the constructor method you call. An
edge-based body does not have mass or volume, and is unaffected by
forces or impulses in the system. Edge-based bodies are used to
represent volume-less boundaries or hollow spaces in your physics
simulation. In contrast, volume-based bodies are used to represent
objects with mass and volume.

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

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.

Instantaneous hit detection with SpriteKit

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.

Is there a way to set offset between SKSpriteNode and it's physicsBody?

I am creating a game where user controls a cube (50x50px) and can collect more cubes while exploring and collected cubes added to a random side of the users current cubes.
User starting cube
http://s7.postimg.org/id95wms7r/cube.png
After user collected some more cubes
http://s13.postimg.org/oo9v4c9bb/morecubes.png
As you can already see from the second image, my problem is when I generate the body from texture, it has some offset with the original picture. Cube adding is random so you can get really weird shapes and those offsets are always different. My question is, is there a way to align that body with the parent sprite? Also I don't want to move the sprite because sprite is in the right place, I actually want to move the body.
This is how my code looks like. Both player and cubes are at CGPointZero in their own coordinate systems.
Player: SKNode
-- Cubes: SKSpriteNode
physicsBody = SKPhysicsBody(texture: cubes.texture, size: accumulatedSize)
physicsBody?.dynamic = true
physicsBody?.mass = 2.0
physicsBody?.allowsRotation = false
Instead of creating the SKPhysicsBody from the texture, create an SKPhysicsBody for each individual cube, then put all those individual bodies into an array, and then create the SKPhysicsBody from the bodies in that array using:
(SKPhysicsBody *)bodyWithBodies:(NSArray *)bodies
That will get you a more accurate body with the sprite.
"The shapes of the physics bodies passed into this method are used to create a new physics body whose covered area is the union of the areas of its children" (from the SKPhysicsBody Class Reference).

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.