My scene has a bunch of rectangles with physics bodies that are the same size as the rectangle. I like to anchor all of my objects to CGPointZero, however I've noticed when I do that the physicsbody remains anchored in the middle. In other words, the position of my physics body is like 100 pixels lower and to the left of the visual representation.
Here is a simple snippet of code:
SKSpriteNode* square = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:CGSizeMake(width, height)];
square.anchorPoint = CGPointZero; //position based on bottom-left corner
square.position = CGPointMake(x, y);
square.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(width, height)];
Any ideas or advice to solving this problem would be appreciated. For example, if I could visualize the physics bodies, that might help, but I'm not sure how to.
UPDATE: So I've solved the problem by simply not setting the anchor point and repositioning my rectangles. So the problem still exists, but I have a work around in place and the work around is working well.
I wrote this to fix Apple's lack thereof:
use pathForRectangleOfSize:withAnchorPoint: to replace your call to bodyWithRectangleOfSize: whose brief documentation tells us the problem: "Creates a rectangular physics body centered on the owning node’s origin."
#implementation SKPhysicsBody (CWAdditions)
+ (CGPathRef)pathForRectangleOfSize:(CGSize)size withAnchorPoint:(CGPoint)anchor {
CGPathRef path = CGPathCreateWithRect( CGRectMake(-size.width * anchor.x, -size.height * anchor.y,
size.width, size.height), nil);
return path;
}
+ (SKPhysicsBody *)bodyWithRectangleOfSize:(CGSize)size withAnchorPoint:(CGPoint)anchor {
CGPathRef path = [self pathForRectangleOfSize:size withAnchorPoint:anchor];
return [self bodyWithPolygonFromPath:path];
}
#end
Edit: There is a new API in 7.1 to provide for this oversight.
+ (SKPhysicsBody *)bodyWithRectangleOfSize:(CGSize)s center:(CGPoint)center
You can use
[SKPhysicsBody bodyWithRectangleOfSize:size center: center];
CGPoint center = CGPointMake(size.width*(anchorPoint.x-0.5f), size.height*(0.5f-anchorPoint.y))
should transform the bounding box according to the anchorPoint of the parents node
I am using Swift but the SKPhysicsBody was kind of half width and height wrong. I am using anchor point(0,0). Then I used the method with rectangleOfSize, center :
var cc = SKSpriteNode(color: UIColor.greenColor(), size: CGSizeMake(32, 64))
cc.physicsBody = SKPhysicsBody(rectangleOfSize: cc.size, center: CGPointMake(32/2, 64/2))
I hope it works for you too guys...thanks !
You need the anchorPoint only when you set your spriteNode's position. I don't quite understand why would you need to move physicsBody (which is the same size of node, I presume) to a corner...
But you might find useful this class method [SKPhysicsBody bodyWithPolygonFromPath:path].
Here is a nice generator for that: http://dazchong.com/spritekit/
I have run in to this myself when using SpriteKit. Unfortunately there appears to be issues based on the timing of creating a physics body and actually adding the node to the scene. If you swap the order from your code and ensure you don't do ANY physics changes until the node is actually in the render tree, that should resolve the 'weirdness' and everything will be all gravy.
Swift 4.1
let centerPoint = CGPoint(x: house1.size.width / 2 - (house1.size.width * house1.anchorPoint.x), y: house1.size.height / 2 - (house1.size.height * house1.anchorPoint.y))
house1.physicsBody = SKPhysicsBody(rectangleOf: house1.size, center: centerPoint)
Use default anchor point and it should solve your problem. I had the same problem and I had to programmatically set anchor point to x: 0.5 and y: 0.5 for every physics body in scene. This solved my problems.
Related
I have created an SKSpriteNode called let's say Map that has an edge path that I have defined (some simple polygon shape).
What I am trying to figure out is how to add several other edge paths that would act as interior edges of the Map. As if the "map" as a whole did in fact have holes. Some sort of inner boundary shapes that could act with Map as a whole: one edge path (as shown below)
©
I understand that there is a method that allows for creating an SKPhysicsBody with bodies (some NSArray), like such
Map.physicsBody = [SKPhysicsBody bodyWithBodies:bodiesArray];
Does this method in fact generate what I have shown in the image? Assuming that the bodiesArray contains 3 SKSpriteNode's each with a defined path from using such method:
+ (SKPhysicsBody *)bodyWithEdgeChainFromPath:(CGPathRef)path
, with creating the path like such
SKSpriteNode *innerNode1 = [SKSpriteNode spriteNodeWithImageNamed:#"map"];
CGMutablePathRef innerNode1Path = CGPathCreateMutable();
CGPathMoveToPoint(mapPath, NULL, 1110, 1110);
CGPathAddLineToPoint(mapPath, NULL, <some x1>, <some y1>);
CGPathAddLineToPoint(mapPath, NULL, <some x2>, <some y2>);
CGPathAddLineToPoint(mapPath, NULL, <some x3>, <some y3>);
.
.
.
CGPathCloseSubpath(mapPath);
innerNode1.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:innerNode1Path];
[bodiesArray addObject:innerNode1];
// Repeat for other 2 nodes
I understand that an alternative would be to create 3 separate nodes with the location and shape of the intended "holes", but I am tying to avoid creating more nodes than I need. If anyone can confirm what I am trying to do is correct, or perhaps suggest an alternative that I am unaware of.
NOTE: IF what I am doing is correct but I am missing something, I would appreciate it if someone can show me the correct way to do what I am trying to do (even a simple example of a square with an inner smaller square would be great). Thanks!
EDIT 1:
Below is the code snippet that I am using as an attempt to create the "inner boundaries". This issue here, is that while both the outer and inner rect's are drawn and shown, when I add the inner rect to the Map bodyWithBodies, it takes full control of the collision detection, removing all contact control from the outer rect shell. When I remove the bodyWithBodies it goes back to normal with showing both rects, the outer has collision detection (does not allow me to pass through), while the inner one has nothing... so close
// 1 Create large outer shell Map
CGRect mapWithRect = CGRectMake(map.frame.origin.x + offsetX, map.frame.origin.y + offsetY, map.frame.size.width * shrinkage, map.frame.size.height * shrinkage);
self.physicsWorld.gravity = CGVectorMake(0.0, 0.0);
self.physicsWorld.contactDelegate = self;
// 2 Create smaller inner boundary
CGRect innerRect = CGRectMake(100, 100, 300, 300);
SKPhysicsBody *body = [SKPhysicsBody bodyWithEdgeLoopFromRect:innerRect];
body.categoryBitMask = wallCategory;
NSArray *bodyArray = [NSArray arrayWithObject:body];
// 3 Add bodies to main Map body
myWorld.physicsBody = [SKPhysicsBody bodyWithBodies:bodyArray];
myWorld.physicsBody.categoryBitMask = wallCategory;
if ( [[levelDict objectForKey:#"DebugBorder"] boolValue] == YES) {
// This will draw the boundaries for visual reference during testing
[self debugPath:mapWithRect];
[self debugPath:innerRect];
}
EDIT 2
This approach works..by just adding a new node with the same properties as the outer rect:
SKPhysicsBody *innerRectBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:innerRect];
innerRectBody.collisionBitMask = playerCategory;
innerRectBody.categoryBitMask = wallCategory;
SKNode *innerBoundary = [SKNode node];
innerBoundary.physicsBody = innerRectBody;
[myWorld addChild: innerBoundary];
...but I would very much like a cleaner solution that does not require additional nodes..thoughts?
you are doing nothing wrong here i come with an example where i created two edge rect bodies with two physics bodies
//adding bodies after some time using gcd
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self addBodyA];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self addBodyB];
});
-(void)addBodyB
{
SKSpriteNode *node=[SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(20, 20)];
node.physicsBody=[SKPhysicsBody bodyWithRectangleOfSize:node.frame.size];
node.position=CGPointMake(550, 420);
node.physicsBody.restitution=1;
[self addChild:node];
}
-(void)addBodyA
{
SKSpriteNode *node=[SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(20, 20)];
node.physicsBody=[SKPhysicsBody bodyWithRectangleOfSize:node.frame.size];
node.position=CGPointMake(400, 420);
node.physicsBody.restitution=1;
[self addChild:node];
}
-(void)addEdgesBodies
{
SKAction *r=[SKAction rotateByAngle:1.0/60 duration:1.0/60];
SKSpriteNode *rect=[SKSpriteNode spriteNodeWithColor:[SKColor clearColor] size:CGSizeMake(300,300)];
rect.physicsBody=[SKPhysicsBody bodyWithEdgeLoopFromRect:rect.frame];
rect.position=CGPointMake(500, 400);
[self addChild:rect];
//
SKSpriteNode *rect1=[SKSpriteNode spriteNodeWithColor:[SKColor clearColor] size:CGSizeMake(100,100)];
rect1.physicsBody=[SKPhysicsBody bodyWithEdgeLoopFromRect:rect1.frame];
rect1.position=CGPointMake(550, 450);
[self addChild:rect1];
[rect1 runAction:[SKAction repeatActionForever:r]];
}
[self addEdgesBodies];
remember edge bodies comes with low cpu overhead so don't worry about performance untill your polygon don't have so many edges.
Your code for making a path then using it in a physics body looks like it would work. As well as your physics body from bodies. Unfortunately I do not know SKPhysicsBody's can really support holes because you can not flip the normals of the body. The way I read apples documentation is that it is meant to do things like take two circle shapes and make them into one body, rather then creating a complex shape like that. The problem being that having a hole inside of your bigger shape would mean ignoring collision in that area.
Here are some alternative options
One option is you could build your stages from multiple shapes. For example if you break your map into two pieces (with a line going through each shape) and make physics bodies for those. Then have them overlap and make them into one body then it might work out. I made a diagram showing this (pardon its terrible quality you should still be able to understand what it is doing (hopefully)).
Another option would be to make it with a texture, this can hurt preformance a bit but if you can manage it then it probably would work nicely.
Map.physicsBody = SKPhysicsBody(texture: Map.texture, size: Map.size)
I'm very new to cocos2d development.
What I am trying to implement is some sprite nodes with physics bodies. As far as I understand, you're supposed to add them to the scene like this ("physics" here is the name of CCPhysicsNode declared earlier):
[physics addChild:node];
instead of
[self addChild:node];
The second one is pretty self-explanatory, I never had any trouble with it. But with the first the position of collision shapes does not match the position of the actual sprite (as seen in debug drawing, and by offset I mean greatly offset, like 2x the actual position). This is how I declare and add the node:
CCSprite *sprite = [CCSprite spriteWithImageNamed:#"sprite.png"];
sprite.position = position;
sprite.physicsBody = [CCPhysicsBody bodyWithRect:[sprite boundingBox] cornerRadius:0];
sprite.physicsBody.collisionType = #"SomeCollisionType";
sprite.name = #"Name";
[physics addChild:sprite];
What am I doing wrong? Please explain me how to make the positions match. TIA.
Two likely related things here:
1) I can draw a box and add to child from my SKScene impelmentation file with self, self.scene, and myWorld, but not with an SKSprite node's scene property.
SKSpriteNode *bla = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(100, 100)];
[self.scene addChild:bla]; // If I use bla.scene, it doesn't work. self, self.scene, myworld all do work though.
bla.position = CGPointMake(0, 0);
bla.zPosition = 999;
2) I've seen the related questions here and here, but I'm trying to add a joint during gameplay (grabbing a rope). This method gets called after doing some sorting in `didBeginContact:
-(void)monkey:(SKPhysicsBody *)monkeyPhysicsBody didCollideWithRope:(SKPhysicsBody *)ropePhysicsBody atPoint:(CGPoint)contactPoint
{
if (monkeyPhysicsBody.joints.count == 0) {
// Create a new joint between the player and the rope segment
CGPoint convertedRopePosition = [self.scene convertPoint:ropePhysicsBody.node.position fromNode:ropePhysicsBody.node.parent];
SKPhysicsJointPin *jointPin = [SKPhysicsJointPin jointWithBodyA:playerPhysicsBody bodyB:ropePhysicsBody anchor:convertedRopePosition];
jointPin.upperAngleLimit = M_PI/4;
jointPin.shouldEnableLimits = YES;
[self.scene.physicsWorld addJoint:jointPin];
}
}
I've got showPhyiscs enabled on the scene, so I can see that the joint is ending up in a totally wacky place. Unfortunately, I don't know how to apply the linked solutions since I'm not adding the SKSpriteNodes in this method, they already exist, so I can't just flip the order of position and physicsBody.
I've tried every permutation I could for both of the convertPoint methods. Nothing worked. My best guess is that physicsWorld is using some wacky coordinate system.
Method members of SKPhysicsWorld that relate to position (CGPoint) or frame (CGRect) are to be in scene coordinates. Scene coordinates reference the point {0,0} as the bottom left corner and is consistent throughout SpriteKit.
The scene property of your object named bla will be nil when bla is first created and is set by the scene when added to it.
[bla.scene addChild:bla]; // this won't do anything as scene will be nil when first created
It looks as though convertedRopePosition is being assigned an incorrect value because the second member you're passing into, - (CGPoint)convertPoint:(CGPoint)point fromNode:(SKNode *)node , is the scene when it should be another node in the same node tree as this node. where this node is the caller (in this case the SKScene subclass).
Try replacing the line-
CGPoint convertedRopePosition = [self.scene convertPoint:ropePhysicsBody.node.position fromNode:ropePhysicsBody.node.parent];
with-
CGPoint convertedRopePosition = [self convertPoint:ropePhysicsBody.node.position fromNode:ropePhysicsBody.node];
I came up with a janky work around for this problem. It turns out that the coordinate system offset for physicsWorld was likely due to an anchor difference. Changing the anchors of every related thing made no difference, and you can't change the anchor of the physicsWorld directly, so I ended up adding half of the scene width and half of the scene height to the anchor position of my joint. That got it to show in the right place and behave normally.
This problem persisted once side scrolling was factored in. I've posted other questions here with this same problem but I'll include th
I added the following convenience method to my GameScene.m. It essentially takes the place of the seemingly useless convertTo built in method.
-(CGPoint)convertSceneToFrameCoordinates:(CGPoint)scenePoint
{
CGFloat xDiff = myWorld.position.x - self.position.x;
CGFloat yDiff = myWorld.position.y - self.position.y;
return CGPointMake(scenePoint.x + self.frame.size.width/2 + xDiff, scenePoint.y + self.frame.size.height/2 + yDiff);
}
I use this method to add joints. It handles all of the coordinate system transformations that need to be dealt with that lead to the issue raised in this question. For example, the way I add joints
CGPoint convertedRopePosition = [self convertSceneToFrameCoordinates:ropePhysicsBody.node.position];
SKPhysicsJointPin *jointPin = [SKPhysicsJointPin jointWithBodyA:monkeyPhysicsBody bodyB:ropePhysicsBody anchor:convertedRopePosition];
jointPin.upperAngleLimit = M_PI/4;
jointPin.shouldEnableLimits = YES;
[self.scene.physicsWorld addJoint:jointPin];
Hi I have a bunch of round SKSpriteNodes with a circle physical body. Now when these balls roll down a path I want some of these SKSpritenodes image to stay upright even when rolling. So think of an arrow pointing upwards. When the ball starts rolling the arrow spins in circles. But for some balls Id like the arrow to remain pointing up even when the ball rolls. Whats the best way of doing this?
Edit
So an answer was given but from testing it turns out it is not the correct one. Not allowing the ball to rotate affects the way it rolls down the path. So I guess what I want is rotation to be on but the image to always appear to the user like its not rotating. Thanks.
This looks like a job for SupermSKConstraint. Constraints are evaluated and applied after the physics simulation runs on each frame, so you can use them for tasks like making a node point a certain direction regardless of what physics does to it. For this, you'd want a zRotation constraint.
But there's a bit more to it than that. If you set a zero-rotation constraint on the ball:
// Swift
let constraint = SKConstraint.zRotation(SKRange(constantValue: 0))
ball.constraints = [constraint]
You'll find that SpriteKit resets the physics body's transform every frame due to the constraint, so it only sort-of behaves like it's rolling. Probably not what you want. (To get a better idea what's going on here, try adding a zero-rotation constraint to a rectangular physics body in a world without gravity, applying an angular impulse to it, and watching it try to spin in a view with showsPhysics turned on. You'll see the sprite and its physics body get out of sync and shake a bit -- probably due to accumulated rounding errors as the physics engine and the constraint engine fight it out.)
Instead, you can do a bit of what's in 0x141E's answer, but use constraints to make it less code (and run more efficiently):
Give the ball node a circular physics body. (And possibly no texture, if the only art you want for the ball is a non-rotating sprite.)
Add the arrow node as a child of the ball node. (It doesn't need its own physics body.)
Put a zero-rotation constraint on the arrow.
Wait, that doesn't work -- I told the arrow to not rotate, but it's still spinning?! Remember that child nodes are positioned (and rotated and scaled) relative to their parent node. So the arrow isn't spinning relative to the ball, but the ball is spinning. Don't worry, you can still solve this with a constraint:
Tell the constraint to operate relative to the node containing the ball (probably the scene).
Now the constraint will keep the arrow in place while allowing the ball to rotate however the physics simulation wants it to.
Here's some test code to illustrate:
// Step 1: A rectangular spinner so we can see the rotation
// more easily than with a ball
let spinner = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 300, height: 20))
spinner.position.x = scene.frame.midX
spinner.position.y = scene.frame.midY
spinner.physicsBody = SKPhysicsBody(rectangleOfSize: spinner.size)
scene.addChild(spinner)
spinner.physicsBody?.applyAngularImpulse(0.1) // wheeeeee
// Step 2: Make the arrow a child of the spinner
let arrow = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width: 20, height: 50))
spinner.addChild(arrow)
// Step 3: Constrain the arrow's rotation...
let constraint = SKConstraint.zRotation(SKRange(constantValue: 0))
arrow.constraints = [constraint]
// Step 4: ...relative to the scene, instead of to its parent
constraint.referenceNode = scene
Here are two methods to create a ball with a physics body and an arrow:
Add an arrow as a child of a ball
Add both the ball and the arrow directly to the scene
Here's what will happen when you add the above to the SpriteKit simulation:
The arrow will rotate when the ball rotates
Both the arrow and the ball will move/rotate independently
If you want the arrow to rotate with the ball, choose Option 1. If you want the arrow to remain fixed, choose Option 2. If you choose Option 2, you will need to adjust the rotation of the arrow to ensure that it points upward. Here's an example of how to do that.
-(void)didMoveToView:(SKView *)view {
self.scaleMode = SKSceneScaleModeResizeFill;
/* Create an edge around the scene */
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:view.frame];
// Show outline of all physics bodies
self.view.showsPhysics = YES;
CGFloat radius = 16;
SKNode *balls = [SKNode node];
balls.name = #"balls";
[self addChild:balls];
// Create 5 balls with stationary arrows
for (int i = 0;i<5;i++) {
// Create a shape node with a circular physics body. If you are targeting iOS 8,
// you have other options to create circular node. You can also create an SKSpriteNode
// with a texture
SKShapeNode *ball = [SKShapeNode node];
// Create a CGPath that is centered
ball.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-radius,-radius,radius*2,radius*2)].CGPath;
ball.fillColor = [SKColor whiteColor];
ball.position = CGPointMake(100, 100+i*radius*2);
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:radius];
[balls addChild:ball];
// Create an arrow node
CGSize size = CGSizeMake(2, radius*2);
SKSpriteNode *arrow = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:size];
arrow.name = #"arrow";
arrow.position = CGPointZero;
[ball addChild:arrow];
// Apply angular impulse to the ball so it spins when it hits the floor
[ball.physicsBody applyAngularImpulse:-1];
}
}
- (void) didSimulatePhysics
{
SKNode *balls = [self childNodeWithName:#"balls"];
for (SKNode *ball in balls.children) {
SKNode *arrow = [ball childNodeWithName:#"arrow"];
arrow.zRotation = -ball.zRotation;
}
}
sprite.physicsBody.allowsRotation = NO;
The allowRotation property should control exactly what you are asking.
I am creating a simple Sprite Kit game however when i am adding the PhysicsBody to one of my sprites it seems to be going in the wrong position. i know that it is in the wrong position as i have have set
skView.showsPhysics = YES;
and it is showing up in the wrong position.
The Square in the bottom corner is the physics body for the first semicircle. I am using a square at the moment just for testing purposes.
My app includes view following and follows my main sprite when it moves. I implemented this by following apples documentation and creating a 'myworld' node and creating all other nodes from that node.
myWorld = [SKNode node];
[self addChild:myWorld];
semicircle = [SKSpriteNode spriteNodeWithImageNamed:#"SEMICRICLE.png"];
semicircle.size = CGSizeMake(semicircle.frame.size.width/10, semicircle.frame.size.height/10);
semicircle.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:semicircle.frame.size];
semicircle.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
semicircle.physicsBody.dynamic = YES;
semicircle.physicsBody.collisionBitMask = 0;
semicircle.name = #"semicircle";
[myWorld addChild:semicircle];
To centre on the node I call these methods
- (void)didSimulatePhysics
{
[self centerOnNode: [self childNodeWithName: #"//mainball"]];
}
- (void) centerOnNode: (SKNode *) node
{
CGPoint cameraPositionInScene = [node.scene convertPoint:node.position fromNode:node.parent];
node.parent.position = CGPointMake(node.parent.position.x - cameraPositionInScene.x, node.parent.position.y - cameraPositionInScene.y);
}
I don't know if the my world thing makes any difference to the SkPhysics body...
SKPhysicsBody starts at coordinates 0,0 which is at the bottom left hand corner. If you make the area smaller, as you did by width/10 and height/10, you decrease the size but from the bottom left.
I think what you are looking for is bodyWithRectangleOfSize:center: which allows you to manually set the center from which you base your physics body area on.
Update:
Based on what I understand, your smallest semi circle pic size is the same as the screen size. I would suggest you modify the image size to something like the example I have. You can then set the sprite's position as required and set the physics body to the half of the image containing your semi circle.
Your centerOnNode call should be put in the didEvaluateActions function instead of the didSimulatePhysics function. This is because you need to move the world before the physics are drawn so that they stay in sync. Similar question found here: https://stackoverflow.com/a/24804793/5062806