I want to create a metal ball on the screen, I had the material and run animation done. But When the ball was rolling that I hope the texture shouldn't roating. Because that made rolling ball not look like metal ball...
There is anyways or not? I could make SKSpriteNode don't rotate the ball.
SKSpriteNode *node = [self spriteNodeWithImageNamed:#"ball.png"];
node.name = #"ball";
node.size = CGSizeMake(sideSize, sideSize);
node.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius: sideSize/2];
node.physicsBody.restitution = 0.2;
node.physicsBody.friction = 0.01;
node.physicsBody.angularDamping = 0.5;
node.physicsBody.categoryBitMask = GMCategoryBall;
node.position = CGPointMake(wood.position.x,wood..position.y +wood..size.height);
node.physicsBody.contactTestBitMask =.....
node.physicsBody.collisionBitMask =.....
Here are some suggestions:
Add another SKSpriteNode without a physicsBody that is just there for looks. Have it always centered on your ball.
Add another SKSpriteNode as a sub-node of your ball, and set its rotation to the opposite of your ball's rotation.
Add another SKSpriteNode, set its physicsBody to a bit mask value that won't interact with anything else in the scene (0 is a good choice) and set a joint that keeps it locked to the ball. Turn off rotation for that Sprite.
Related
I have a player character and a ground tile. On both physics bodies, I have restitution set to 0.
To jump, I directly set the velocity.dy of the player. When the player comes back to the ground due to gravity, for the most part it works, or seems to.
Problems occur when I repeatedly jump. If I jump right as the player lands, there's some bounce and the height the player reaches on the next bounce doesn't always match the initial bounce.
I've tried various ways to force the velocity.dy to 0 when the user lands, but nothing fixes the weird jumping issue. How can I properly and smoothly have a consistent physics jump?
Honestly, I am not sure what you are trying to accomplish. Normally we shouldn't mess with velocities of objects. In a typical Spritekit game, we must treat it like a "real world" situation and generally apply force or impulse on the object.
I suspect you are trying to make a Mario-like game. All you have to do is apply a big enough gravity to the physicsworld and apply impulse on the sprite to jump (on touchesBegan delegate).
Just now I went ahead and made a simple mario jump scenario in Spritekit. And this is what I ended up with by setting gravity -30 for the y-component and impulse y=100 on the mario sprite. (Frame rate is bad. Looks better on simulator/device)
PhysicsWorld setup:
[self.physicsWorld setGravity:CGVectorMake(0, -30)];
self.physicsWorld.contactDelegate = self;
Mario and platform sprite setup code:
SKSpriteNode *platform = [SKSpriteNode spriteNodeWithImageNamed:#"platform"];
platform.position = CGPointMake(view.frame.size.width/2.0,0);
platform.name = #"platform";
platform.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:platform.frame.size];
platform.physicsBody.categoryBitMask = platformCategory;
platform.physicsBody.dynamic = NO;
platform.physicsBody.usesPreciseCollisionDetection = YES;
platform.physicsBody.affectedByGravity = NO;
[self addChild:platform];
SKSpriteNode *mario = [SKSpriteNode spriteNodeWithImageNamed:#"mario"];
mario.position = CGPointMake(view.frame.size.width/2.0, 400);
mario.name = #"mario";
mario.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:mario.frame.size];
mario.physicsBody.categoryBitMask = marioCategory;
mario.physicsBody.dynamic = YES;
mario.physicsBody.usesPreciseCollisionDetection = YES;
mario.physicsBody.contactTestBitMask = platformCategory;
mario.physicsBody.affectedByGravity = YES;
[self addChild:mario];
touchesBegan:
SKSpriteNode *mario = (SKSpriteNode*)[self childNodeWithName:#"mario"];
[mario.physicsBody applyImpulse:CGVectorMake(0, 100)];
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
I created an SKEmitterNoder (standard Fire.sks template) & added it to an SKSpriteNode called ball. The thing is, I followed a tutorial of iOS Games by Tutorials & the way they taught me to create Tiled maps, is essentially flip them upon init so that (0, 0) would be in bottom left corner of the screen & NOT top left. I think this is affecting my SKEmitterNode targetNode property because as the ball is moving in the scene, the fire emitter node goes in the exact opposite direction. Could someone tell me what to do to change this? I can't change the world parameters, so I need something that would alter the SKEmitterNode trajectory so that it actually follows the ball node properly. Here's my current code:
NPCBall *ball = [[NPCBall alloc] initWithBallType:BallTypeRed andName:#"Ball Red"];
ball.position = CGPointMake(x + w/2, y + h/2);
ball.zPosition = -104.0;
[_entityNode addChild:ball];
//Create a random Vector.dx & dy for the NPC. This will apply a random impulse to kick start NPC non-stop movement.
[ball.physicsBody applyImpulse:CGVectorMake([self randomVectorGeneration].dx, [self randomVectorGeneration].dy)];
//Create a particle for the ball (fire).
SKEmitterNode *emitFire = [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:#"Particle_Fire" ofType:#"sks"]];
emitFire.position = CGPointMake(ball.position.x, ball.position.y);
emitFire.targetNode = ball;
[_entityNode addChild:emitFire];
P.S. _entityNode, to which both the ball & SKEmitterNode fire are added, is an SKNode. Which in turn is a child of an SKNode called _worldNode. _worldNode is a child of self (which is an SKScene). This _worldNode is the one flipped to have (0, 0) coordinates begin at the bottom left.
A buddy of my'n has found a solution to this. I'm posting it here in case someone also has an inverted Tiled map where the (0, 0) coordinates are in the bottom left corner of the scene.
//Create a ball sprite.
SKSpriteNode *ball = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(20, 20)];
ball.position = CGPointMake(_worldNode.frame.size.width/2, _worldNode.frame.size.height/2);
[_entityNode addChild:ball]; //<--_entityNode is also an SKNode. It's a child of _worldNode.
ball.zPosition = -104.0;
//Create a random Vector.dx & dy for the NPC. This will apply a random impulse to kick start NPC non-stop movement.
[ball.physicsBody applyImpulse:CGVectorMake([self randomVectorGeneration].dx, [self randomVectorGeneration].dy)];
//Create a particle effect for the ball.
SKEmitterNode *emitFire = [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:#"Particle_Fire" ofType:#"sks"]];
emitFire.name = #"Fire Emitter";
emitFire.targetNode = _worldNode; //<-- This is the demon. Make the emitter follow this SKNode since it moves when the ball moves. Everything in my game is a child of _worldNode.
[ball addChild:emitFire]; //Add the emitter to the ball.
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.
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.