Cocos2d 2.0 - centering a sprite on a layer - ios

I have a CClayer class and when this class inits it creates a CCSprite that should be centered, so later, when I rotate an object created with that CCLayer class, it rotates around its center. I mean, if the sprite on that class is an image 200 pixels wide and 300 pixels height, I want the CCLayer pivot to be at 100,150.
I have tried to set it at 0,0 and 0.5,0.5 without success.
As far as I understand, CCLayer has no bounding box, it is like a kind of node, right? so, I create the class like this:
-(id) initWithImage:(UIImage*)image Name:(NSString*)name
{
if( (self=[super init])) {
self.isTouchEnabled = YES;
self.mySprite =
[CCSprite spriteWithCGImage:image.CGImage key:name];
self.mySprite.position = CGPointZero;
[self addChild:self.mySprite];
self.mySprite.anchorPoint = ccp(0.0f, 0.0f);
// have tried also 0.5f, 0.5f... no success
}
return self;
}
How do I do that?
thanks

Provide a method in your CCLayer subclass to rotate the sprite:
-(void) rotateMySpriteToAngle:(float) angle
{
self.mySprite.rotation = angle;
}
The anchor point of the sprite should be (0.5, 0.5) to rotate it about its centre.
I feel you are making your program too complex though? Could you just use a sprite instead of a layer with a sprite as a child? Then you could rotate it directly.
It looks as though you want to make your sprite touchable. Consider using CCMenu and CCMenuItems if you are looking to implement buttons.
Edit
Try setting the anchor point of the layer to (0, 0) and the anchor point of the sprite to (0.5, 0.5), then set the position of the sprite to (0, 0)
This means the centre of the sprite is at (0, 0) on the layer and you then rotate the layer around it's origin.
Scene
=============================================
= =
= =
= =
= =
= | =
= | Layer (effective infinite size) =
= __|__ =
= | | | =
= | +--|-------------- =
= |_____| =
= Sprite =
=============================================
The + is the origin of the layer and the center point of the sprite
When you rotate the layer around it's origin, you are simultaneously rotating the sprite about it's centre.

Related

SpriteNode from class not rotating correctly in Scene

I have a Class with a SpriteNode that rotates very wide, when rotated within the main Scene(as if the anchor point is in the middle of the screen and the Sprite is rotating around it). I want it to rotate around the anchor point of itself in the main Scene(anchor point on the Sprite).
So in the Class i have something like the following
- (void)createChainWithPos:(CGPoint)pos {
SKTexture *myTex...
SKTexture *myTex2...
SKSpriteNode *chainFront = [SKSpriteNode spriteWithTexture:myTex];
chainFront.position = pos;
chainFront.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:mytex.size];
[self addChild:chainFront];
[_chainParts addObject:chainFront];
SKSpriteNode *chainSide = [SKSpriteNode spriteWithTexture:myTex2];
chainSide.position = CGPointMake(chainFront.position.x, chainFront.position.y - chainSide.size.height + 6);
chainSide.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:myTex2.size;
[self addChild:chainSide];
[_chainParts addObject:chainSide];
}
I have an loop creating the chain parts in the main file but couldn't get it rotate so stripped it down in an new project. There is actually 4 chain parts but i only did two. The other two are just mirrors of the ones above with their positions mirroring the chainSide.(to position them in a chain like fashion)
and in the Scene
self.chain1 = [chain node];
[self.chain1 createChainWithPos:CGPointMake(self.size.width/2, self.size.height/2);
self.chain1.zRotation = 3.14/4;
[self addChild:self.chain1];
I have a NSMutableArray in the chain class header that i use to hold the chains.
the physics joints
for (int i = 1; i < self.chain1.chainParts.count; i++ {
SKSpriteNode *nodeA = [[self.chain1 chainParts]objectAtindex:i-1];
SKSpriteNode *nodeB = [[self.chain1 chainParts]objectAtindex:i];
SKPhysicsJointPin *pin = [SKPhysicsJointPin jointWithBodyA:nodeA.physicsBody
bodyB:nodeB.physicsBody
anchor:CGPointMake(CGRectGetMidX(nodeA.frame), CGRectGetMinY(nodeA.Frame))];
}
I found that if i set the position of the chain in the middle in the Class, it rotates correctly in the Scene. However, the physics joints just start moving randomly across the screen and isn't correct to the anchor points set. (the physics joints are set in the Scene)
I don't know if i have to convert the coordinates or play with random anchor point positions , but if someone could shed some light if would be greatly appreciated.
You do not want to use the nodes frame, you want to use the nodes size, the frame mid is the screen coordinate + the fitted node size / 2, you want the middle of your sprite only, also for pi / 4 use M_PI_4

Sprite Kit - Make SKPhysicsBody with restitution only on sides

I am trying to create a complex SKPhysicsBody that does not bounce on the top, but does allow bouncing on the sides and bottom. Currently I am creating two nodes. One that has the image of the Sprite and a no restitution PhysicsBody on the top.
My second node matches the first node, but is clear with the same size as the first node It just has a PhysicsBody on the front and bottom and has a restitution. to allow my main character to bounce off the bottom and front.
Here is the code for my current setup:
//This is the top part.
- (void)nodePhysicsBodySetup:(SKSpriteNode *)node
{
CGPoint topStart = CGPointMake(0, node.size.height);
CGPoint topEnd = CGPointMake(node.size.width, node.size.height);
SKPhysicsBody *topEdge = [SKPhysicsBody bodyWithEdgeFromPoint:topStart toPoint:topEnd];
node.physicsBody = topEdge;
//[SKPhysicsBody bodyWithBodies:#[topEdge, frontEdge, bottomEdge]]
//node.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:node.frame.size];
node.physicsBody.dynamic = NO;
node.physicsBody.restitution = 0.0;
node.physicsBody.affectedByGravity = NO;
node.physicsBody.categoryBitMask = groundCategory;
node.physicsBody.collisionBitMask = mainHeroCategory;
node.physicsBody.contactTestBitMask = mainHeroCategory;
}
//This is the bottom physicsbody that matches up with the image of the main node.
- (void)bottomNodePhysicsBodySetup:(SKSpriteNode *)node
{
CGPoint topStart = CGPointMake(0, node.size.height);
CGPoint frontEnd = CGPointMake(0,0);
CGPoint bottomEnd = CGPointMake(node.size.width,0);
SKPhysicsBody *frontEdge = [SKPhysicsBody bodyWithEdgeFromPoint:topStart toPoint:frontEnd];
SKPhysicsBody *bottomEdge = [SKPhysicsBody bodyWithEdgeFromPoint:frontEnd toPoint:bottomEnd];
bottomNode.physicsBody = [SKPhysicsBody bodyWithBodies:#[frontEdge, bottomEdge]];
bottomNode.physicsBody.dynamic = NO;
bottomNode.physicsBody.affectedByGravity = NO;
bottomNode.physicsBody.restitution = 0.5;
bottomNode.physicsBody.categoryBitMask = otherCategory;
bottomNode.physicsBody.collisionBitMask = mainHeroCategory;
bottomNode.physicsBody.contactTestBitMask = mainHeroCategory;
}
Picture as screenshots are under NDA:
Currently, the Green PhysicsBody is attached to the node, and the red PhysicsBody is attached to a transparent second node with the same position as the first node.
This does not really work. My main character (mainHeroCategory) gets stuck sometimes on the node corner where the PhysicsBody with restitution and the one without meet. What is a better way to do this?
I would try to do this using only one physics body and look at the contact delegate methods to determine where the contact occurred and what the normal vector is (that information is in the SKPhysicsContact object passed in the delegate method).
Once I got that information I would apply an impulse to the mainHeroCategory object, or not, depending on where the contact occurred.
I suggest you join the physics bodies with an SKPhysicsJointFixed or by constraining the bodies with an SKConstraint or two. SpriteKit only uses the shape when merging two or more bodies with bodyWithBodies. From the docs...
The properties on the children, such as mass or friction, are ignored.
Only the shapes of the child bodies are used.
The restitution property is ignored.

Set permanent SKSpritenode image orientation

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.

Sprite Kit - World not in drawing sprites in correct position despite skview with right dimensions

I am trying to draw a basic ground to the game for my sprite to run on.
But it seems that the ground is too short although it is suppose to take up 1/3 of the height of the screen.
My GameScene.sks is already changed to 568x320 (landscape, iPhone 5/5S)
this is my current code
func initMainGround() {
let gSize = CGSizeMake(self.size.width/4*3*2, 120);
let ground = SKSpriteNode(color: SKColor.brownColor(), size: gSize);
ground.name = gName; //Ground
ground.position = CGPointMake(0, 0);
ground.physicsBody = SKPhysicsBody(rectangleOfSize: gSize);
ground.physicsBody.restitution = 0.0;
ground.physicsBody.friction = 0.0;
ground.physicsBody.angularDamping = 0.0;
ground.physicsBody.linearDamping = 0.0;
ground.physicsBody.allowsRotation = false;
ground.physicsBody.usesPreciseCollisionDetection = true; //accurate collision
ground.physicsBody.affectedByGravity = false;
ground.physicsBody.dynamic = false;
ground.physicsBody.categoryBitMask = gBitmask; // 0x1 << 0
ground.physicsBody.collisionBitMask = pBitmask; //0x1 << 1 playerCategoryBitmask
self.addChild(ground);
}
NSLog(String(self.size.height)) return 320.0 which is perfectly fine.
But why is it that the SKSpriteNode is draw wrongly?
Setting the height of the ground to 320 only fills up half of the screen although the height of the screen in landscape is 320.
Like Jon said, this is a placement issue not a size issue. The default anchor point of any given node is in its center, so you have two options here:
1) set ground.position to CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame))
(or even better yet, capture that as an ivar, because you'll be referring to it a whole lot when adding things to your screen, and there's no real reason to do the calculations dozens of times)
2) change the anchor point of the ground node. This is done as a CGPoint, but is interpreted as a percentage of the size of the node in question, with the default (center) being (0.5, 0.5). ground.anchorPoint = CGPointZero (which is just a shortcut for CGPointMake(0, 0)) will set the node's anchor point to its lower-left corner, at which point setting its position to (0,0) will correctly place it starting at the lower-left corner of your scene (or its parent node, in any event).

SKPhysicBody In the Wrong position

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

Resources