SKshapenode is not responding to Physicsbody - swift

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.

Related

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.

SpriteKit tilemaps collision with curved or sloped floor tiles

I am planning a platforming game for iOS using SpriteKit and Swift. I did some research on how to handle collisions of the player sprite and stumbled upon this article.
http://www.learn-cocos2d.com/2013/08/physics-engine-platformer-terrible-idea/
That article advices you not to use the build-in physics engine of SpriteKit but to implement things like moving, jumping and collision handling on your own. The platform tutorial on Ray Wenderlichs site suggests a similar approach.
So far so good but let's talk about floor tiles on which the player can stand upon. A custom physics implementation would be easy as long as the tiles are rectangular and have a flat surface (like in the tutorial from Ray Wenderlich), since you would use CGRectIntersectsRect to detect a collision. But how would you check the player collision on curved or sloped tiles? From what I have read, CGRectIntersectsRect does not account for tranparent pixel inside a sprites rect.
http://i.stack.imgur.com/lRP2Q.png
Look at the above tile for example. The white area (upper left) would be transparent. Now, if the player sprite would drop on this tile, the collision would be detected at the upper border of the tiles rectangle, although there are no ground pixels there (blue area, lower right). So ultimately the player would hover in mid-air above this tile. I could push the player sprite down a few pixels but that is a bit hacky and gets harder if the curved floor tiles have different slope angles.
So the question is, how can I handle these types of collision with SpriteKit alone (no extra frameworks like Cocos2D, Kobold Kit, ...)? Or is this approach entirely wrong and collisions in platformer with SpriteKit should be done fundamentally different?
Any help is very much appreciated!
I disagree with not using physics to handle collisions and contacts. You are really just trying to reinvent the wheel here by not using physics and implementing your own custom code.
If you are using the Tiled app then assigning a physics body is a simple task. Use the Objects in Tiled to assign various body types. In your code you can then go about creating a specific body for each object type.
For example:
In the above image I have created a 45 degree right side sloped floor. The object I added is called floor45R.
The next step is to parse your map objects. In case of the 45floorR, you create a physics body like this:
NSArray *arrayObjects = [group objectsNamed:#"floor45R"];
for (NSDictionary *dicObj in arrayObjects) {
CGFloat x = [dicObj[#"x"] floatValue];
CGFloat y = [dicObj[#"y"] floatValue];
CGFloat w = [dicObj[#"width"] floatValue];
CGFloat h = [dicObj[#"height"] floatValue];
SKSpriteNode *myNode = [SKSpriteNode spriteNodeWithColor:[SKColor clearColor] size:CGSizeMake(w, h)];
myNode.position = CGPointMake(x, y);
myNode.zPosition = 100;
CGMutablePathRef trianglePath = CGPathCreateMutable();
CGPathMoveToPoint(trianglePath, nil, -myNode.size.width/2, myNode.size.height/2);
CGPathAddLineToPoint(trianglePath, nil, myNode.size.width/2, -myNode.size.height/2);
CGPathAddLineToPoint(trianglePath, nil, -myNode.size.width/2, -myNode.size.height/2);
CGPathAddLineToPoint(trianglePath, nil, -myNode.size.width/2, myNode.size.height/2);
myNode.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:trianglePath];
myNode.physicsBody.dynamic = NO;
myNode.physicsBody.restitution = 0;
myNode.physicsBody.friction = 0.0;
myNode.physicsBody.categoryBitMask = CategoryFloor45;
myNode.physicsBody.collisionBitMask = 0x00000000;
CGPathRelease(trianglePath);
[worldNode addChild:myNode];
}
This works for any degree floor slope. Remember to set your player's and any other node's collision bit mask to collide with the floor.
In order for your player move smoothly over sloped floor, I recommend building the player's physics body in 2 pieces. A circle at the bottom and a rectangle at the top. The circle will prevent getting stuck in any potential cracks caused by 2 joining physics bodies.
SKPhysicsBody *firstBody = [SKPhysicsBody bodyWithCircleOfRadius:10 center:CGPointMake(0, 0)];
SKPhysicsBody *secondBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(5, 50) center:CGPointMake(0, 10)];
self.physicsBody = [SKPhysicsBody bodyWithBodies:#[firstBody, secondBody]];
You will have to adjust the position and center coordinates to match your sprite's image.
You can make use of a level editor, Tiled to handle curved and sloped floor tiles along with JSTileMap which processes the TMX map.
It will be able to able to handle contact with curved and sloped floor tiles but as for adjusting the angle of objects standing on top of those tiles, you would have to utilize some math functions, a good example is found here
You can still make use of SpriteKit's collision detection which will simplify things for you but create your own jumping or movement engine.

SpriteKit Physics Bodies Collisions in Multiple Layers

Okay, so I'm writing code for a space blaster game, and all seems to be going well, minus these crucial issues.
First, I added a spaceship (with PhysicsBody) as a child of self (GameScene).
Then I added a background layer as a node also as a child of self, behind the spaceship, and simulated movement by moving the background around behind the spaceship via joystick control.
I added a node with a boundary physics Edge Loop body so the background wouldn't move beyond the ship, and added that node as a child of the bgLayer.
I have objects for the spaceship to shoot at, but I want them to move around as the background does, so I added them as children of the bgLayer.
Of course, I needed a laser when the spaceship fires, so I added this as well as a child of the bgLayer.
_spaceship.physicsBody = (custom physicsBody code);
[self addChild:_spaceship];
_bgLayer = [SKNode node];
[self addChild:_bgLayer];
_bounds = [SKNode node];
_bounds.physicsBody = (physicsBody edgeLoop code);
[_bgLayer addChild:_bounds];
_otherObjects.physicsBody = (custom physicsBody code);
[_bgLayer addChild:_otherObjects];
_laser.physicsBody = (custom physicsBody code);
[_bgLayer addChild:_laser];
All is well, the background doesn't move beyond the spaceship,the other objects move as the background moves, and the laser fires when called upon.
My first big dilemma is when my laser fires, it does not move as the background moves, but the background moves behind it. I could probably make do but it looks funny if the laser always moves in parallel with the ship. This seems odd to me since I added the laser as a child of _bgLayer.
Second, my laser's physicsBody doesn't seem to recognize the EdgeLoop body, and sails right on through it. Also, my spaceship's physicsBody seems to recognize the EdgeLoop body, but it does not recognize the other objects that are children of _bgLayer.
Do Physics Bodies that are not children of the same layer recognize each other?
And why doesn't my laser behave similarly to other children of the same layer?
Moving the world by changing its position will affect
Children with physics bodies
Children without physics bodies
Moving the world by applying a force/impulse or by settings its velocity will affect
Children without physics bodies
Instead of moving the world by setting its velocity, you can add a camera (an SKNode with a physics body) to the world, move the camera by setting its velocity, and then update the position of the world to center the camera. You can center the camera in the didFinishUpdate method.
-(void)fireLaser
{
_laser = [SKSpriteNode spriteNodeWithImageNamed: [NSString stringWithFormat:#"blueLaser"]];;
_laser.zRotation = _spaceShip.zRotation;
CGVector rotationVector = RadiansToVector(_spaceShip.zRotation);
_laser.position = (Custom code for positioning the laser just in front of the spaceship);
_laser.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_laser.size];
[_bgLayer addChild:_laser];
_laser.physicsBody.velocity = CGVectorMake(rotationVector.dx * LASER_SPEED, rotationVector.dy * LASER_SPEED);
}
[self fireLaser] is called in touchesBegan when a particular SpriteNode is touched.
The laser fires beautifully, but does not scroll with the background, but rather moves in relation to the spaceship. Background scrolls with a PhysicsBody and a setVelocity method is called when the joystick moves, thus simulating spaceship motion, when in reality the spaceship never leaves the center of the screen. Physics categories prevent the background physics body from colliding with anything. LASER_SPEED is simply a static const float.

Why doesn't SKPhysicsBody -bodyWithEdgeLoopFromRect react to collisions?

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.

Static and moving shapes in spacemanager

Dear all,
I have an application that uses cocos2d spacemanager with gravity set to a specific value.
If i want to make a shape in the middle of the screen it will fall down to the floor, if i set the gravity to zero all other object will not move as supposed, if i use a second spacemanager and set its gravity to 0 i cant detect collision between objects from different spacemanagers. how can i add a shape that wont fall down in the middle of the screen and detect its collision while other objects behave correctly according to the gravity set.
Also a question is, should i use shapes (Circle, rectangle, ... etc) with spacemanager and if i want to use a ccsprite (image) i should put it in a shape or i can use the sprite alone (e.g. a tree is not a rectangle or circle collision and reflection wont be natural how can i do this).
regards
Every shape has a property called mass. If you want a shape to be static and respond to collisions just set mass to STATIC_MASS like this:
cpShape *ball = [smgr addCircleAt:cpv(440,70) mass:STATIC_MASS radius:10];
to ad an image, do that:
cpShape *ball = [smgr addCircleAt:cpv(440, 70) mass:STATIC_MASS radius:10];
[super initWithShape:playerShape file:#"ball.png"];
If this doesn't work, set up a cpCCSprite in it with a shape.
You can search for cpCCSprite on google, im sure you'l find something :)