At the time of this post, I am using the latest versions of XCode and SpriteBuilder.
I have 2 games, one of which is built using purely Objective-C on XCode and another which uses Objective-C on XCode as well as SpriteBuilder. The non-SpriteBuilder game has a tap to play screen whose background looks the same as the in-game background. The second you tap the screen, you go straight into the game.
The Sprite Builder game is the one I am trying to implement a Start screen on. I read a few tutorials, http://www.reigndesign.com/blog/creating-a-simple-menu-with-scene-transition-in-cocos2d/, but it didn't help me. I don't want to transition from one screen to another using different background images.
This is the start screen that my non-SpriteBuilder game uses:
-(void)NewGame{
Bottom1.hidden = YES;
Bottom2.hidden = YES;
Bottom3.hidden = YES;
Bottom4.hidden = YES;
Bottom5.hidden = YES;
Bottom6.hidden = YES;
Bottom7.hidden = YES;
Top1.hidden = YES;
Top2.hidden = YES;
Top3.hidden = YES;
Top4.hidden = YES;
Top5.hidden = YES;
Top6.hidden = YES;
Top7.hidden = YES;
Obstacle.hidden = YES;
Obstacle2.hidden = YES;
Intro1.hidden = NO;
Intro2.hidden = NO;
Intro3.hidden = NO;
Heli.hidden = NO;
Heli.center = CGPointMake(31, 74);
Heli.image = [UIImage imageNamed:#"HeliUp.png"];
Start = YES;
ScoreNumber = 0;
Score.text = [NSString stringWithFormat:#"Score: 0"];
Intro3.text = [NSString stringWithFormat:#"High Score: %i", HighScore];
All obstacles are hidden except the background and the intro labels. You also see your high score and the tap to play button.
When you tap the screen, this is the next code:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (Start == YES) {
Intro1.hidden = YES;
Intro2.hidden = YES;
Intro3.hidden = YES;
timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(HeliMove) userInfo:nil repeats:YES];
Scorer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(Scoring) userInfo:nil repeats:YES];
Start = NO;
Bottom1.hidden = NO;
Bottom2.hidden = NO;
Bottom3.hidden = NO;
Bottom4.hidden = NO;
Bottom5.hidden = NO;
Bottom6.hidden = NO;
Bottom7.hidden = NO;
Top1.hidden = NO;
Top2.hidden = NO;
Top3.hidden = NO;
Top4.hidden = NO;
Top5.hidden = NO;
Top6.hidden = NO;
Top7.hidden = NO;
Obstacle.hidden = NO;
Obstacle2.hidden = NO;
RandomPosition = arc4random() %75;
RandomPosition = RandomPosition + 110;
Obstacle.center = CGPointMake(570,RandomPosition);
RandomPosition = arc4random() %75;
RandomPosition = RandomPosition + 110;
Obstacle2.center = CGPointMake(855,RandomPosition);
RandomPosition = arc4random() %55;
Top1.center = CGPointMake(560, RandomPosition);
RandomPosition = RandomPosition +265;
Bottom1.center = CGPointMake(560, RandomPosition);
RandomPosition = arc4random() %55;
Top2.center = CGPointMake(640, RandomPosition);
RandomPosition = RandomPosition +265;
Bottom2.center = CGPointMake(640, RandomPosition);
RandomPosition = arc4random() %55;
Top3.center = CGPointMake(720, RandomPosition);
RandomPosition = RandomPosition +265;
Bottom3.center = CGPointMake(720, RandomPosition);
RandomPosition = arc4random() %55;
Top4.center = CGPointMake(800, RandomPosition);
RandomPosition = RandomPosition +265;
Bottom4.center = CGPointMake(800, RandomPosition);
RandomPosition = arc4random() %55;
Top5.center = CGPointMake(880, RandomPosition);
RandomPosition = RandomPosition +265;
Bottom5.center = CGPointMake(880, RandomPosition);
RandomPosition = arc4random() %55;
Top6.center = CGPointMake(960, RandomPosition);
RandomPosition = RandomPosition +265;
Bottom6.center = CGPointMake(960, RandomPosition);
RandomPosition = arc4random() %55;
Top7.center = CGPointMake(1040, RandomPosition);
RandomPosition = RandomPosition +265;
Bottom7.center = CGPointMake(1040, RandomPosition);
}
Y = -7;
Heli.image = [UIImage imageNamed:#"HeliUp.png"];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
Y = 7;
Heli.image = [UIImage imageNamed:#"HeliDown.png"];
}
In basic terms, all obstacles are loaded and are generated in random positions, the object moves with u tap the screen and drops if you don't tap the screen. The intro labels are hidden.
Here's the code for my SpriteBuilder game:
- (void)didLoadFromCCB {
self.userInteractionEnabled = TRUE;
_grounds = #[_ground1, _ground2];
for (CCNode *ground in _grounds) {
// set collision txpe
ground.physicsBody.collisionType = #"level";
ground.zOrder = DrawingOrderGround;
_scrollSpeed = 80.f;
}
// set this class as delegate
_physicsNode.collisionDelegate = self;
// set collision txpe
_hero.physicsBody.collisionType = #"hero";
_hero.zOrder = DrawingOrdeHero;
_obstacles = [NSMutableArray array];
[self spawnNewObstacle];
[self spawnNewObstacle];
[self spawnNewObstacle];
}
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
if (!_gameOver) {
[_hero.physicsBody applyImpulse:ccp(0, 400.f)];
[_hero.physicsBody applyAngularImpulse:10000.f];
_sinceTouch = 0.f;
}
}
Really short code. Basically, after the logo screen disappears, the game starts right away without a start screen. When the game ends, it restarts and you immediately start playing again.
Trying to figure out how I can add another void statement to the SpriteBuilder game so that a person goes from tapping the screen then to playing the game instead of immediately going into playing the game.
I can add the viewDidLoad statement to the SpriteBuilder game but I wonder if it will conflict with the didLoadfromCCB statement. I need to have the start screen transition into the game screen using the same background. Only difference is, the start screen will hide the obstacles and show the play now labels, then the labels will hide and obstacles become visible in the game screen.
I would suggest doing it following the spritebuilder tutorial. This will probably require you to rejig your code. The following tutorial also helps you with linking spritebuilder to backend code, and also how to transition between scenes.
I'm not 100% sure why you included so much detail in your question, I'm confused with what is game logic, and what is other logic... So If I'm off the mark, let me know and I'll update my answer.
https://www.makegameswith.us/tutorials/getting-started-with-spritebuilder/menus/
Smells a bit like Flappy Bird to me.
Either way I would implement it with a gameState variable and a layer with the menu in.
When you open the game (viewDidLoad) set the gameState to 0 e.g. Menu
then in the touch method I would have a switch like the following:
switch(gameState){
case 0: //Menu
menuLayer.hidden = YES;
gameState = 1 //IN_GAME
break;
case 1: //In game
//Do your game tap code
break;
case 2: //Game Over
gameOverLayer.hidden = NO;
break;
}
Or you could duplicate the CCb and remove the game code and load the game cCB on touch, but I think the former is the best option.
Related
I'm making a simple little game.
I have two ints:
1) One to make an object's position to be generated randomly on the Y- and X-axis.
2) One for the scoring function.
I use NSTimers to make the objects move, and for the score to add 1 every second.
The problem is that the game gets all bugged and the timers start going crazy when the both ints exist. If I remove the scoring int/timer, the objects move perfectly. And the other way around.
I can't seem to find the issue since it should be working.
Any ideas?
ViewController.h:
int scoreNumber;
int randomPosition;
ViewController.m:
#define IsIphone4 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )480 ) < DBL_EPSILON )
#define IsIphone5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
#define IsIphone6 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )667 ) < DBL_EPSILON )
#define IsIphone6Plus ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )736 ) < DBL_EPSILON )
- (void)obstaclesMoving {
obstacle.center = CGPointMake(obstacle.center.x - 1, obstacle.center.y);
obstacle2.center = CGPointMake(obstacle2.center.x - 1, obstacle2.center.y);
obstacle3.center = CGPointMake(obstacle3.center.x - 1, obstacle3.center.y);
if (IsIphone4) {
if (obstacle.center.x < 0) {
randomPosition = arc4random_uniform(60);
randomPosition = randomPosition + 70;
obstacle.center = CGPointMake(320, randomPosition);
}
if (obstacle2.center.x < 0) {
randomPosition = arc4random_uniform(60);
randomPosition = randomPosition + 360;
obstacle2.center = CGPointMake(320, randomPosition);
}
if (obstacle3.center.x < 0) {
randomPosition = arc4random_uniform(100);
randomPosition = randomPosition + 215;
obstacle3.center = CGPointMake(320, randomPosition);
}
}
if (IsIphone5) {
if (obstacle.center.x < 0) {
randomPosition = arc4random_uniform(60);
randomPosition = randomPosition + 70;
obstacle.center = CGPointMake(320, randomPosition);
}
if (obstacle2.center.x < 0) {
randomPosition = arc4random_uniform(60);
randomPosition = randomPosition + 448;
obstacle2.center = CGPointMake(320, randomPosition);
}
if (obstacle3.center.x < 0) {
randomPosition = arc4random_uniform(100);
randomPosition = randomPosition + 259;
obstacle3.center = CGPointMake(320, randomPosition);
}
}
if (IsIphone6) {
if (obstacle.center.x < 0) {
randomPosition = arc4random_uniform(60);
randomPosition = randomPosition + 70;
obstacle.center = CGPointMake(375, randomPosition);
}
if (obstacle2.center.x < 0) {
randomPosition = arc4random_uniform(60);
randomPosition = randomPosition + 547;
obstacle2.center = CGPointMake(375, randomPosition);
}
if (obstacle3.center.x < 0) {
randomPosition = arc4random_uniform(100);
randomPosition = randomPosition + 309;
obstacle3.center = CGPointMake(375, randomPosition);
}
}
if (IsIphone6Plus) {
if (obstacle.center.x < 0) {
randomPosition = arc4random_uniform(60);
randomPosition = randomPosition + 70;
obstacle.center = CGPointMake(414, randomPosition);
}
if (obstacle2.center.x < 0) {
randomPosition = arc4random_uniform(60);
randomPosition = randomPosition + 616;
obstacle2.center = CGPointMake(414, randomPosition);
}
if (obstacle3.center.x < 0) {
randomPosition = arc4random_uniform(100);
randomPosition = randomPosition + 343;
obstacle3.center = CGPointMake(414, randomPosition);
}
}
}
- (IBAction)characterUp:(id)sender {
characterDrop = 5;
}
- (IBAction)characterDown:(id)sender {
characterDrop = -5;
}
- (void)characterMoving {
character.center = CGPointMake(character.center.x, character.center.y - characterDrop);
characterDrop = characterDrop - 0.1;
if ((CGRectIntersectsRect(character.frame, ground.frame)) && (characterDrop < -1)) {
[self gameOver];
//Sound for G.O.
}
if ((CGRectIntersectsRect(character.frame, roof.frame)) && (characterDrop < -1)) {
[self gameOver];
//Sound for G.O.
}
}
- (IBAction)startGame:(id)sender {
startGameButton.hidden = YES;
characterMovement = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:#selector(characterMoving) userInfo:nil repeats:YES];
characterDrop = -5;
obstacleTimer = [NSTimer scheduledTimerWithTimeInterval:0.0055 target:self selector:#selector(obstaclesMoving) userInfo:nil repeats:YES];
scorer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(scoring) userInfo:nil repeats:YES];
}
- (void)scoring {
scoreNumber = scoreNumber + 1;
scoreLabelInGame.text = [NSString stringWithFormat:#"%i", scoreNumber];
scoreLabelGameOver.text = [NSString stringWithFormat:#"%i", scoreNumber];
}
- (void)gameOver {
if (scoreNumber > bestScoreNumber) {
[[NSUserDefaults standardUserDefaults] setInteger:scoreNumber forKey:#"bestScoreSaved"];
bestScoreLabel.text = [NSString stringWithFormat:#"New best: %i", scoreNumber];
}
character.hidden = YES;
obstacle.hidden = YES;
obstacle2.hidden = YES;
obstacle3.hidden = YES;
startGameButton.hidden = YES;
scoreLabelInGame.hidden = YES;
characterUpButton.hidden = YES;
characterDownButton.hidden = YES;
scoreLabelGameOver.hidden = NO;
bestScoreLabel.hidden = NO;
gameOverLabel.hidden = NO;
restartGameButton.hidden = NO;
backButton.hidden = NO;
[characterMovement invalidate];
[obstacleTimer invalidate];
[obstacleTimer2 invalidate];
[obstacleTimer3 invalidate];
[obstacleTimer4 invalidate];
[scorer invalidate];
}
- (void)viewDidLoad {
bestScoreNumber = [[NSUserDefaults standardUserDefaults] integerForKey:#"bestScoreSaved"];
bestScoreLabel.text = [NSString stringWithFormat:#"Best: %li", (long)bestScoreNumber];
scoreNumber = 0;
scoreLabelGameOver.hidden = YES;
bestScoreLabel.hidden = YES;
gameOverLabel.hidden = YES;
restartGameButton.hidden = YES;
backButton.hidden = YES;
[super viewDidLoad];
// Do any additional setup after loading the view.
}
I think a cleaner approach would be to move to a CADisplayLink or a single NSTimer and move your views off of the selector it calls.
/* Add _amountOfTicks as an instance Variable */
{
int _amountOfTicks;
int scoreNumber;
int randomPosition;
}
/* replace timers in startGame */
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(tick)];
displayLink.frameInterval = 1;
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
- (void)tick {
_amountOfTicks++;
if (_amountOfTicks > 16){
_amountOfTicks = 1;
}
if (_amountOfTicks == 2) {
[self characterMoving]
}
if (_amountOfTicks == 4) {
[self obstaclesMoving]
}
if (_amountOfTicks == 16) {
[self scoring]
}
}
Also, in the method below you're reducing the center.x by 1 then changing it back to the screens width directly below when checking for macros.
- (void)obstaclesMoving {
obstacle.center = CGPointMake(obstacle.center.x - 1, obstacle.center.y); /* reduce by 1 */
obstacle2.center = CGPointMake(obstacle2.center.x - 1, obstacle2.center.y);
obstacle3.center = CGPointMake(obstacle3.center.x - 1, obstacle3.center.y);
if (IsIphone4) {
if (obstacle.center.x < 0) {
randomPosition = arc4random_uniform(60);
randomPosition = randomPosition + 70;
obstacle.center = CGPointMake(320, randomPosition); /* changes it back to screen width which makes what you did up above useless */
}
}
From the docs:
the effective resolution of the time interval for a timer is limited
to on the order of 50-100 milliseconds
My guess is that this maybe the issue.
If you need to your score increment every 1 sec, why you calling that function every 0.0055 sec?
You also need to move [super viewDidLoad]; to the top of you viewDidLoad function.
Also as #rmaddy stated, you shouldn't be explicitly checking for the screen size.
I can tell you that running a timer with a resolution of 1/180th of a second is going to get you into trouble. Since the frame is updated at most every 60th of a second, the user isn't going to see it. There is no guarantee that the timers will actually run that often (most likely they won't).
You can't use timers like that. You can only use them to trigger an event, and then you need to check exactly what time it is and what you should do.
What might be happening is that you managed to turn autolayout on.
I am having a problem with my coding on Xcode. I am making an app in which a target appears, then you have to tap it, but the time allowed between each tap shortens gradually.
MY PROBLEM: whenever I try and make a UILabel update after a tap, the target goes back to it's original position on my view controller. I have all relevant code here:
-(IBAction)startButtonPressed:(id)sender {
startButton.hidden = YES;
Text.hidden = YES;
targX = arc4random() %769;
targY = arc4random() % 925;
Target.center = CGPointMake(targX, targY);
Target.hidden = NO;
}
-(IBAction)targetTapped:(id)sender {
[Time invalidate];
targX = arc4random() %769;
targY = arc4random() % 925;
[self scoreAdd:(id)sender];
Target.center = CGPointMake(targX, targY);
timeMax = 5 - (Score * 0.05);
if (Score >= 90) {
timeMax = 0.5;
}
Time = [NSTimer scheduledTimerWithTimeInterval:timeMax target:self selector:#selector(Lose:) userInfo:nil repeats:NO];
}
-(void)Lose:(id)sender {
Target.hidden = YES;
Text.hidden = NO;
}
-(void)scoreAdd:(id)sender{
Score = Score + 1;
scoreLabel.text = [NSString stringWithFormat:#"Score: %li", Score];
}
print
targX = arc4random() %769;
targY = arc4random() % 925;
in both methods , check whether you are getting proper points. And check generated points lies with in device screen.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I have just started developing a game, new to Objective-c. Basically at the moment you can move the spaceship around and the spaceship fires continually and there are asteroids coming from the top of screen. But my app freezes after a minute every time why is this?
CODE
#import "AppDelegate.h"
#pragma mark - HelloWorldLayer
// HelloWorldLayer implementation
#implementation HelloWorldLayer
// Helper class method that creates a Scene with the HelloWorldLayer as the only child.
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
-(id) init
{
if( (self=[super init]) ) {
self.isTouchEnabled = YES;
moveLeft = NO;
moveRight = NO;
speed = 3;
fireSpeed = 8;
fireArray = [[NSMutableArray alloc] init];
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite *bg = [[CCSprite alloc] initWithFile:#"space_bg.jpg"];
[bg setPosition:ccp(160, 240)];
CGSize imageSize = bg.contentSize;
bg.scaleX = winSize.width / imageSize.width;
bg.scaleY = winSize.height / imageSize.height;
bg.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:bg];
ship = [[CCSprite alloc] initWithFile:#"ship.png"];
[ship setPosition:ccp(100, 100)];
[self addChild:ship];
[self schedule:#selector(fireLoop:)];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(fireCreate)
userInfo:nil
repeats:YES];
int t = arc4random() % 5;
timer3 = [NSTimer scheduledTimerWithTimeInterval:t
target:self
selector:#selector(asteroidTimer)
userInfo:nil
repeats:YES];
}
return self;
}
NSTimer *timer3;
NSTimer *timer2;
NSTimer *tmpTimer;
-(void)asteroidTimer {
int t = arc4random() % 10;
timer2 = [NSTimer scheduledTimerWithTimeInterval:0
target:self
selector:#selector(asteroidCreate)
userInfo:nil
repeats:YES];
}
-(void)asteroidCreate {
[timer2 invalidate];
[tmpTimer invalidate];
CGSize winSize = [[CCDirector sharedDirector] winSize];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"asteroids.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"asteroids.png"];
NSMutableArray *asteroidAnimFrames = [NSMutableArray array];
for(int i=1; i <= 8; i++) {
[asteroidAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"asteroid%d.png", i]]];
}
CCAnimation *moveAsteroidAnim = [CCAnimation animationWithFrames:asteroidAnimFrames delay:0.1f];
CCSprite *asteroid = [CCSprite spriteWithSpriteFrameName:#"asteroid1.png"];
int x = arc4random() % 320;
int y = arc4random() % 480;
asteroid.position = ccp(x, 480);
CCAction *asteroidAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:moveAsteroidAnim restoreOriginalFrame:NO]];
int q = arc4random() % 320;
int r = arc4random() % 10;
CCAction *moveAction = [CCMoveTo actionWithDuration:r position:ccp(q, -50)];
[asteroid runAction:moveAction];
[asteroid runAction:asteroidAction];
[spriteSheet addChild:asteroid];
[self addChild:spriteSheet];
int t = arc4random() % 10;
tmpTimer = [NSTimer scheduledTimerWithTimeInterval:t
target:self
selector:#selector(asteroidTimer)
userInfo:nil
repeats:YES];
}
-(void)fireLoop:(ccTime)fl {
if(fireArray.count > 0) {
for(int i = 0; i < fireArray.count; i++){
CCSprite *tmpFire = [fireArray objectAtIndex:i];
if(tmpFire.position.y < 500){
[tmpFire setPosition:ccp([tmpFire position].x, [tmpFire position].y + fireSpeed)];
}else{
[fireArray removeObjectAtIndex:i];
}
}
} else
{
}
}
-(void)fireCreate {
int shootPositionX = [ship position].x;
int shootPositionY = ([ship position].y) + 35;
CCSprite *fire;
fire = [[CCSprite alloc] initWithFile:#"fire.png"];
[fire setPosition:ccp(shootPositionX, shootPositionY)];
[fireArray addObject:fire];
[self addChild:fire];
[fire release];
}
-(void)gameLoop:(ccTime)dt {
int shipPositionX = 41/2;
if([ship position].x > shipPositionX){
[ship setPosition:ccp([ship position].x - speed, [ship position].y)];
}
}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
for(UITouch *t in touches){
CGPoint point = [self convertTouchToNodeSpace:t];
//if(point.x <= 160){
// moveRight = NO;
// moveLeft = YES;
//}else{
// moveRight =YES;
// moveLeft = NO;
//}
if(allowedToMove)
[ship setPosition:ccp(point.x, point.y + 76)];
}
}
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
for(UITouch *t in touches){
CGPoint point = [self convertTouchToNodeSpace:t];
int shipX = [ship position].x;
int shipY = [ship position].y;
if (CGRectContainsPoint(CGRectMake (shipX - 20.5, shipY - 96, 50, 50), point))
{
allowedToMove = true;
}
}
}
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
for(UITouch *t in touches){
CGPoint point = [self convertTouchToNodeSpace:t];
pointXLeft = point.x;
pointYLeft = point.y;
allowedToMove = false;
}
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
// in this particular example nothing needs to be released.
// cocos2d will automatically release all the children (Label)
// don't forget to call "super dealloc"
[super dealloc];
}
In general track down what seems to be causing it. What does the debugger say? Did you stop the execution or set a breakpoint somewhere that might be problematic? What about exception breakpoints? If those don't work take out your timers and see if you have any change. What about your fire methods?
Also your use of timers look weird and should be the first area you look. Side note, for the asteroids why are you not running the two actions you want to run on the new asteroid using CCSpawn?
In addition are you ever removing projectiles that you shoot from the screen or the asteroids that are no longer needed? If not you should.
But to answer your question the problem looks most likely a timer issue. Your use of timers are wrong. You have timer-3 call asteroid timer, which in turns creates timer-2 that calls asteroid create, which in turns invalidates timer-2 (that had an interval of 0 which makes it look a little unnecessary) and also creates a tmp-timer that calls asteroid create. Timer 3 is left to continuously do what it wants, which is all the steps above, at every x number of seconds. Even though tmp-timer seems to be trying to spawn more asteroid creations and take over the process, instead that looks both incorrect and odd. And you are doing this with a few global timers that can, seemingly have a valid timer but is overridden by a new allocation, just to be invalidated by another method randomly soon after even though what it was trying to invalidate technically was leaked and no longer has a pointer to it, thus invalidating the wrong instance to the wrong timer. Keep in mind your tmp-timer and timer-3 has random intervals. If tmp-timer has an interval of 3 seconds and timer-3 has a 9 second interval at some point something looks like it is bound to go wrong. It is hard to say by glancing at it just how, but it looks inevitable.
Or at least I think that is what is happening. Again the code makes very weird and potentially error prone use of timers.
Also timer-1 is going to keep creating sprites unchecked without those sprites ever going away. Something to think about for sure.
Edit (Solution):
If I understand what you are trying to do you want to create timers every variable amount of seconds that, at the end, creates a new asteroid. There are two things that stand out with this. You want something to call the creation of an asteroid, and something else to do the actual create:
- (void)countDownToCreateNextAsteroid
{
int minTime = 1;
int maxTime = 10;
int randomTime = (arc4random() % (maxTime - minTime)) + minTime;
id countdownDelay = [CCDelayTime actionWithDuration:randomTime];
id creationMethod = [CCCallFunc actionWithTarget:self selector:#selector(createAsteroid)];
id countdownToCreateSeq = [CCSequence actions:countdownDelay, creationMethod, nil];
[self stopAllActions];
[self runAction:countdownToCreateSeq];
}
- (void)createAsteroid
{
CGPoint someStartPosition = ccp(1024.0f, 512.0f);
CCSprite* asteroid = [CCSprite spriteWithFile:#"someImage.png"];
asteroid.position = someStartPosition;
// asterdoid dot yada yada = ....
[self addChild:asteroid];
float someDuration = 10.0f;
CGPoint someOffscreenPos = ccp(-1024.0f, 512.0f);
// Move off screen, then remove yourself...
id asteroidMove = [CCMoveTo actionWithDuration:someDuration
position:someOffscreenPos];
id asteroidRemove = [CCCallBlock actionWithBlock:^
{
[asteroid removeFromParentAndCleanup:YES];
}];
id asteroidSeq = [CCSequence actions:asteroidMove, asteroidRemove, nil];
[asteroid runAction:asteroidSeq];
[self countDownToCreateNextAsteroid];
}
Therefore in your onEnter you could have something to kick it all off and that's it:
- (void)onEnter
{
[super onEnter];
// yada yada
[self countDownToCreateNextAsteroid];
}
And the rest would take care of itself. This assumes this quick and simple approach is all you were after. The count down method sets up the count down, calls a method to create an asteroid at the end of that time, then exits. After the asteroid is created a new call to the count down method is invoked, which will setup another asteroid to be created at another random time. This is one quick and simple way to do what it looks you are were actually trying to accomplish with all those timers.
I'm using SpriteKit to create a game in which objects at the bottom of the map move.
The objects are crocodiles and coins.
The scene uses an NSTimer to that calls this selector:
timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(generateBottomSprite) userInfo:nil repeats:YES];
-(void) generateBottomSprite{
int random = (arc4random() % difficulty+3) + difficulty-3;
if (counter >random){
SKSpriteNode *bottomSprite;
if (random>difficulty){
bottomSprite = [SKSpriteNode spriteNodeWithImageNamed:#"crocodile"];
bottomSprite.size = CGSizeMake(70, 120);
bottomSprite.physicsBody.categoryBitMask = crocMask;
}else{
bottomSprite = [SKSpriteNode spriteNodeWithImageNamed:#"coin"];
bottomSprite.size = CGSizeMake(50, 50);
bottomSprite.physicsBody.categoryBitMask = coinMask;
}
//create a crocodile
bottomSprite.position = CGPointMake(self.frame.size.width,bottomSprite.frame.size.height/2);
bottomSprite.name = #"bottomSprite";
[self addChild: bottomSprite];
counter = 0;
[self enumerateChildNodesWithName:#"bottomSprite" usingBlock: ^(SKNode *node, BOOL *stop) {
node.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:node.frame.size];
node.physicsBody.affectedByGravity = NO;
node.physicsBody.mass = 8000.0;
node.physicsBody.friction = 2.50;
node.physicsBody.usesPreciseCollisionDetection = YES;
}];
}
}
The problem: all the physicsWorld's properties are re-initialized on each call to the selector and i must iterate over the children of the scene in order to re-assign them.
Why are the properties get re-initialized?
you are setting the categorybitmask before creating the skphysicsbody itself
bottomSprite = [SKSpriteNode spriteNodeWithImageNamed:#"crocodile"];
bottomSprite.size = CGSizeMake(70, 120);
//add the following
bottomSprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:bottomSprite.frame.size];
///////////////
bottomSprite.physicsBody.categoryBitMask = crocMask;
move all the physics settings outside the enumeration like so
if (counter >random){
SKSpriteNode *bottomSprite;
int currentBitMask;
if (random>difficulty){
bottomSprite = [SKSpriteNode spriteNodeWithImageNamed:#"crocodile"];
bottomSprite.size = CGSizeMake(70, 120);
currentBitMask = crocMask;
}else{
bottomSprite = [SKSpriteNode spriteNodeWithImageNamed:#"coin"];
bottomSprite.size = CGSizeMake(50, 50);
currentBitMask = coinMask;
}
**bottomSprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:bottomSprite.frame.size];**
bottomSprite.physicsBody.categoryBitMask = currentBitMask ;
bottomSprite.physicsBody.affectedByGravity = NO;
bottomSprite.physicsBody.mass = 8000.0;//way too high
bottomSprite.physicsBody.friction = 2.50;//probably high
bottomSprite.physicsBody.usesPreciseCollisionDetection = YES;//overkill, set only if critical
//create a crocodile
bottomSprite.position = CGPointMake(self.frame.size.width,bottomSprite.frame.size.height/2);
bottomSprite.name = #"bottomSprite";
[self addChild: bottomSprite];
you also might wanna make use of the update method in the skscene and nstimeintervals to measure time till you pass half a second then call the generateBottomSprite method, it should be more efficient than using an additional NSTimer.
Feel free to ask if anything is unclear, hope this helps.
I am new to cocos2d so please help me if u can
I have background moving from right to left, and the background contains small windows with 3 rows of windows
_spaceDust1 = [CCSprite spriteWithFile:#"bg_front_spacedust.png"];
_spaceDust2 = [CCSprite spriteWithFile:#"bg_front_spacedust.png"];
CGPoint dustSpeed = ccp(0.1 , 0.1);
CGPoint bgSpeed = ccp(0.05 , 0.05);
[_backgroundNode addChild:_spaceDust1 z:0 parallaxRatio:dustSpeed positionOffset:ccp(0,winSize.height / 2)];
[_backgroundNode addChild:_spaceDust2 z:0 parallaxRatio:dustSpeed positionOffset:ccp(_spaceDust1.contentSize.width , winSize.height / 2)];
Now add enemies wich also move from right to left with same speed
_robbers = [[CCArray alloc] initWithCapacity:kNumAstroids];
for (int i = 0; i < kNumAstroids; ++i) {
CCSprite *asteroid = [CCSprite spriteWithSpriteFrameName:#"robber.png"];
asteroid.visible = NO;
[_batchNode addChild:asteroid];
[_robbers addObject:asteroid];
}
in update method:
double curTime = CACurrentMediaTime();
if (curTime > _nextRunemanSpawn) {
float randSecs = [self randomValueBetween:0.20 andValue:1.0];
_nextRunemanSpawn = randSecs + curTime;
float randY = 80.0;
float randY1 = 185.0;
float randY2 = 293.0;
float randDuration = [self randomValueBetween:5.2 andValue:5.2];
float randDuration1 = [self randomValueBetween:1.0 andValue:1.0];
CCSprite *asteroid = [_robbers objectAtIndex:_nextRobber];
_nextRobber++;
if (_nextRobber >= _robbers.count) {
_nextRobber = 0;
}
//[asteroid stopAllActions];
int winChoice = arc4random() % 3;
if (winChoice == 0) {
asteroid.position = ccp(winSize.width +asteroid.contentSize.width / 2 , randY);
asteroid.visible = YES;
}
else if(winChoice == 1){
asteroid.position = ccp(winSize.width +asteroid.contentSize.width / 2 , randY1);
asteroid.visible = YES;
}else {
asteroid.position = ccp(winSize.width +asteroid.contentSize.width / 2 , randY2);
asteroid.visible = YES;
}
[asteroid runAction:[CCSequence actions:[CCMoveBy actionWithDuration:randDuration position:ccp(-winSize.width-asteroid.contentSize.width, 0)],
[CCCallFuncN actionWithTarget:self selector:#selector(setInvisible:)],nil]];
All is going well but i want to set this enemies in to window and in random position
so how can i set x-argument of enemies so it can be fix in to window of the background?
For some reason I cannot comment so this is written as an answer. What exactly are you trying to do? I am a bit confused. It sounds like you want the X position to be set so that it is in one of 3 random positions, is that correct?