I am making an iOS game. Part of the goal of the game is to collect coins. I generate the coins randomly, and keep track of them with an NSMutable array. I know how to handle the coins if they are collected, but I want the coins to disappear from the screen after 10 seconds if they are not collected so that they are not permanently displayed or remembered in the NSMutable array. Any suggestions on how to do this?
If you want to disappear after 10 seconds from appear if they are not collected, you can run CCAction on them.
[SpriteCoin runAction:[CCSequence actions:[CCDelayTime actionWithDuration:10], [CCCallFuncN actionWithTarget:self selector:#selector(removeSprite:)]];
you will remove it with this function:
-(void) removeSprite:(id)sender
{ [self removeChild:sender cleanup:YES]; }
Related
Someone please push me in the right direction.
Here's the thing: what I'm trying to do is to limit the amount of projectiles (shot by player) that can be on screen. That limit is recorded in a constant. So while the number of projectile nodes on the screen is equal to that constant, the player will not be able to shoot any more.
Once a projectile goes off screen, that projectile is getting removed, decreasing the amount of on-screen nodes, and the player should be able to shoot again, of course without exceeding the limit. (The shots are being done using a push of a button, not tapping the screen in the direction you want to shoot, if that is important. You can only shoot one at a time.)
What kind of algorithm can I use to solve this? How to keep up the number of projectiles currently on-screen? How to let the system know WHEN to decrease that amount (I've been using collision detection, having a "screen bounds node") and WHAT node to remove (I've been recording node ID in the name, but looks like doing that the wrong way)? Please suggest a solid solution to this. Hope what I have requested is possible.
You have made two claims here:
A projectile is created when shot by a player.
A projectile is deleted when it goes off screen.
If you already have code removing the projectile when it goes off screen, you could just leverage a simple static count:
#implementation Projectile
static NSUInteger projectileCount = 0;
+ (instancetype)fireProjectile {
if (projectileCount < MAX_PROJECTILES) {
projectileCount++;
return [[Projectile alloc] init];
}
return nil;
}
- (void)dealloc {
projectileCount--;
}
#end
Not sure if I understand exactly what you want, but based on what I have read you could have an array of projectiles that contains the amount of projectiles you want to allow.
// create an array
NSMutableArray *projectiles = [NSMutableArray array];
// populate the array with your projectiles
for (int index = 0;index < kMaxProjectiles;index++)
{
Projectile *projectile = [[Projectile alloc]init];
[projectiles addObject:projectile]
}
Now whenever you want to shoot a projectile, remove it from that array and shoot it.
Projectile *projectile = [projectiles objectAtIndex:0];
[projectiles removeObjectAtIndex:0];
When it goes off the screen, remove it from the parent and push it right back into the array.
[projectile removeFromParent];
[projectiles addObject:projectile];
It's basically a pooling system where you would create your projectiles once and recycle them. When choosing to shoot, you can just check the count of the array to determine if you have a projectile available :
if (projectiles.count > 0)
{
// yep, there are projectiles in the pool, fire away
}
Added advantage of pooling is that you are not constantly creating and killing your projectiles, which will result in pauses when Garbage Collection occurs or if you ever tried to create alot of projectiles at once.
I'm developing a game in iOS Spritekit using Objective C. I created a menu view in viewController and from that I'm calling a gameScene. In gameScene I uses five different nodes which has to released one by one randomly from the top of the screen. Each of those five nodes have 4 different images so it is stored in atlas. If the node moves to the bottom of the screen I change the image in the node and release from the top of the screen.
I'm releasing the node using the following code
self.wait = [SKAction waitForDuration: 1.0];
self.run = [SKAction runBlock:^{
if ([nodesArray count] > 0) {
//select the node to release randomly and remove it from array
}
}];
self.seq = [SKAction sequence: #[self.wait, self.run]];
Those five nodes are stored in an array called nodesArray.
Code for selecting the random node :
int i = arc4random() %[nodesArray count];
SKSpriteNode *mynode = nodesArray[i];
For removing the node in array :
[nodesArray removeObjectAtIndex:i];
After for setting the position :
mynode.position = CGPointMake(x, y)
and for velocity :
mynode.physicsBody.velocity = CGVectorMake(dx,dy)
And finally add the node to scene :
[self addChild: mynode];
Repeat these steps until the array count is 0.
Now my problem is that I noticed a stutter when the nodes are released from the array. I don't know why these stutter are occurs as it's random, these predominantly occurs when the scene is restarted or If I come from another scene to game scene. When I come directly to GameScene without any intermediate scene with just a Launchscreen.xib , there is no stutter.
For restarting the scene I call the scene by the following code
GameScene *firstScene = [GameScene sceneWithSize:self.size];
[self.view presentScene:firstScene transition:[SKTransition fadeWithDuration:1.0]]
PS. I also tried implementing Menu as a scene and returning to game scene, nevertheless it too causes random stutter in the game scene irrespective of whether I remove the Node's & Actions before I leave the Game Scene.
Already added a log statement to know how many nodes are available in array.
NSLog(#"Nodes Count is %lu and Random Number is %lu",(unsigned long)[nodesArray count],(unsigned long)i);
After removing the node, the array count will be one less than normal count.
NSLog(#"Nodes Count is %lu",(unsigned long)[nodesArray count]);
After removing all nodes, the method won't call so it wouldn't print any log inside this method.
Log Output :
2014-12-04 11:17:31.171 newGame[633:21010] Nodes Count is 5 and Random Number is 2
Now the second node in the array will be called. After removing the second node in array the array count will be shown in the next log.
2014-12-04 11:17:31.171 newGame[633:21010] Nodes Count after removal of node : 4
This is the bug which is stopping me from releasing the game, so any help will be appreciated.
i am copying a sprite from one NSMutableArray to another, but when i delete the CCSprite from the first NSMutableArray then it is also deleted in the second Array.
How can i prevent this?
In the init method the Arrays are initialized like below.
spriteTempArray = [[NSMutableArray alloc] init] ;
myPowerUpArray = [[NSMutableArray alloc] init] ;
This is the first method were the sprite is placed on screen and animated somewhere on the screen
CCSprite *powerUpSprite = [[CCSprite alloc] initWithFile:SpriteFileName ] ;
powerUpSprite.position = ccp(xcenter,ycenter);
powerUpSprite.scale = 0;
[self addChild:powerUpSprite z:20 tag:puTag];
[spriteTempArray addObject:powerUpSprite];
id zoomIn = [CCScaleTo actionWithDuration:0.2 scale:1] ;
id moveTo = [CCMoveTo actionWithDuration:0.2 position:ccp(winSize.width/2,120)];
[powerUpSprite runAction:zoomIn];
[powerUpSprite runAction:moveTo];
Then when de Sprite is touched, it's moved to the corner of the screen and also stored in another NSMutableArray (myPowerUpArray). But the delete action erases the sprite in both arrays.
CCSprite *powerUpSprite = [spriteTempArray objectAtIndex:0];
id zoomOut = [CCScaleTo actionWithDuration:0.2 scale:0.35] ;
id moveTo = [CCMoveTo actionWithDuration:0.2 position:ccp(winSize.width - 24,winSize.height -31 * myPowerUps -80)];
[powerUpSprite runAction:zoomOut];
[powerUpSprite runAction:moveTo]
[myPowerUpArray addObject:powerUpSprite];
[self deleteSpriteTempArray];
Below the sprite delete method.
-(void)deleteSpriteTempArray{
NSMutableArray *filesToRemove = [[NSMutableArray alloc] init];
for ( id obj in spriteTempArray) {
[filesToRemove addObject:obj];
[self removeChild:obj cleanup:YES];
}
[spriteTempArray removeObjectsInArray:filesToRemove];
}
I haven't observed the Array, other than de sprite is disappearing from the screen.
Your delete array method is odd. To begin why are you calling:
[self removeChild:obj cleanup:YES];
If your intent is not to remove the sprite from the scene? You are telling your code here to remove the sprite from the scene and to clean it up, but based on your post I don't get the impression you actually want that to happen at that moment. If it is your intent that it gets removed after it moves to the corner of your screen, then you should do the move and scale in a CCSpawn (let's simply call it moveScale) and in a CCSequence you should be adding that moveScale action followed by a block action that calls a method on the sprite to remove itself from its parent with cleanup:
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite* powerUp = ...;
float duration = 0.2f;
float desiredScale = 1.0f;
CGPoint desiredPosition = ccp(winSize.width / 2,120);
id zoomIn = [CCScaleTo actionWithDuration:duration scale:desiredScale] ;
id moveTo = [CCMoveTo actionWithDuration:duration position:desiredPosition];
id remove = [CCCallBlock actionWithBlock:^
{
[powerUp removeFromParentAndCleanup:YES];
}];
id moveScale = [CCSpawn actions:zoomIn, moveTo, nil];
id moveScaleRemove = [CCSequence actions:moveScale, remove, nil];
[powerUp runAction:moveScaleRemove];
Secondly why do you have a "files to remove" array in your deletion method that is built with objects to remove? That "files to remove" array is adding all objects from the sprite temp array to it, so it seems a little pointless. Just remove all objects from your sprite temp array. No need to build another temp list with everything to remove, just to essentially be saying remove everything from my original temp array list. That would only be useful if you were removing only SOME of the objects in the temp array. Since you are removing all, the "files to remove" array doesn't serve a purpose. Or at least that is what your code is showing that you are doing. Whether you intend on that behavior is another question.
Also if you have two arrays and an object in both, removing it from one array wouldn't have an impact on the second.
Another issue with your code is in the first code block. If you intend on running a move and a scale action together, they need to be executed via a CCSpawn. Instead you are telling it to scale, then immediately telling it to move which therefore it will be moving but not scaling as it moves. In other words both are not occurring at the same time. On the other hand if you intend for them to be done in sequence, you should be executing them via a CCSequence.
Another thing I noticed about your code is why are you deleting the entire sprite temp array when only one sprite was touched? Did you intend to only remove that one sprite that was touched? Why not just remove that one object, unless I am missing something?
[spriteTempArray removeObject:thisPowerUpIJustTouched];
A final issue I see is with your naming. Your code example indicates that the order of each example code block is the order in which events are occurring right? So why is the initial array you add powerups to called spriteTempArray while the array you add objects to after it is touched is called powerUpArray? Based on the code you've provided and the order they are shown, there is no point to the spriteTempArray. And even if there was, the naming looks backwards. Unless of course I'm missing something. It looks like your intention is to have a power ups array and when a power up is touched it is added to another array to mark that it is to be deleted. But as I've shown earlier in this post, it doesn't look like that second array has a true purpose since your desired behavior is A) not being done and B) would be done using the CCSpawn/CCSequence combo I showed earlier.
Hope this helped. I just woke up so hopefully I addressed your issue properly.
Please bear with me because I'm very new to OOP/ObjC/Cocos2d.
I have a method that is triggered every second like so: [self schedule:#selector(eyelidsBlink:) interval:1.0];
The schedule method is this:
-(CCTimer *) schedule:(SEL)selector interval:(CCTime)interval
{
return [self schedule:selector interval:interval repeat:CCTimerRepeatForever delay:interval];
}
The method is below:
- (void)eyelidsBlink:(CCTime)dt{
CCActionRemove *actionRemoveEyelidsNormal = [CCActionRemove action];
[_whiteGuy_EyelidsNormal runAction:actionRemoveEyelidsNormal];
_whiteGuy_EyelidsBlink = [CCSprite spriteWithImageNamed:#"EyelidsBlink_iPhone4.png"];
_whiteGuy_EyelidsBlink.position = ccp(self.contentSize.width/2,self.contentSize.height/2);
[_whiteGuy_EyelidsBlink setScale:0.5];
[self addChild:_whiteGuy_EyelidsBlink];
CCActionRemove *remove_eyelidsBlink = [CCActionRemove action];
[_whiteGuy_EyelidsBlink runAction:remove_eyelidsBlink];
NSLog(#"Eyelids blinked");
_whiteGuy_EyelidsNormal = [CCSprite spriteWithImageNamed:#"EyelidsNormal_iPhone4.png"];
_whiteGuy_EyelidsNormal.position = ccp(self.contentSize.width/2,self.contentSize.height/2);
[_whiteGuy_EyelidsNormal setScale:0.5];
[self addChild:_whiteGuy_EyelidsNormal];
}
I can see the first blink, but I can't see any others after that. My NSLog is printing in the console every second, so I know the eyelidsBlink method is being called.
Can anyone help me figure out why I can't see any blinks after the first? Let me know if you need more information, or if you can suggest any tests to troubleshoot.
There's practically no time for the blink sprite to be rendered because you remove it the instant it was added. You'd have to schedule another selector once, ie eyeLidsBlinkOff that runs 0.1 seconds later and hides the blink sprite.
Note: This code is very inefficient. Creating sprites is a relatively slow operation. Instead keep both sprites as children but set one sprite's visible property to NO. While blinking simply flip each sprite's visible flag. This will make the code a lot shorter too.
I am using cocos2d and have 2 AI sprites called TheEvilOne and TheEvilTwo. These 2 sprites call the same bloc of code from the same class sending the sprite as a parameter. However the game gets buggy as I run this code as only one of the sprites preforms the actions and my code stops working. My question is is it possible to call the same bloc of code for multiple sprites simultaneously or is there something wrong with my code. Below is an example of what I am running on my program.
-(void)LoadingEvilActions:(id)sender { //This is Getting Constantly Called
if(loaded == NO) {
theEvilOne = [CCSprite spriteWithFile:#"Evil_Alt_Idle_00.png"]; //These sprites have been declared in my .h file
[self addChild:theEvilOne z:200];
theEvilTwo = [CCSprite spriteWithFile:#"Evil_Alt_Idle_00.png"];
[self addChild:theEvilTwo z:200];
loaded = YES;
}
[self CheckCollision:theEvilOne];
[self setCenterofScreen:theEvilOne];
[self StayonScreen:theEvilOne];
[self AiCharacter:theEvilOne];
[self CheckCollision:theEvilTwo];
[self setCenterofScreen:theEvilTwo];
[self StayonScreen:theEvilTwo];
[self AiCharacter:theEvilTwo];
}
-(void)AiCharacter:(id)sender {
CCSprite *EvilCharacter = (CCSprite *)sender;
if (aiactionrunning == NO){
.... Do More Stuff // For E.G.
id jump_Up = [CCJumpBy actionWithDuration:0.6f position:ccp(randomjump, EvilCharacter.contentSize.height)
height:25 jumps:1];
id jump_Down = [CCJumpBy actionWithDuration:0.42f position:ccp(randomjump,-EvilCharacter.contentSize.height)
height:25 jumps:1];
id seq = [CCSequence actions:jump_Up,jump_Down, [CCCallFuncN actionWithTarget:self selector:#selector(stopAiAction:)], nil];
[EvilCharacter stopAllActions];
[EvilCharacter runAction:seq];
aiactionrunning = YES;
}
}
-(void)stopAiAction:(id)sender {
aiactionrunning = NO;
}
EDIT
I'm running random number generators within my AI Character method and many actions.
The Game already works with just 1 enemy sprite and I decided to try and implement a way to create many.
EDIT 2
I just added the part of the method that stops the sprites being constantly, I was trying to simplify all the code that was irrelevant to my question and forgot to add that part.
I changed the way I am calling my methods like suggested by LearnCocos2d not solving my problem but making it much simpler. I am also not using ARC
One of my sprites is the only one preforming the majority of actions however in some instances the other sprite may preform an actions aswell, but is mainly preformed one sprite. I think my main question is can I call the same method using different sprites passing the sprite as a parameter.
EDIT 3
I have figured out that my problem is there is a Boolean value flag that is enclosing my AiCharacter method, where when one sprite runs through the method it stops the other sprite running the method. Is there some way I can implement an array of records or such so each sprite have their own Boolean flags.
With making this 'array' is it possible to change the Boolean for TheEvilOne and TheEvilTwo using the temp sprite EvilCharacter without doing both separately.
If I understand your question correct you are trying to take one action and run it on more than one character at a time, like:
id move = [CCMoveTo actionWithDuration:0.5f position:ccp(0,0)];
[theEvilOne runAction:move];
[theEvilTwo runAction:move];
That wouldn't work because Evil 1 will not have anything performed on it since it was moved to running on Evil 2. When an action is running it is running on one target. Instead you would:
id move = [CCMoveTo actionWithDuration:0.5f position:ccp(0,0)];
[theEvilOne runAction:move];
[theEvilTwo runAction:[move copy]];
Assuming you are using arc. If not then you'd want to release the copy of course. When it comes to your particular example, why are you trying actions to call methods. Why not just call the method? It seems pointless as LearnCocos2D has pointed out.
Another potential problem you have is that you are constantly creating more and more sprites each time the method is called. Is that on purpose or are there only suppose to be one Evil 1 and one Evil 2? If so then you shouldn't be constantly creating more sprites.
Edit:
Remove your bool. Do something like this instead:
CCSprite *EvilCharacter = (CCSprite *)sender;
if ([EvilCharacter numberOfRunningActions] == 0){
...
}