Spritekit - Spawn waves of monsters - sprite-kit

I'm making a simple racing game in which the monsters are spawned (randomly) from 3 out of the 5 lanes in the portrait mode.
-(void)addEnemy
{
SKSpriteNode *Enemy;
Enemy = [SKSpriteNode spriteNodeWithImageNamed:#"Enemy1"];
[Enemy setScale:.65];
Enemy.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:Enemy.size];
Enemy.physicsBody.categoryBitMask = obstacleCategory;
Enemy.physicsBody.dynamic = YES;
Enemy.zPosition = 2;
Enemy.physicsBody.contactTestBitMask = TurtleCategory;
Enemy.physicsBody.collisionBitMask = 0;
Enemy.physicsBody.usesPreciseCollisionDetection = YES;
Enemy.name = #"Enemy";
//selecting random y position for Enemy
int r = (arc4random() % 5) ;
Enemy.position = CGPointMake(48+r*56,self.frame.size.height);
[self addChild:Enemy];
SKAction *actionMove =
[SKAction moveTo:CGPointMake(Enemy.position.x,-80)
duration:1.43];
[Enemy runAction:actionMove];
SKSpriteNode *Enemy2;
Enemy2 = [SKSpriteNode spriteNodeWithImageNamed:#"Enemy1"];
[Enemy2 setScale:.65];
//Adding SpriteKit physicsBody for collision detection
Enemy2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:Enemy.size];
Enemy2.physicsBody.categoryBitMask = obstacleCategory;
Enemy2.physicsBody.dynamic = YES;
Enemy2.zPosition = 2;
Enemy2.physicsBody.contactTestBitMask = TurtleCategory;
Enemy2.physicsBody.collisionBitMask = 0;
Enemy2.physicsBody.usesPreciseCollisionDetection = YES;
Enemy2.name = #"Enemy2";
//selecting random y position for Enemy
int r2 = (arc4random() % 5) ;
Enemy2.position = CGPointMake(48+r2*56,self.frame.size.height);
[self addChild:Enemy2];
SKAction *actionMove2 =
[SKAction moveTo:CGPointMake(Enemy2.position.x,-80)
duration:1.43];
[Enemy2 runAction:actionMove2];
SKSpriteNode *Enemy1;
Enemy1 = [SKSpriteNode spriteNodeWithImageNamed:#"boss1"];
[Enemy1 setScale:.65];
//Adding SpriteKit physicsBody for collision detection
Enemy1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:Enemy1.size];
Enemy1.physicsBody.categoryBitMask = obstacleCategory;
Enemy1.physicsBody.dynamic = YES;
Enemy1.zPosition =2;
Enemy1.physicsBody.contactTestBitMask = TurtleCategory;
Enemy1.physicsBody.collisionBitMask = 0;
Enemy1.physicsBody.usesPreciseCollisionDetection = YES;
Enemy1.name = #"Enemy1";
int r1 = (arc4random() % 5) ;
Enemy1.position = CGPointMake(48+r1*56,self.frame.size.height);
[self addChild:Enemy1];
SKAction *actionMove1 =
[SKAction moveTo:CGPointMake(Enemy1.position.x,-80)
duration:1.43];
[Enemy1 runAction:actionMove1];
However, one of problem of this code is that monsters are sometimes spawned on top of each other. Is there a better way to do this?
Thanks

- (CGFloat)randomValueBetween:(CGFloat)low andValue:(CGFloat)high {
return (((CGFloat) arc4random() / 0xFFFFFFFFu) * (high - low)) + low;
}
I found this piece of code from a raywenderlich tutorial I think.
What u can do is call,
CGFloat r1 = [self randomValueBetween:0 andValue:3];
CGFloat r2 = [self randomValueBetween:3 andValue:6]; etc
that might give you different values that u can use as offset.

Related

SKSpriteNode vibrating when physics body is attached and impulse is applied

I've noticed something very odd with my subclassed SKSpriteNode with physicsBody attached and responding to impulses.
It has a SKPhysicsBody attached, but when applying impulses to the sprite it vibrates during movement. It's highly annoying and looks awful.
What could be causing this?
Here's the setup code:
self.texture = [[SKTextureAtlas atlasNamed:#"karl"]textureNamed:#"karlidle-1"];
self.size = CGSizeMake(69.0, 93.0);
self.userInteractionEnabled = NO;
self.name = #"karl";
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(69.0, 93.0)];
self.physicsBody.dynamic = YES;
self.physicsBody.allowsRotation = NO;
self.physicsBody.affectedByGravity = YES;
self.physicsBody.usesPreciseCollisionDetection = YES;
self.physicsBody.mass = 1.0;
self.physicsBody.linearDamping = 0.0;
self.physicsBody.angularDamping = 0.0;
self.physicsBody.restitution = 0.3;
self.physicsBody.categoryBitMask = karlCategory;
self.physicsBody.collisionBitMask = obstaclesCategory|floorCategory|wallCategory;
self.physicsBody.contactTestBitMask = obstaclesCategory|floorCategory|wallCategory;
and applying the impulse:
if (self.physicsBody.velocity.dy >= kKarlMaxVelocity) {
self.physicsBody.velocity = CGVectorMake(0, kKarlMaxVelocity);
}
else if (self.physicsBody.velocity.dy <= kKarlLowVelocityBoostThreshold){
self.physicsBody.velocity = CGVectorMake(0, kKarlVelocityImpulseIncrement*kKarlVelocityBoost);
}
else{
[self.physicsBody applyImpulse:CGVectorMake(0, kKarlVelocityImpulseIncrement)];
//[self.physicsBody applyForce:CGVectorMake(0, 3000) atPoint:self.frame.origin];
//self.physicsBody.velocity = CGVectorMake([self returnNormalisedXVelocity], self.physicsBody.velocity.dy);
}
One thing to note is I clamp the velocity so it doesn't go nuts, but turning this off doesn't seem to fix the issue.

Stop node rotation in Sprite Kit

I can't figure out how to stop node from rotating.
Why allowsRotation isn't disabling it?
Here's how I describe my node:
SKSpriteNode *badguy = [SKSpriteNode spriteNodeWithTexture:[self.spriteAtlas textureNamed:#"test"]];
badguy.texture.filteringMode = SKTextureFilteringNearest;
badguy.physicsBody.angularVelocity = 0;
badguy.physicsBody.allowsRotation = NO;
badguy.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(32, 32)];
badguy.physicsBody.velocity = CGVectorMake(0, 0);
badguy.physicsBody.categoryBitMask = CollisionTypeEnemy;
badguy.physicsBody.contactTestBitMask = CollisionTypePlayer | CollisionTypeWall | CollisionTypeEnemy;
badguy.physicsBody.collisionBitMask = CollisionTypeWall | CollisionTypePlayer | CollisionTypeEnemy;
badguy.physicsBody.mass = 10000;
badguy.physicsBody.restitution = 0;
badguy.physicsBody.dynamic = YES;
badguy.zPosition = 10;
On Update I call this method:
-(void)enemiesFollow
{
[self.world enumerateChildNodesWithName:#"badGuy" usingBlock:^(SKNode * _Nonnull badGuyNode, BOOL * _Nonnull stop) {
if((SDistanceBetweenPoints(self.player.position, badGuyNode.position) < 100)&&
(SDistanceBetweenPoints(self.player.position, badGuyNode.position) > 32))
{
SKAction * actionMove = [SKAction moveTo:self.player.position duration:2.0];
[badGuyNode runAction:actionMove];
}else{
[badGuyNode removeAllActions];
}
}];
}
Thanks to Skyler Lauren's suggestion, I am pretty much sure that the problem is in these lines:
SKSpriteNode *badguy = [SKSpriteNode spriteNodeWithTexture:[self.spriteAtlas textureNamed:#"test"]];
badguy.texture.filteringMode = SKTextureFilteringNearest;
badguy.physicsBody.angularVelocity = 0;
badguy.physicsBody.allowsRotation = NO;
badguy.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(32, 32)];
The point is that you can't set properties of a physics body before you create an actual body. So, just move those lines after you create a physics body and you will be good, like this:
SKSpriteNode *badguy = [SKSpriteNode spriteNodeWithColor:[SKColor greenColor] size:CGSizeMake(32,32)];
badguy.texture.filteringMode = SKTextureFilteringNearest;
badguy.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(32, 32)];
badguy.physicsBody.angularVelocity = 0;
badguy.physicsBody.allowsRotation = YES;

Rotate sprite with path

Hi i have a sprite following a path that is a bezier curve and id like the sprite to rotate to the direction it is currently moving, i can tell it to follow the path by setting orientToPath to YES in
SKaction *enemyCurve = [SKAction followpath:cgpath asOffset:NO orientToPath:YES duration:5];
but this just results in the sprite facing the final point of movement. Any suggestions on how i can make it rotate to the current direction its traveling in?
I have included the code i use for the path below. Thank you for any help and suggestions.
-(void)AddEnemy:(MotionType *)motion :(CGPoint*)Start :(CGPoint*)Finish :(EnemyType)enemyType{
double curTime = CACurrentMediaTime();
if (enemyType == One) {
if(curTime > _nextAsteroidspawn)
{
float randSecs = [self randomValueBetween:0.20 andValue:1.0];
_nextAsteroidspawn = randSecs + curTime;
float randY = [self randomValueBetween:0.0 andValue:self.frame.size.height];
float randDuration = [self randomValueBetween:2.0 andValue:10.0];
SKSpriteNode *asteroid = [_Asteroids objectAtIndex:_nextAsteroid];
_nextAsteroid++;
if(_nextAsteroid >= _Asteroids.count){
_nextAsteroid = 0;
}
[asteroid removeAllActions];
asteroid.position = CGPointMake(self.frame.size.width+asteroid.size.width/2, 50);
asteroid.hidden = NO;
CGMutablePathRef cgpath = CGPathCreateMutable();
CGPoint startingPoint = CGPointMake(self.frame.size.width+asteroid.size.width/2 , 50);
CGPoint controlPoint1 = CGPointMake(160, 300);
CGPoint controlPoint2 = CGPointMake(200, 600);
CGPoint endingPoint = CGPointMake(0, self.frame.size.width - self.frame.size.width - asteroid.size.width / 2);
CGPathMoveToPoint(cgpath, NULL, startingPoint.x, startingPoint.y);
CGPathAddCurveToPoint(cgpath, NULL, controlPoint1.x, controlPoint1.y, controlPoint2.x
, controlPoint2.y, endingPoint.x
, endingPoint.y);
SKAction *enemyCurve = [SKAction followPath:cgpath asOffset:NO orientToPath:YES duration:5];
CGPoint location = CGPointMake(-self.frame.size.width-asteroid.size.width, randY);
SKAction *moveAction = [SKAction moveTo:location duration:randDuration];
SKAction *doneAction = [SKAction runBlock:(dispatch_block_t)^(){
asteroid.hidden = YES;
}];
SKAction *moveAsteroidActionWithDone = [SKAction sequence:#[enemyCurve,moveAction, doneAction]];
[asteroid runAction:moveAsteroidActionWithDone withKey:#"asteroidMoving"];
CGPathRelease(cgpath);
//[asteroid runAction: [SKAction repeatActionForever:[SKAction animateWithTextures:_Asteroids timePerFrame:0.1f]]];
}
The code that creates the enemy node can be seen bellow, i do not apply any rotations to the node.
In initWithSize:
_Asteroids = [[NSMutableArray alloc] initWithCapacity:knumAsteroids];
for(int i = 0; i < knumAsteroids; i++)
{
SKSpriteNode *asteroid = [SKSpriteNode spriteNodeWithImageNamed:#"enemyBasic1"];
asteroid.hidden = YES;
[_Asteroids addObject:asteroid];
[self addChild:asteroid];
}
In Update:
[self AddEnemy:BezierCurve :NULL :NULL:One];
The method in update just links to the code posted at the top which determines the path the enemy will take i have also updated it to include the entire method.

Prevent SKSpriteNode from bouncing off other SKSpriteNode

I have a SKSpriteNode that collides and bounces off another SKSpriteNode. What I want is to be able to detect the collision, but not have it bounce off. I want it to bounce off other nodes, but not this one. Is that possible?
self.physicsWorld.contactDelegate = self;
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsWorld.gravity = CGVectorMake(0.0f, 0.0f);
self.zone = [SKSpriteNode spriteNodeWithImageNamed:#"Oval.png"];
self.zone.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame)); self.zone.xScale = 0.3;
self.zone.yScale = 0.3;
self.zone.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.zone.frame.size.width/2];
self.zone.physicsBody.restitution = 0.0f;
self.zone.physicsBody.density = 0;
self.zone.physicsBody.friction = 0.4f;
self.zone.physicsBody.categoryBitMask = zoneCategory;
self.zone.physicsBody.dynamic = NO;
self.ball = [SKSpriteNode spriteNodeWithImageNamed:#"Ball.png"];
self.ball.position = CGPointMake(80,0);
self.ball.name = #"BallNode";//how the node is identified later
self.ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.ball.frame.size.width/2];
self.ball.physicsBody.restitution = 0.1f;
self.ball.physicsBody.friction = 0.4f;
self.ball.physicsBody.categoryBitMask = ballCategory;
self.ball.physicsBody.dynamic = NO;
I don't want ball and zone to bounce. Any ideas?
set collisionBitMask and contactTestBitMask of those two bodies
like:
self.zone.collisionBitMask = 0xFFFFFFFF & (~ballCategory);
self.ball.collisionBitMask = 0xFFFFFFFF & (~zoneCategory);
self.zone.contactTestBitMask = ballCategory;
then when contact occurs expect call of didBeginContact: in your SKPhysicsContactDelegate.
There will be no collsion.

How to add to ammo if item collected sprite kit

i have a game where the guy runs around and collects stuff. So I want the score keeper to update and increase if he collects an item and decrease if he uses the item. For example, life item. so here is the code I got so far for labels but that works with text score not integers. any ideas? thanks
- (void)setupUI
{
int barHeight = 45;
CGSize backgroundSize = CGSizeMake(self.size.width, barHeight);
SKColor *backgroundColor = [SKColor colorWithWhite:10 alpha:50];
SKSpriteNode *hudBarBackground = [SKSpriteNode spriteNodeWithColor:backgroundColor size:backgroundSize];
hudBarBackground.position = CGPointMake(0, self.size.height - barHeight);
hudBarBackground.anchorPoint = CGPointZero;
[_hudLayerNode addChild:hudBarBackground];
// 1
SKLabelNode *scoreLabel = [SKLabelNode labelNodeWithFontNamed:#"AvenirNextCondensed-HeavyItalic"];
// 2
scoreLabel.fontSize = 20.0;
scoreLabel.text = #"Score: 0";
scoreLabel.name = #"scoreLabel";
scoreLabel.fontColor = [SKColor orangeColor];
// 3
scoreLabel.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter;
// 4
scoreLabel.position = CGPointMake(self.size.width - self.size.width / 4, self.size.height - scoreLabel.frame.size.height + 3);
// 5
[_hudLayerNode addChild:scoreLabel];
_scoreFlashAction = [SKAction sequence: #[[SKAction scaleTo:1.5 duration:0.1],
[SKAction scaleTo:1.0 duration:0.1]]];
[scoreLabel runAction: [SKAction repeatAction:_scoreFlashAction count:10]];
}
double score; //property
-(void)setupUI {
//...
SKAction *tempAction = [SKAction runBlock:^{
scoreLabel.text = [NSString stringWithFormat:#"Score: 3.0f", self.score];
}];
SKAction *waitAction = [SKAction waitForDuration:0.2];
[scoreLabel runAction:[SKAction repeatActionForever:[SKAction sequence:#[tempAction,waitAction]]]];
//...
}