I've found lately a tutorial on how to build a game like "flappy bird" using SpriteKit. Instead of implementing the tap-mechanism, I've used the device accelerometer to move the bird, right and left.
My problem, now, is with generating pipes. The method used in the tutorial creates pipes on the x axe and not the y axe, which I want to do.
-(void)createPipes
{
SKTexture* _pipeTexture1 = [SKTexture textureWithImageNamed:#"Pipe1"];
_pipeTexture1.filteringMode = SKTextureFilteringNearest;
SKTexture* _pipeTexture2 = [SKTexture textureWithImageNamed:#"Pipe2"];
_pipeTexture2.filteringMode = SKTextureFilteringNearest;
SKNode* pipePair = [SKNode node];
pipePair.position = CGPointMake( self.frame.size.width + _pipeTexture1.size.width * 2, 0 );
pipePair.zPosition = -10;
CGFloat y = arc4random() % (NSInteger)( self.frame.size.height / 3 );
SKSpriteNode* pipe1 = [SKSpriteNode spriteNodeWithTexture:_pipeTexture1];
[pipe1 setScale:2];
pipe1.position = CGPointMake( self.frame.size.width/2 -100, self.frame.size.height+250 );
pipe1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:pipe1.size];
pipe1.physicsBody.dynamic = NO;
[pipePair addChild:pipe1];
SKSpriteNode* pipe2 = [SKSpriteNode spriteNodeWithTexture:_pipeTexture2];
[pipe2 setScale:2];
pipe2.position = CGPointMake( self.frame.size.width/2 +100, self.frame.size.height+250 );
pipe2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:pipe2.size];
pipe2.physicsBody.dynamic = NO;
[pipePair addChild:pipe2];
SKAction* movePipes = [SKAction repeatActionForever:[SKAction moveByX:0 y:-2 duration:0.02]];
[pipePair runAction:movePipes];
[self addChild:pipePair];
}
My idea is to generate pipes that fall from the "sky" and the bird has to move between the pipes to keep living.
I hope that the description of my problem was very clear :)
Thanks
Clarification : Pipes do fall from the "sky" but the problem lies with their positioning on the screen. When I run the project, there's no gap between the right pipe or the left one. I only see a giant pipe falling, filling, vertically, a good proportion of the screen.
Thanks for linking the tutorial!
If you want the pipes to fall, then just let them fall! The pipes have physics bodies attached so they should fall down from the sky depending on their vertical position. There's no need to use the SKAction for moving the pipes then. However, you need to change their dynamic flag to YES so that gravitational force is applied. See Xcode reference:
A Boolean value that indicates whether the physics body is moved by the physics simulation.
The default value is YES. If the value is NO, then the physics body ignores all forces and impulses applied to it. This property is ignored on edge-based bodies; they are automatically static.
I have finally found a solution. By changing the values of pipes position as follow, I've managed to make them appear properly on the screen.
SKSpriteNode* pipe1 = [SKSpriteNode spriteNodeWithTexture:_pipe1];
pipe1.position = CGPointMake( (-0.39*pipe1.size.width), 0 );
SKSpriteNode* pipe2 = [SKSpriteNode spriteNodeWithTexture:_pipe2];
pipe2.position = CGPointMake(self.frame.size.width *1.8, 0 );
The result ==> picture
The big challenge now is find a way to make the position of the pipes somehow random. :)
Related
I am building a platform game in Xcode 6.0 using Sprite Kit. The problem that I have is related to the "monster sprite node" which are created by the following method
-(void) generateMonster {
monster = [SKSpriteNode spriteNodeWithImageNamed:#"monster.png"];
monster.name = #"monster";
monster.size = CGSizeMake(monster.frame.size.width, monster.frame.size.height);
monster.position = CGPointMake(self.currentMonsterX, ground.position.y + monster.frame.size.height);
monster.zPosition = 3.0;
monster.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:monster.size];
monster.physicsBody.categoryBitMask = monsterCategory;
SKAction *moveRight = [SKAction moveByX:30.0 y:0 duration:1.0];
SKAction *moveLeft = [SKAction moveByX:-30.0 y:0 duration:0.5];
SKAction *pulseMovement = [SKAction sequence:#[moveRight, moveLeft]];
[monster runAction:[SKAction repeatActionForever:pulseMovement]];
[self.world addChild:monster];
self.currentMonsterX += (arc4random() % 400) + 100;
}
the position of monster in the debugging is OK "about 50.0", but when it placed to the other SKNode *World which contains all other contents like clouds, stars and hero, the monster.position.y gets a negative value which moves it way below "about -9000"!!?
Any help is appreciated.
My bad ..
I had a little logic mistake in the order of creation .. Here is what I was doing, just in case someone else stuck with the same mistake.
The problem was in the code line:
monster.position = CGPointMake(self.currentMonsterX, ground.position.y + monster.frame.size.height);
Precisely at "ground.position.y" since the node "ground" was not yet created and hence the monsters were falling and thus the -9000 or lower.
Sorry about that.
I am having trouble getting collision detection to work with my two objects. Here is my current code:
_firstPosition = CGPointMake(self.frame.size.width * 0.817f, self.frame.size.height * .40f);
_squirrelSprite = [SKSpriteNode spriteNodeWithImageNamed:#"squirrel"];
_squirrelSprite.position = _firstPosition;
_atFirstPosition = YES;
[self addChild:_squirrelSprite];
SKAction *wait = [SKAction waitForDuration:3.0];
SKAction *createSpriteBlock = [SKAction runBlock:^{
SKSpriteNode *lightnut = [SKSpriteNode spriteNodeWithImageNamed:#"lightnut.png"];
BOOL heads = arc4random_uniform(100) < 50;
lightnut.position = (heads)? CGPointMake(257,600) : CGPointMake(50,600);
[self addChild: lightnut];
SKAction *moveNodeUp = [SKAction moveByX:0.0 y:-700.0 duration:1.3];
[lightnut runAction: moveNodeUp];
}];
SKAction *waitThenRunBlock = [SKAction sequence:#[wait,createSpriteBlock]];
[self runAction:[SKAction repeatActionForever:waitThenRunBlock]];
I was following this answer at first: Sprite Kit Collision Detection but when I set the physics body to my squirrel and nut they would just fall. Is there any way to not mess with the physics (I'm happy with how everything currently works in the app) and just make it so that when one object touches the other the game will end? Is there a way to just set a radius around a sprite? Thank you for any help or information that can be provided.
Physics bodies are required for collision detection. If you don't also want gravity, either set the physics world's gravity to zero or turn off the affectedByGravity property on each physics body.
You can use the Pythagorean theorem to determine the distance between two sprite nodes. Here's an example of how to do that:
CGFloat dx = sprite1.position.x - sprite2.position.x;
CGFloat dy = sprite1.position.y - sprite2.position.y;
CGFloat distance = sqrt(dx*dx+dy*dy);
// Check if the two nodes are close
if (distance <= kMaxDistance) {
// Do something
}
How do I make an object grow in one direction is SpriteKit?
Ultimately, I would like to have a node be inserted where the user touches and have each end of a line scale until it hits another object in the same category or hits the edge of the screen. I understand how to have it scale on an axis and I can then cancel the scaling after one collision is detected but I want both directions to scale independently until a collision is made for each. I am new to SpriteKit so my searches may be using the wrong terms but I have not been able to find anything relevant.
Code to add a "wall" where the user touches
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch * touch in touches) {
CGPoint location = [touch locationInNode:self];
[self addWallAtLocation:location];
}
}
-(void)addWallAtLocation:(CGPoint)location{
int odd_or_even = arc4random() % 2;
SKSpriteNode * wall = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:CGSizeMake(2, 2)];
wall.position = location;
wall.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(2, 2)];
wall.physicsBody.dynamic = NO;
wall.physicsBody.categoryBitMask = wallCategory;
wall.physicsBody.contactTestBitMask = ballCategory;
[self addChild:wall];
SKAction * grow;
if (odd_or_even == 1) {
grow = [SKAction scaleYTo:300 duration:2];
} else {
grow = [SKAction scaleXTo:300 duration:2];
}
[wall runAction:grow];
}
How I want it to work:
Touch 1 makes the horizontal line from a point which shoots out in both directions on the x-axis until both ends hit the edge of the screen. Touch 2 would then create a point which shoots out in both directions on the y-axis until the lower end hits the wall created by Touch 1 and the upper end hits the edge of the screen. The lines (scaled points) are generated where the user touches. So I do not want the part of the vertical line that is highlighted by the red box to be there.
Scaling in one direction is just a solution I see to this problem, if there is a better way of going about it I will certainly try that other solution.
EDIT FOR UPDATED QUESTION:
It sounds like you might be asking "how to scale a thing in a single axis, but then stop the scaling of that single axis in one direction only when it collides with another thing"? There isn't a simple way to do that just using actions, but you you could do some slight of hand by manipulating the anchor point of a sprite. Or, probably more appropriately, you should use 2 sprites per touch, with one action sending one north, and another action sending the other one south. Maybe like this:
-(void)someMethod{
SKSpriteNode *touch2TopPart = [SKSpriteNode node];
SKSpriteNode *touch2BottomPart = [SKSpriteNode node];
touch2TopPart.anchorPoint = CGPointMake(.5, 0);
touch2BottomPart.anchorPoint = CGPointMake(.5, 1);
touch2TopPart.name = #"touch2TopPart";
touch2BottomPart.name = #"touch2BottomPart";
SKAction *sendTopPartUp = [SKAction scaleYTo:100 duration:2];
SKAction *sendBottomPartDown = [SKAction scaleYTo:100 duration:2];
[touch2TopPart runAction:sendTopPartUp withKey:#"sendTopPartUp"];
[touch2BottomPart runAction:sendBottomPartDown withKey:#"sendBottomPartDown"];
}
-(void)whateverTheCollisonMethodIsCalledBetweenThingAandThingB
{
SKSpriteNode *somePartMovingInASingleDirection = thingAOrWhatever;
[somePartMovingInASingleDirection removeAllActions];
}
---ORIGINAL ANSWER
I'm not exactly sure what you're asking, but you already have the actions for scaling in just X or Y, so here's the simple usage for removing one of those actions after physics is simulated. You might be looking for how to detect a specific collision between two entities, but I can't be sure.
-(void)someMethod{
SKSpriteNode *thisSprite = [SKSpriteNode node];
thisSprite.name = #"spriteRunningActions";
SKAction *growX = [SKAction scaleXTo:100 duration:2];
SKAction *growY = [SKAction scaleYTo:100 duration:2];
[thisSprite runAction:growX withKey:#"actionCurrentlyScalingX"];
[thisSprite runAction:growY withKey:#"actionCurrentlyScalingY"];
}
-(void)didSimulatePhysics
{
SKSpriteNode *thatSprite = (SKSpriteNode*)[self childNodeWithName:#"spriteRunningActions"];
//stop a given action
[thatSprite removeActionForKey:#"actionCurrentScalingX"];
[thatSprite removeActionForKey:#"actionCurrentScalingY"];
}
Setting the anchor point like below made the "lines" scale in one direction.
wall.anchorPoint = CGPointMake(0, 0);
I have two nodes in my scene. The first node is a ball at the top of my view. The second node is a rectangle at the bottom of my view to stop the ball from dropping out of view. I am wanting this ball to have NO bounciness at all. I am wanting it to just completely stop when it hits the rectangle. I don't know what I am doing wrong. Any help would be greatly appreciated!
SKSpriteNode *rectangle = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:CGSizeMake(self.frame.size.width, 50)];
bottom.position = CGPointMake(self.frame.size.width / 2, 0);
bottom.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(rectangle, bottom.size.height)];
bottom.physicsBody.dynamic = NO;
[self addChild:bottom];
SKSpriteNode *ball = [SKSpriteNode spriteNodeWithImageNamed:#"ball"];
ball.position = CGPointMake(node.position.x, node.position.y);
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius: 5];
ball.physicsBody.restitution = 0;
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 0, 0);
CGPathAddLineToPoint(path, NULL, [self randomX], [self randomY]);
SKAction *move = [SKAction followPath:path asOffset:YES orientToPath:NO duration:3.0];
[ball runAction:move];
[self addChild: ball];
Why is is still bouncing off the rectangle?
In my experience, you should in more cases than not be working with frameworks like they expects you to, hacks just end up with weird quirks like the one you encountered. In this case, as #LearnCocos2D said, you should probably be using [ball.physicsBody applyImpulse:(CGVEctor)] if you need the ball to move along a specific path but still maintain a dynamic physicsBody. If you then set the restitution to 0, it will not bounce. I promise.
Read up on vectors and vector maths if you need the ball to follow a specific path (it's knowledge that is good to have anyways when working with physics). I've found this to be a good resource for that. It's for openFrameworks, hence C++, but the concept remains the same.
And if you still really need to use a CGPath, then I think you should tell us a bit more specific about what you need to accomplish. There might be a better solution, or maybe we should file a bug to Apple. Sprite-Kit is still fairly young, I have myself encounter problems I believe to be bugs.
The documentation indicates that the value of the "restitution" property of SKPHysicsBody is "how much energy the physics body loses when it bounces off another object." (here). By setting the restitution to zero, you make the ball as bouncy as possible - it loses 0 energy. If you set the restitution to 1 - loses all energy - it should stop.
I am trying to programmatically build a pendulum using iOS SpriteKit using the built in physics.
Currently, I have the pendulum pivot, a weight, and a limited joint that allows the weight to swing... But, I have no idea on how to code a line (rod) between the pivot and weight.
I assume that drawing a line with SKShapeNode would be a start...?
-(void)setupPendulum
{
pivot = [SKSpriteNode spriteNodeWithImageNamed:#"pivot.png"];
pivot.position = CGPointMake(self.size.width / 2, self.size.height / 2);
pivot.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:1.0];
pivot.physicsBody.dynamic = NO;
pivot.physicsBody.affectedByGravity = NO;
pivot.xScale = 0.25;
pivot.yScale = 0.25;
[self addChild:pivot];
weight = [SKSpriteNode spriteNodeWithImageNamed:#"weight.png"];
weight.position = CGPointMake(150, 512);
weight.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:100.0];
weight.physicsBody.dynamic = YES;
weight.physicsBody.affectedByGravity = YES;
weight.physicsBody.mass = 0.5;
weight.xScale = 0.75;
weight.yScale = 0.75;
[self addChild:weight];
SKPhysicsBody *ab = pivot.physicsBody;
SKPhysicsBody *bb = weight.physicsBody;
CGPoint ap = pivot.position;
CGPoint bp = weight.position;
SKPhysicsJointLimit *joints = [SKPhysicsJointLimit jointWithBodyA:ab
bodyB:bb
anchorA:ap
anchorB:bp];
[self.physicsWorld addJoint:joints];
}
My project required the SKPhysicsJointLimit instead of the pin so after a lot of trial and error I found the following solution. The line is removed and drawn in didSimulatePhysics. The line connects the "satelliteShip" as it orbits around the "motherShip". I'm new to all this so I appreciate any feedback on this approach.
Setup variable first:
SKShapeNode *_lineNode;
Now draw the line in didSimulatePhysics:
- (void)didSimulatePhysics {
if (_lineNode){
[_lineNode removeFromParent];
}
SKNode *satelliteShip = [self childNodeWithName:kSatelliteShipName];
CGMutablePathRef pathToDraw;
pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, motherShip.position.x, motherShip.position.y);
CGPathAddLineToPoint(pathToDraw, NULL, satelliteShip.position.x, satelliteShip.position.y);
CGPathCloseSubpath(pathToDraw);
_lineNode = [SKShapeNode node];
_lineNode.path = pathToDraw;
CGPathRelease(pathToDraw);
_lineNode.strokeColor = [SKColor grayColor];
[self addChild:_lineNode];
}
The correct direction was to look at SKPhysicsJointPin, which can use a SKShapeNode or SKSpriteNode (etc.).
https://developer.apple.com/library/iOS/documentation/SpriteKit/Reference/SKPhysicsJointPin_Ref/Reference/Reference.html
Note About my question and the comments on it:
Since there isn't very much documentation (now) on SpriteKit beyond the (small book sized) reference by Apple and a grip of single-class-app examples/tutorials online -- most peoples responses are to, "just look it up + Cocos2d in google" and just re-do that. That's nice en all, but I'm not a copy-paste kinda of person. I like to learn the best way which is sustainable and reusable. This is why I asked what the best way to do it vs. posting busted Cocos2d ported copy-paste code and letting someone fill in the blanks like I see happen too often.