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
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.
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;
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.
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.
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]]]];
//...
}