I'm having problems detecting collisions in Cocos2D, because the coordinates I use to see if an object collided are always wrong.
I have the following objects:
All the sprites from a tiled map's layer, containing all the obstacles;
A sprite which is child of another sprite, which is child of the hello world layer (the main layer I'm using, which returns the scene). This sprites continuously moves in the map, and it may collide with the obstacles.
To detect collision I just see look at the distance between 2 sprite's bounding boxes:
inline BOOL collision(CGRect r1, CGRect r2)
{
CGPoint c1= RectCenter(r1);
CGPoint c2= RectCenter(r2);
BOOL result= (fabs(c1.x-c2.x)<MAX(r1.size.width/2.0,r2.size.width/2.0)) && (fabs(c1.y-c2.y)<MAX(r1.size.height/2.0,r2.size.height/2.0));
return result;
}
I use the boundingBox property to get the coordinate and the size of every sprite. But the coordinates are wrong, and it doesn't detect collisions correctly.
I'm pretty sure that I'm doing something wrong, could someone tell me how is the way to manage all sprites to have the same coordinate system? I also tried with convertToWordSpace, but with no luck.
You can do away with collision method in favor of the builtin function for seeing if two CGRects intersect:
CGRectIntersectsRect(r1, r2);
Related
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.
so I have a ball (sprite subclass) that can be dragged around the screen and updates it body to tell the world its position.
The problem is, when I apply a force to the ball while the ball is being touched (thus being controlled by a finger), I have two pieces of code fighting against each other to update the sprite's visual position.
The world stepper is trying to follow through the physics and update the ball's position. The touch methods however are also trying to update the ball's body and graphic position. Ideally, when touched I would like to kill off any physics affecting the ball.
So what exactly should I be trying to do to consolidate this? This is also creating issues for collisions and filtering. Any help appreciated. Thanks
Do you want the ball to have an accurate physics simulation? If not, you could always use the SetTransform method of the b2body class like this.
CGPoint ballLocation = ;//use the touched location for this
b2Vec2 vecBallLocation = b2Vec2(ballLocation.x / 32, ballLocation.y / 32);//calculate this on your own
b2Body * ballBody = ;//replace this variable with your ball's b2Body;
ballBody->SetTransform(vecBallLocation, ballBody->GetAngle());
Do not set the ball sprite's position manually. Leave this up to the physics simulation. Your code will get really messy over time if you have to keep track of what sprites you set the position of manually and which ones use the physics simulation to update their positions. This way is also much easier.
When the physics object is being dragged, set its velocity and angular velocity to 0,0 before running the physics step simulation. You may also want to temporarily get the physics object's position, then step, then set the body's position back, then apply whatever touch dragging offset you got from moving the finger to the body.
so I have a sprite that is created every second and that is moving at a random position (sprite1) and another sprite that has a fixed position (sprite2). I would like that when sprite1 collide with sprite2, sprite1 is like sticked to it (it stops moving and is sticked to it) . How can I do this please ? sorry for my english I'm french :/
p.s : sprite2 is rotating with accelerometer, so if sprite1 collide with it I would like that it rotate too :)
I think, you can try to use box2d to do this. It will help to detect collisions and to manage rotations, movement, etc.
I think, you can do it simply in Cocos2d.
1) First set the rect for sprite1 and sprite2 using CGRectMake(x,y,width,height)
2) As you told sprite1 is moving at random position and sprite2 is fixed to particular position, you can check them collide by using CGRectIntersectsRect([sprite1 bounds],[sprite2 bounds]).
3) if it intersects, set sprite1.position = sprite2.position
Note: you said sprite1 is rotating, rect can be fit only to the regular bodies. if you want exact collision or physical properties for sprite better you can go for box2d.
If you don't want to use Box2d (which can handle circle collisions), you can try something like this:
1.) Detect collision, is the distance between the two circles center point (x,y), less than the sum of the two circles radius.
2.) Make the Sprite1 stick to Sprite2, Stop the movement of Sprite1, and save the relative delta (x,y) to Sprite2, then whenever Sprite2 moves or rotates apply the same delta movement and rotation to Sprite1.
I need to animate an object along a path. I am doing so by creating an animation like:.
This works great but since my terrain is not flat it will be nice if I don't have to deal with the y component. In other words I want to move an object along the x-axis and z-axis and if there is a small slope increase then increase the object y position. same thing if there is a downward slope. Or maybe I have to create a script where it checks to see if the object is colliding with the terrain. if not then decrease its y position. I don't know if this will work meanwhile animating an object though.
I think the easiest way I can think of to solve this is for you to make the object a rigid body and use collision (probably a mesh or capsule collider if its a player character) to get it to sit on the floor.
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 :)