Reputation: 1132
It seems like the Spritekit's API does not allow to change the center of the physicsbody when using 'bodyWithTexture' (or maybe I'm missing something).
I'm wondering how you get around this when using a pixel precision physicsbody.
//Upper Claw
SKSpriteNode *claw = [SKSpriteNode spriteNodeWithImageNamed:BOSS_CLAW_SPRITE];
claw.position = CGPointMake(800, 520);
claw.anchorPoint = CGPointMake(1, .7);
//physics
claw.physicsBody = [SKPhysicsBody bodyWithTexture:claw.texture size:claw.size];
You can clearly see that the physicsbody's center position is where the anchor point is.
Alternatively, bodyWithCircle / bodyWithRectangle have a 'center' property. However, it isn't as precise and needs a lot of code to make (unscalable).
-(SKPhysicsBody*)getPhysicsForClaw:(BOOL)isUpperClaw
{
NSInteger reverseConstant = 1;
if (!isUpperClaw) {
reverseConstant = -1;
}
SKPhysicsBody *clawTip1 = [SKPhysicsBody bodyWithCircleOfRadius:5 center:CGPointMake(-545, -140*reverseConstant)];
SKPhysicsBody *clawTip2 = [SKPhysicsBody bodyWithCircleOfRadius:6 center:CGPointMake(-540, -130*reverseConstant)];
SKPhysicsBody *clawTip3 = [SKPhysicsBody bodyWithCircleOfRadius:7 center:CGPointMake(-535, -120*reverseConstant)];
SKPhysicsBody *clawTip4 = [SKPhysicsBody bodyWithCircleOfRadius:8 center:CGPointMake(-530, -110*reverseConstant)];
SKPhysicsBody *clawTip5 = [SKPhysicsBody bodyWithCircleOfRadius:8 center:CGPointMake(-525, -100*reverseConstant)];
SKPhysicsBody *clawTip6 = [SKPhysicsBody bodyWithCircleOfRadius:9 center:CGPointMake(-515, -90*reverseConstant)];
SKPhysicsBody *clawTip7 = [SKPhysicsBody bodyWithCircleOfRadius:11 center:CGPointMake(-508, -78*reverseConstant)];
SKPhysicsBody *clawTip8 = [SKPhysicsBody bodyWithCircleOfRadius:12 center:CGPointMake(-495, -65*reverseConstant)];
SKPhysicsBody *clawTip9 = [SKPhysicsBody bodyWithCircleOfRadius:13 center:CGPointMake(-480, -50*reverseConstant)];
SKPhysicsBody *clawTip10 = [SKPhysicsBody bodyWithCircleOfRadius:14 center:CGPointMake(-465, -35*reverseConstant)];
SKPhysicsBody *clawTeeth1 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(6, 40) center:CGPointMake(-433, -70)];
SKPhysicsBody *clawTeeth2 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(15, 20) center:CGPointMake(-420, -60)];
SKPhysicsBody *clawTeeth3 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(9, 40) center:CGPointMake(-395, -70)];
SKPhysicsBody *clawTeeth4 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(15, 20) center:CGPointMake(-382, -60)];
SKPhysicsBody *clawTeeth5 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(9, 40) center:CGPointMake(-345, -70)];
SKPhysicsBody *clawTeeth6 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(10, 20) center:CGPointMake(-334, -60)];
SKPhysicsBody *clawTeeth7 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(9, 30) center:CGPointMake(-295, -60)];
SKPhysicsBody *clawTeeth8 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(9, 30) center:CGPointMake(-255, -42)];
SKPhysicsBody *clawBody1 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(100, 45) center:CGPointMake(-400, -30)];
SKPhysicsBody *clawBody2 = [SKPhysicsBody bodyWithCircleOfRadius:26 center:CGPointMake(-325, -25*reverseConstant)];
SKPhysicsBody *clawBody3 = [SKPhysicsBody bodyWithCircleOfRadius:28 center:CGPointMake(-290, -12*reverseConstant)];
SKPhysicsBody *clawBody4 = [SKPhysicsBody bodyWithCircleOfRadius:29 center:CGPointMake(-250, 0*reverseConstant)];
SKPhysicsBody *clawBody5 = [SKPhysicsBody bodyWithCircleOfRadius:28 center:CGPointMake(-210, 10*reverseConstant)];
SKPhysicsBody *clawBody6 = [SKPhysicsBody bodyWithCircleOfRadius:30 center:CGPointMake(-165, 24*reverseConstant)];
SKPhysicsBody *clawBase1 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(120, 55) center:CGPointMake(-75, 24)];
SKPhysicsBody *claw = [SKPhysicsBody bodyWithBodies:@[clawTip1, clawTip2, clawTip3, clawTip4, clawTip5, clawTip6, clawTip7, clawTip8, clawTip9, clawTip10, clawTeeth1, clawTeeth2, clawTeeth3, clawTeeth4, clawTeeth5, clawTeeth6, clawTeeth7, clawTeeth8, clawBody1, clawBody2, clawBody3, clawBody4, clawBody5, clawBody6, clawBase1]];
return claw;
}
Upvotes: 4
Views: 812
Reputation: 925
Code example from the solution of primaryartemis, will work for any other shapes beside from circle and rectangle, which is more practical.
In other words, we use a SKNode
to contain our sprite(s), the SKNode
's position will act like anchorPoint, we shift our sprite(s) away from this SKNode
container to our desire position for our transformation to work as intended.
This is the graphic, a simple half circle arc with size of (width:100, height: 50) ,we want this graphic to rotate at the anchorPoint of (0.5, 0.0).
class SemiArc: SKNode {
var sprite: SKSpriteNode
override init() {
sprite = SKSpriteNode(imageNamed: "semi_arc")
super.init()
// Move away from this container SKNode, this SKNode's position act like anchorPoint
sprite.position.y = sprite.size.height / 2
// The physicsBody will move along as well
sprite.physicsBody = SKPhysicsBody(texture: sprite.texture!, size: sprite.size)
// Add the sprite to this container SKNode
addChild(sprite)
// To test the transformation from the anchor point we desire
let rotationForever = SKAction.repeatActionForever(SKAction.rotateByAngle(CGFloat(M_PI) * 2, duration: 2.0))
runAction(rotationForever)
}
}
We then can just place this SemiArc
SKNode
to our scene, it will anchor at the point we desire.
We can create a helper of this SKNode
container so we can reuse it more easily for simple sprite.
extension SKNode {
class func containerNodeWithSprite(sprite: SKSpriteNode, withAnchorPoint anchorPoint: CGPoint) -> SKNode {
let containerNode = SKNode()
sprite.position.x = (sprite.size.width / 2) - ( sprite.size.width * anchorPoint.x)
sprite.position.y = (sprite.size.height / 2) - ( sprite.size.height * anchorPoint.y)
containerNode.addChild(sprite)
return containerNode
}
}
Example usage:
// Somewhere in a scene class
let sprite = SKSpriteNode(imageNamed: "semi_arc")
sprite.physicsBody = SKPhysicsBody(texture: sprite.texture!, size: sprite.size)
let container = SKNode.containerNodeWithSprite(sprite, withAnchorPoint: CGPointMake(0.5 , 0.0))
container.position = view!.center
let rotationForever = SKAction.repeatActionForever(SKAction.rotateByAngle(CGFloat(M_PI) * 2, duration: 2.0))
containedNode.runAction(rotationForever)
addChild(container)
I discover a caveat of this solution which is when you want to remove this SKNode
based on physic simulated position will not work, because you did not give the container SKNode
a physic body, so it will never change its position. Your logic that check it is out of bound will not work, since you are checking this container. This also the same when you detecting contact and collision, it is the physic body attached node inside the container cause contact and collision, NOT the container itself. But you can still react properly for contact and collision, the problem only arise when you want to remove this SKNode
Workaround 1: Compute the relative position of its children node with physic body plus the container position.
enumerateChildNodesWithName("fallingObjectWithMiddleBottomAnchor") {
(node, stop) in
if ((node.children.first?.position.y)! + node.position.y) < 0 {
node.removeFromParent()
}
}
Workaround 2: Don't use this for dynamic body, it make sense not mess with anchor point for dynamic body, as its property should be simulate, not manually update.
Workaround 3: A better solution is use SKConstraint to achieve what you need.
Upvotes: 1
Reputation: 91
My solution for situations like this is to use an invisible SKNode.
Have the claws without a changed anchor point so that it lines up with the body. Then parent that node to a blank SKNode and shift the claws over accordingly. Now by rotating the blank SKNode you get the effect of a changed anchor point whilst the physics body still lines up.
Upvotes: 3
Reputation: 11696
Anchor points have no effect on a physics body. There are a couple of physics bodies for which you can define a center point.
(SKPhysicsBody *)bodyWithCircleOfRadius:(CGFloat)r
center:(CGPoint)center
(SKPhysicsBody *)bodyWithRectangleOfSize:(CGSize)s
center:(CGPoint)center
Unfortunately the bodyWithTexture:
has no such capability. As a hack you could use a number of various sized rectangles, rotate them to the desired angle and join them together with (SKPhysicsBody *)bodyWithBodies:(NSArray *)bodies
. This will allow you to pretty much cover your texture.
As an added benefit, using rectangles instead of bodyWithTexture is also less of a burden to your FPS.
Upvotes: 2