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
}
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 don't really know how to explain what i'm after, so I drew some (very) artistic diagrams to help convey the idea. I'll also try and explain it the best I can.
I'm essentially trying to 'shoot' bullets/lasers/whatever from a circle in the center of the screen, and for it to repeat this at a rather rapid rate. Here are two pictures which kind of show what i'm trying to achieve: (Don't have enough reputation to post them here.
(1) http://i.imgur.com/WpZlTQ7.png
This is kind of where I want the bullets to shoot from, and how many I'd like.
(2) http://i.imgur.com/psdIjZG.png
This is pretty much the end result, I'd like them to repeatedly fire and make the screen kind of look like this.
Can anyone refer me to what I should be looking at in order to achieve this?
When dealing with circles, it's usually easier to use polar coordinates. In this case, each direction can be represented with a magnitude and an angle, where the magnitude is the amount of the force/impulse to apply to the bullet and angle is the direction to shoot the bullet.
The basic steps are
Determine the number of directions
Determine the angle increment by dividing 2*PI (360 degrees) by the number of directions
Start at angle = 0
Shoot bullet in the direction specified by angle
Convert the angle and magnitude to cartesian coordinates to form a vector
Apply an impulse or a force to the bullet using the vector
Increment angle by angle increment
Here's an example of how to do that in Obj-C:
#implementation GameScene {
CGFloat angle;
SKTexture *texture;
CGFloat magnitude;
CGFloat angleIncr;
}
-(void)didMoveToView:(SKView *)view {
/* Setup your scene here */
long numAngles = 15;
magnitude = 1;
angleIncr = 2 * M_PI / numAngles;
angle = 0;
texture = [SKTexture textureWithImageNamed:#"Spaceship"];
SKAction *shootBullet = [SKAction runBlock:^{
SKSpriteNode *bullet = [SKSpriteNode spriteNodeWithTexture:texture];
bullet.size = CGSizeMake(8, 8);
bullet.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:bullet.size.width/2];
bullet.physicsBody.affectedByGravity = NO;
bullet.position = self.view.center;
bullet.zRotation = angle-M_PI_2;
[self addChild:bullet];
CGFloat dx = magnitude * cos(angle);
CGFloat dy = magnitude * sin(angle);
CGVector vector = CGVectorMake(dx,dy);
[bullet.physicsBody applyImpulse:vector];
angle = fmod(angle+angleIncr,2*M_PI);
}];
SKAction *wait = [SKAction waitForDuration:0.25];
SKAction *shootBullets = [SKAction sequence:#[shootBullet, wait]];
[self runAction:[SKAction repeatActionForever:shootBullets]];
}
From what I understand, you want this sprite to shoot a bunch of projectiles every X seconds.
SKAction *ShootProjectiles = [SKAction runBlock:^{
//Create Projectile1
projectile1.physicsBody.applyImpulse(CGVectorMake(1, 0)); //Shoot directly right
//Create Projectile2
projectile2.physicsBody.applyImpulse(CGVectorMake(1, 1)); //Shoot diagnally Up and to the right
//Follow this pattern to create all projectiles desired to be shot in one burst
}];
SKAction *delayBetweenShots = [SKAction scaleBy:0 duration:5];
SKAction* ShootSequence= [SKAction repeatActionForever:[SKAction sequence:#[ShootProjectiles, delayBetweenShots]]];
[self runAction: ShootSequnce];
What this does is create as many projectiles as you desire and fire them in the direction of the vectors you define. It then waits 5 seconds (the scaleBy:0 action does nothing except delay) and then repeats over and over again until you remove the action.
I recently came across an issue where I would increase the size of a node, but the physics body would remain the same size. I tried to look up solutions to this with no success. How can I make the body scale with the size of the node?
CGPoint location = CGPointMake(randX, -self.frame.size.height - expander.size.height);
SKAction *moveAction = [SKAction moveTo:location duration:randDuration];
SKAction *expandAction = [SKAction resizeToWidth:(expander.size.width * 1.4) height:(expander.size.width * 1.4) duration:1.0];
SKAction *collapseAction = [SKAction resizeToWidth:(expander.size.width) height: (expander.size.height) duration:1.0];
SKAction *doneAction = [SKAction runBlock:(dispatch_block_t)^() {
expander.hidden = YES;
}];
SKAction *expandCollapseAction = [SKAction repeatActionForever:[SKAction sequence:#[expandAction, collapseAction]]];
SKAction *moveExpandAction = [SKAction group:#[moveAction, expandCollapseAction]];
SKAction *moveExpanderActionWithDone = [SKAction sequence: #[moveExpandAction, doneAction ]];
[expander runAction:moveExpanderActionWithDone withKey: #"expanderMoving"];
I thought this won't work, but it looks like that physics body is scaled after all. Here is the code which can reproduce physics body scaling on iOS8 :
- (void)didMoveToView:(SKView *)view {
/* Setup your scene here */
SKSpriteNode *s = [SKSpriteNode spriteNodeWithColor:[SKColor yellowColor] size:CGSizeMake(100,100)];
s.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMinY(self.frame)+100);
s.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:s.size];
s.physicsBody.dynamic = YES;
s.physicsBody.affectedByGravity = NO;
[self addChild:s];
[s runAction:[SKAction scaleTo:0.3f duration:5]];
[s.physicsBody applyImpulse:CGVectorMake(0,30)];
}
If I recall correctly , this wasn't worked earlier, but I just tried it and it looks like it work. Still, this is not fully tested and I am not sure if this will mess up the physics simulation. Contact detection will probably work though. I just wanted to show that actual physics bodies are scaled when sprite is scaled . Here is the result:
From the gif above it can be seen that physics body is scaled along with sprite (without re-creating another different sized physics body). The blue bounding box around the sprite is visual representation of sprite's physics body (enabled in view controller with skView.showsPhysics = YES);
this is strange you can do it add physics body before adding it to the SKNode or SKScene for e.g.
SKTexture *texture=[_gameTexture textureNamed:#"mysprite"];
SKSpriteNode *bubble=[SKSpriteNode spriteNodeWithTexture:texture];
bubble.anchorPoint=CGPointMake(0.5,0.5);
//_bubble.name=[#"bubble" stringByAppendingFormat:#"%d ",index];
bubble.name=newcolor;
// _bubble.userInteractionEnabled=YES;
_bubble.position=CGPointMake(newPosition.x, newPosition.y);
//
bubble.physicsBody=[SKPhysicsBody bodyWithCircleOfRadius:60];
bubble.physicsBody.dynamic=NO;
bubble.physicsBody.linearDamping=0;
bubble.physicsBody.restitution=0;
bubble.physicsBody.density=1;
bubble.physicsBody.allowsRotation=NO;
bubble.physicsBody.affectedByGravity=NO;
//_bubble.physicsBody.velocity=CGVectorMake([self getRandomNumberBetween:-10 to:7], [self getRandomNumberBetween:-10 to:7]);
// _bubble.physicsBody.usesPreciseCollisionDetection=YES;
bubble.physicsBody.categoryBitMask=ballCategory;
bubble.physicsBody.collisionBitMask=ballCategory | pathCategory ;
bubble.physicsBody.contactTestBitMask=ballCategory | pathCategory ;
[self addChild:_bubble];
bubble.xScale=_bubble.yScale=2;
bubble.alpha=0.5;
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'm working in IOS/spritekit where I have a sprite that rotates. The rotation works fine, but in addition I want that the sprite jumps. Therefore I added dynamic and restitution to my physicsBody of my sprite:
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"bigball"];
sprite.position = location;
sprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:sprite.size.width/2];
sprite.physicsBody.dynamic = YES;
sprite.physicsBody.restitution = 0.7;
SKAction *action = [SKAction rotateByAngle:M_PI duration:1];
[sprite runAction:[SKAction repeatActionForever:action]];
This also works, but the sprite only jumps 2 times and then comes to a standstill, which is logical because I've added the dynamic, but I want that the sprite jumps infinitely.
Can anyone explain how I can do it?
Thanks in advance
Try this:
sprite.physicsBody.restitution = 1.0;
If the restitution property is at 1.0, its bounciness will be at the maximum level - the physics body will not lose any energy while bouncing, so it should bounce indefinitely.