SKAction building a timer/score counter - ios

so I am trying to make a game with a timer and a score counter in the bottom right of the screen. but it is not appearing and I am not sure as to why
in - (void)didMoveToView:(SKView *)view
I have the code
SKAction *wait = [SKAction waitForDuration:1.0f];
SKAction *sequence = [SKAction sequence:#[[SKAction performSelector:#selector(timer) onTarget:self], wait]];
SKAction *repeat = [SKAction repeatActionForever:sequence];
[self runAction:repeat];
and timer looks like
-(void)timer
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
float multi = [defaults floatForKey:#"multi"];
if(!_time)
{
_time=0;
}
else if(_time == 30)
{
multi = 1.5;
}
else if(_time == 60)
{
multi = 2;
}
else if(_time == 90)
{
multi = 2.5;
}
else if(_time == 120)
{
multi = 3.0;
}
else if(_time == 150)
{
multi = 3.5;
}
else if(_time == 180)
{
multi = 4.0;
}
else if(_time == 210)
{
multi = 4.5;
}
[defaults setFloat:multi forKey:#"multi"];
_time++;
_score = multi * (float)_time;
SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:#"Chalkduster"];
myLabel.fontSize = 30;
myLabel.position = CGPointMake(150,850);
myLabel.text = [NSString stringWithFormat:#"Time %d Score %f",_time,_score];
}
_timer and _score are properties I have created for this scene
any help would be awesome thank you

You forgot to add the label to the node graph. For example:
[self addChild:myLabel];

Related

How to rotate by using accelerometer? ios

I want to rotate my character by tilting the phone left and right, but it only rotate once when DidMoveToView is called. What do I need to change in my code?
self.manager = [[CMMotionManager alloc] init];
if ([self.manager isAccelerometerAvailable] == YES) {
[self.manager startAccelerometerUpdatesToQueue:[[NSOperationQueue alloc] init]
withHandler:^(CMAccelerometerData *data, NSError *error)
{
float destX = 0.0, destY = 0.0;
float currentX = _character.position.x;
float currentY = _character.position.y;
BOOL shouldMoveToRight = NO;
BOOL shouldMoveToLeft = NO;
if(data.acceleration.y < -0.25) { // tilting the device to the right
destX = currentX + (data.acceleration.x * kPlayerSpeed);
destY = currentY;
shouldMoveToRight = YES;
shouldMoveToLeft = NO;
} else if (data.acceleration.y > 0.25) { // tilting the device to the left
destX = currentX + (data.acceleration.x * kPlayerSpeed);
destY = currentY;
shouldMoveToRight = NO;
shouldMoveToLeft = YES;
}
if(shouldMoveToRight) {
SKAction *moveAction = [SKAction moveTo:CGPointMake(destX, destY) duration:1];
[_character runAction:moveAction];
SKAction *rotateAction = [SKAction rotateToAngle:M_PI duration:1];
[_character runAction:rotateAction];
}
if (shouldMoveToLeft) {
SKAction *moveAction = [SKAction moveTo:CGPointMake(destX, destY) duration:1];
[_character runAction:moveAction];
SKAction *rotateAction = [SKAction rotateToAngle:-M_PI duration:1];
[_character runAction:rotateAction];
}
}];
}

How to detect SKSpriteNode Contact but do not react?

I have two SKSpriteNode first Hero
+(id)hero
{
NSMutableArray *walkFrames = [NSMutableArray array];
SKTextureAtlas *heroAnimatedAtlas = [SKTextureAtlas atlasNamed:#"HeroImages"];
int numImages = (int)heroAnimatedAtlas.textureNames.count;
for (int i=1; i <= numImages; i++) {
NSString *textureName = [NSString stringWithFormat:#"hero%d", i];
SKTexture *temp = [heroAnimatedAtlas textureNamed:textureName];
[walkFrames addObject:temp];
}
Hero *hero = [Hero spriteNodeWithTexture:walkFrames[0]];
hero.heroWalkingFrames = walkFrames;
hero.name =#"Hero";
hero.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:hero.size];
hero.physicsBody.categoryBitMask = heroCategory;
hero.physicsBody.categoryBitMask = obstacleCategory | groundCategory | homeCategory | ~goodiesCategory;
return hero;
}
and second is Coin
SKSpriteNode *coin = [SKSpriteNode spriteNodeWithImageNamed:#"coin"];
coin.size = CGSizeMake(10,10);
coin.position = CGPointMake(100,100);
coin.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:coin.size];
coin.physicsBody.contactTestBitMask = coinCategory;
coin.physicsBody.dynamic=NO;
coin.name = #"coin";
[self.world addChild:coin];
And I am able to get collision detection by
if([contact.bodyA.node.name isEqual: #"coin"] || [contact.bodyB.node.name isEqual: #"coin"])
{
//[self LevelComplete];
SKNode* coinNode ;
if ([contact.bodyA.node.name isEqual: #"coin"]) {
coinNode=contact.bodyA.node;
}
else{
coinNode=contact.bodyB.node;
}
[coinNode removeFromParent];
NSLog(#"Coin touched");
}
Now my problem is every time hero jump and touch the coin it will go down to ground instead to continue jump and reach the height that it should, I know I am missing something here but don't know what it is, So anyone can please show me the right direction to correct this effect .
Create an extra "nilCategory" and set the collisionBitMask of your coin..
coin.physicsBody.collisionBitMask = nilCategory;

NodesAtPoint does not find a node

I'm trying to create 10 balloons in my app. I create a random CGPoint to set my node's position and check, in case that already exist a node at this point I try again.
I don't know why, the balloons keep being placed over another balloon.
Any ideia of what I am doing wrong?
Here is my code:
-(void)gerarBaloes:(int)quantidade
{
balloons = [NSMutableArray new];
u_int32_t maxHorizontal = 900;
u_int32_t minHorizontal = 100;
u_int32_t maxVertical = 650;
u_int32_t minVertical = 100;
for (int i=1; i<= quantidade; i++) {
CGPoint posicao = CGPointMake(arc4random_uniform(maxHorizontal - minHorizontal + 1) + minHorizontal, arc4random_uniform(maxVertical - minVertical + 1) + minVertical);
while (![self validPosition:posicao]) {
posicao = CGPointMake(arc4random_uniform(maxHorizontal - minHorizontal + 1) + minHorizontal, arc4random_uniform(maxVertical - minVertical + 1) + minVertical);
}
balao* nBalao = [[balao alloc]initWithPosition:posicao];
SKLabelNode* numero = [[SKLabelNode alloc]initWithFontNamed:#"Arial Rounded MT Bold"];
numero.text = [NSString stringWithFormat:#"%d",i];
numero.zPosition = 1;
nBalao.body.zPosition = 0;
[nBalao addChild:numero];
nBalao.body.name = #"balloon";
[balloons addObject:nBalao];
[self addChild:nBalao];
}
}
And this is my validation method:
-(BOOL)validPosition:(CGPoint)position
{
NSArray* nodes = [self nodesAtPoint:position];
NSLog(#"total de nodes %d",nodes.count);
if (nodes.count > 0) {
for (SKNode* n in nodes) {
if ([n isKindOfClass:[balao class]]) {
return NO;
}
}
return YES;
}else{
return YES;
}
}
Balao.m class:
#implementation balao
-(id)initWithPosition:(CGPoint)pos
{
if (self = [super init]) {
self.position = pos;
int n = arc4random_uniform(4);
_balloonAtlas = [SKTextureAtlas atlasNamed:[NSString stringWithFormat:#"balloon%d",n]];
_explosionFrames = [NSMutableArray array];
int numImages = _balloonAtlas.textureNames.count;
for (int i=1; i<= numImages; i++){
NSString *textureName = [NSString stringWithFormat:#"balloon_%d",i];
SKTexture *temp = [_balloonAtlas textureNamed:textureName];
[_explosionFrames addObject:temp];
}
SKTexture *textInicial = [_explosionFrames[0] copy];
[_explosionFrames removeObjectAtIndex:0];
self.body = [SKSpriteNode spriteNodeWithTexture:textInicial];
[self addChild:_body];
}
return self;
}
- (void)explodeBalloon
{
SKAction* explosao = [SKAction animateWithTextures:_explosionFrames timePerFrame:0.02f resize:NO restore:YES];
[self.body runAction:explosao completion:^(void){
[self removeFromParent];
}];
[self runAction:[SKAction playSoundFileNamed:#"popSound.mp3" waitForCompletion:NO]];
}
This is how the balloons appears:
Edit:
I tried the suggested method, with CGRectCointainsPoint, but it didn't work too. :/
Checking for nodes at a given point is not enough to prevent overlap. You need to check whether the point falls inside the frame of another balloon. You can modify the function like this
-(BOOL)validPosition:(CGPoint)position
{
NSArray* nodes = [self children];
if (nodes.count > 0) {
for (SKNode* n in nodes) {
if ([n isKindOfClass:[balao class]]) {
if (CGRectContainsPoint([n getBalloonFrame], position)) // Changed line
{
return NO
}
}
}
return YES;
}
return YES;
}
CGRectContainsPoint checks whether a given rectangle contains a point.
Inside balao class define the following function. Also note the changed line in the above code.
-(void)getBalloonFrame
{
return CGRectOffset(self.body.frame, self.frame.origin.x, self.frame.origin.y)
}

Why is my SKAction not functioning properly?

Okay, so in my SpriteKit game, I have an SKAction that waits and then calls a method. I have this action repeating forever. The methods spawns sprites. When I press a button (another sprite) the game pauses, and stops the action by removing it. When either resume or restart (also sprites) is pressed the action starts again and the sprites spawn.
However, when returning from the background (after the app is left) and the pause menu method automatically gets called, when I press the resume or restart button, the action does not run for some reason. Here's my code:
In GameScene.m:
-(void)createSceneContents {
self.isPaused = NO;
self.world = [SKNode node];
[self createUI];
[self createPauseMenu];
self.spawningSpeed = 1.5;
self.enemyData = [[Enemy alloc]init];
SKAction *wait = [SKAction waitForDuration:self.spawningSpeed];
SKAction *run = [SKAction performSelector:#selector(spawningEnemy) onTarget:self];
self.spawnAction = [SKAction repeatActionForever:[SKAction sequence:#[wait,run]]];
[self.world runAction:self.spawnAction withKey:#"spawn"];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(wentToForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
[self addChild:self.world];
[self.world addChild:bottom];
[self.world addChild:self.player];
[self addChild:left];
[self addChild:right];
[self addChild:self.pause];
[self addChild:self.scoreLabelInGame];
[self addChild:self.actualScore];
}
-(void)createUI {
self.pause = [SKSpriteNode spriteNodeWithImageNamed:#"pausebutton.png"];
self.pause.size = CGSizeMake(self.customUnit,self.customUnit);
self.pause.name = #"pauseButton";
self.pause.position = CGPointMake(30, self.frame.size.height - 30);
self.scoreLabelInGame = [SKLabelNode labelNodeWithFontNamed:#"Futura"];
self.scoreLabelInGame.text = #"";
self.scoreLabelInGame.fontSize = 25;
self.scoreLabelInGame.position = CGPointMake(self.frame.size.width - 100, self.frame.size.height - 40);
self.actualScore = [SKLabelNode labelNodeWithFontNamed:#"Futura"];
self.actualScore.text = #"SCORE: 0";
self.actualScore.fontSize = 25;
self.actualScore.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeRight;
self.actualScore.position = CGPointMake(self.frame.size.width - 20, self.frame.size.height - 40);
self.deathImage = [SKSpriteNode spriteNodeWithImageNamed:#"youdied.png"];
self.deathImage.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame)*1.5);
}
-(void)createPauseMenu {
self.pausedImage = [SKSpriteNode spriteNodeWithImageNamed:#"paused.png"];
self.pausedImage.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame)*1.5);
self.restart = [SKLabelNode labelNodeWithFontNamed:#"Futura"];
self.restart.text = #"RESTART";
self.restart.fontSize = 25;
self.restart.position = CGPointMake(CGRectGetMidX(self.frame), self.pausedImage.position.y - self.pausedImage.position.y/5);
self.resume = [SKLabelNode labelNodeWithFontNamed:#"Futura"];
self.resume.text = #"RESUME";
self.resume.fontSize = 25;
self.resume.position = CGPointMake(self.restart.position.x, self.restart.position.y - self.customUnit);
}
-(void)spawningEnemy {
NSLog(#"spawned");
SKSpriteNode *aNewEnemy = [self.enemyData createEnemyWithSize:self.customUnit andWidth:self.frame.size.width andHeight:self.frame.size.height + self.player.position.y];
aNewEnemy.physicsBody.allowsRotation = NO;
aNewEnemy.physicsBody.categoryBitMask = self.enemyCategory;
aNewEnemy.physicsBody.collisionBitMask = self.enemyCategory | self.playerCategory | self.edgeCategory | self.bottomCategory;
aNewEnemy.physicsBody.contactTestBitMask = self.enemyCategory | self.playerCategory | self.edgeCategory | self.bottomCategory;
[self.world addChild:aNewEnemy];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
if([self.pause containsPoint:location] && self.isPaused == NO){
[self pauseGame];
}else if([self.resume containsPoint:location] && self.isPaused == YES) {
[self resumeGame];
}else if ([self.restart containsPoint:location] && self.isPaused == YES){
[self restartGame];
}else if (self.isTouchingGround == YES && self.isPaused == NO) {
[self.playerData jump:self.player];
NSLog(#"GOD YES");
self.isTouchingGround = NO;
}
}
-(void)pauseGame {
[self createPauseMenu];
NSLog(#"Pausing...");
[self removeActionForKey:#"spawn"];
self.world.paused = YES;
[self addChild:self.pausedImage];
[self addChild:self.restart];
[self addChild:self.resume];
NSString *path = [NSString stringWithFormat:#"%#/menu_music.mp3", [[NSBundle mainBundle]resourcePath]];
NSURL *pauseMusicURL = [NSURL fileURLWithPath:path];
self.pauseMusicPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:pauseMusicURL error:nil];
self.pauseMusicPlayer.numberOfLoops = -1;
[self.pauseMusicPlayer play];
[self.pause removeFromParent];
self.scoreLabelInGame.position = CGPointMake(self.restart.position.x, self.resume.position.y - self.customUnit);
self.actualScore.position = CGPointMake(self.restart.position.x, self.scoreLabelInGame.position.y - self.customUnit);
self.isPaused = YES;
[self.mainMusicPlayer pause];
}
-(void)restartGame {
[self removeAllChildren];
[self removeAllActions];
self.enemyData = nil;
self.isPaused = NO;
[self.pauseMusicPlayer stop];
[self createSceneContents];
[self runAction:self.spawnAction withKey:#"spawn"];
}
-(void)resumeGame {
self.isPaused = NO;
[self.pauseMusicPlayer stop];
[self runAction:self.spawnAction withKey:#"spawn"];
self.scoreLabelInGame.position = CGPointMake(self.frame.size.width - 100, self.frame.size.height - 40);
self.actualScore.position = CGPointMake(self.frame.size.width - 20, self.frame.size.height - 40);
[self.mainMusicPlayer play];
[self.restart removeFromParent];
[self.resume removeFromParent];
[self.pausedImage removeFromParent];
[self addChild:self.pause];
}
-(void)gameOver {
NSLog(#"Game Over");
GameDataHelper *gameData = [[GameDataHelper alloc]init];
[self removeActionForKey:#"spawn"];
[self addChild:self.restart];
[self addChild:self.deathImage];
SKAction *gameOverSound = [SKAction playSoundFileNamed:#"gameover_tune.mp3" waitForCompletion:NO];
[self runAction:gameOverSound];
NSString *path = [NSString stringWithFormat:#"%#/menu_music.mp3", [[NSBundle mainBundle]resourcePath]];
NSURL *pauseMusicURL = [NSURL fileURLWithPath:path];
self.pauseMusicPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:pauseMusicURL error:nil];
self.pauseMusicPlayer.numberOfLoops = -1;
[self.pauseMusicPlayer play];
[self.pause removeFromParent];
SKLabelNode *highScore = [SKLabelNode labelNodeWithFontNamed:#"Futura"];
NSString *highScoreText = [NSString stringWithFormat:#"HIGHSCORE: %ld",[GameDataHelper sharedGameData].highScore];
highScore.text = highScoreText;
highScore.fontSize = 25;
highScore.position = CGPointMake(self.frame.size.width/2, self.restart.position.y - (2*self.customUnit));
[self addChild:highScore];
self.scoreLabelInGame.position = CGPointMake(self.restart.position.x, self.resume.position.y - self.customUnit);
self.actualScore.position = CGPointMake(self.restart.position.x, self.scoreLabelInGame.position.y - self.customUnit);
self.isPaused = YES;
[self.mainMusicPlayer pause];
[gameData save];
}
-(void)wentToForeground {
[self pauseGame];
}
In Enemy.m:
-(SKSpriteNode *)createEnemyWithSize:(float)size andWidth:(float)width andHeight:(float)height {
self.enemy = [SKSpriteNode spriteNodeWithImageNamed:#"block.png"];
self.enemy.size = CGSizeMake(size - 5, size - 5);
self.enemy.name = #"fallingEnemy";
self.enemy.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(size - 3, size - 3)];
self.enemy.physicsBody.restitution = 0;
self.enemy.physicsBody.allowsRotation = NO;
int randomSection = arc4random_uniform(7);
switch (randomSection) {
case 0:
self.enemy.position = CGPointMake(2.5 + self.enemy.size.width/2, height-5);
break;
case 1:
self.enemy.position = CGPointMake(width/7 + self.enemy.size.width/2, height-5);
break;
case 2:
self.enemy.position = CGPointMake((width/7*2) + self.enemy.size.width/2, height-5);
break;
case 3:
self.enemy.position = CGPointMake((width/7*3) + self.enemy.size.width/2, height-5);
break;
case 4:
self.enemy.position = CGPointMake((width/7*4) + self.enemy.size.width/2, height-5);
break;
case 5:
self.enemy.position = CGPointMake((width/7*5) + self.enemy.size.width/2, height-5);
break;
case 6:
self.enemy.position = CGPointMake((width/7*6) + self.enemy.size.width/2, height-5);
break;
default:
break;
}
return self.enemy;
}
Even though you might have found the answer already i see some problems with your code:
Note: self.spawnAction needs to be a Strong property so maintain the reference when it's removed.
You are running the action on self.world, but removing it on self.
[self.world runAction:self.spawnAction withKey:#"spawn"];
[self removeActionForKey:#"spawn"];
self.world.paused = YES;
If you set the paused property of self.world to YES you do not need to remove the action as pause will immediately pause all actions.
You do not set the paused property to NO again on -(void)resumeGame
And you run it on self instead of self.world on resume.
Overall I would try to avoid using paused properties which might behave different and not give the results you want.
Also, keep in mind that removing nodes and actions don't happen instantly so if you pause nodes afterwards you might not get expected results.
Also, there is a limited amount of time you have on didEnterBackground to do your stuff before the it actually goes to background.
Hope it helps.

adding second to timer iOS game sprite kit

I am trying to add a few seconds to countdown timer each time when enemy and player node contacts.
Also when the player reach to some certain point I want player to pass to another level. I am sending part of the code . Could anybody help me to solve this problem. By the way because I am new to programming my code is a little hard to read . Sorry for that.
Thanks
-(void)update:(CFTimeInterval)currentTime{
if (startGamePlay){
startTime = currentTime;
startGamePlay = NO;
}
countDownInt = 10.0 - (int)(currentTime-startTime);
if(countDownInt > 0 ){ //if counting down to 0 show counter
countDown.text = [NSString stringWithFormat:#"%i", countDownInt];
}
else if (countDownInt == 0){
countDown.text =#"0";
Level2 *level2 = [Level2 sceneWithSize:self.frame.size];
SKTransition *transition = [SKTransition fadeWithDuration:0.5];
[self.view presentScene:level2 transition:transition];
}
}
- (void) didBeginContact:(SKPhysicsContact *)contact {
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
firstBody = contact.bodyA;
secondBody = contact.bodyB;
} else {
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
if ( firstBody.categoryBitMask == CollisionCategoryBrick && secondBody.categoryBitMask == CollisionCategoryShark ) {
NSLog(#"BRICK");
[contact.bodyB.node removeFromParent];
} else if ( firstBody.categoryBitMask == CollisionCategoryWaterBall && secondBody.categoryBitMask == CollisionCategoryShark ) {
NSLog(#"BALL");
countDownInt = [countDown.text intValue];
[contact.bodyA.node removeFromParent];
[self addPoints:PointsPerHit];
if (![ self childNodeWithName:#"WATERBALLTL"]) {
[self addWaterBallTopLeft];
}
if (![ self childNodeWithName:#"WATERBALLTR"]) {
[self addWaterBallTopRight];
}
if (![ self childNodeWithName:#"WATERBALLBL"]) {
[self addWaterBallBottomLeft];
}
if (![ self childNodeWithName:#"WATERBALLBR"]) {
[self addWaterBallBottomRight];
}
}
NSLog(#"%lu", (unsigned long)[self.children count]);
}
#import "HudNode.h"
#implementation HudNode
{
SKLabelNode *countDown;
BOOL startGamePlay;
NSTimeInterval startTime;
}
+ (instancetype) hudAtPosition:(CGPoint)position inFrame:(CGRect)frame {
HudNode *hud = [self node];
hud.position = position;
hud.zPosition = 10;
hud.name = #"HUD";
SKLabelNode *scoreLabel = [SKLabelNode labelNodeWithFontNamed:#"Futura-CondensedExtraBold"];
scoreLabel.name = #"Score";
scoreLabel.text = #"0";
scoreLabel.fontSize = 24;
scoreLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeRight;
scoreLabel.position = CGPointMake(frame.size.width-20, -10);
scoreLabel.zPosition = 12;
[hud addChild:scoreLabel];
SKLabelNode *countDown = [SKLabelNode labelNodeWithFontNamed:#"Futura-Medium"];
countDown.fontSize = 50;
countDown.position = CGPointMake(30, -10);
countDown.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter; //good for positioning with other sprites
countDown.fontColor = [SKColor whiteColor ];
countDown.name = #"countDown";
countDown.zPosition = 100;
[hud addChild:countDown];
return hud;
}
- (void) addPoints:(NSInteger)points {
self.score += points;
SKLabelNode *scoreLabel = (SKLabelNode *) [self childNodeWithName:#"Score"];
scoreLabel.text = [NSString stringWithFormat:#"Score:%li", (long)self.score];
}
Create a boolean flag that the update method checks to see if it should add seconds.
Add the following variable and set self.addSeconds = NO; initially:
#property (nonatomic) BOOL addSeconds;
Set self.addSeconds =YES; in the method where your player hits your enemy:
Add the following to the very beginning of the Update:
if(self.addSeconds == YES)
{
CountDownInt+= 5 //Change this to add as many seconds as you want
self.addSeconds=NO;
}
This should do the trick.

Resources