I am making a game in which when 2 objects (object A and B) collide, the game gets over. Object A's position changes each time update is called. Object B's position is the same. The code is:
[self enumerateChildNodesWithName:#"objA" usingBlock:^(SKNode *node, BOOL *stop) {
if ([objB intersectsNode:node])
{
[node removeFromParent];
[self GameOver];
}
The problem is: I want objA NOT to disappear after the collision. So, for that I removed [node removeFromParent]; , but since update is called again and again. My number of nodes increases and the sound that i have added never seems to end. So, what i tried was adding:
[self performSelector:#selector(pauseGame) withObject:Nil afterDelay:0.1];
-(void) pauseGame
{
self.scene.view.paused = YES;
[self performSelector:#selector(gameOver) withObject:Nil afterDelay:0.1];
}
I had to use performSelector with delay, because putting self.scene.view.paused = YES; within the update wouldn't allow me to go to gameOver. However, I do not want any delays! is there a way to do this??
Thanks
Just use a state variable in your scene which will indicate the current state.
Use this state to run updates on your scene objects.
For example (pseudocode) :
update()
if (state == GAME_PLAY) {
// Update relevant game nodes
} else if (state == GAME_OVER) {
// Update only what needs to be updated when the game is over
}
This way you do not need to stop your entire scene and add only what is relevant to the current game state
Related
I used profiler and detect that after I go back from view controller that contains game scene view (for example pop back to the main menu). The game scene is still in the memory.
I supposed it's connected to some SKAction I use. How track which object cause that issue.
I use some SKAction run block, repeat forever and etc, and I am sure that the something happen with it.
You cannot delete a SKScene. I would recommend creating a function for when your present the scene. Here is and example
(Swift)
func deleteView(deleteEveryThing:Bool) {
if deleteEveryThing {
self.removeAllActions()
self.removeAllChildren()
//Scene presentation code here
}
else {
self.removeAllChildren()
//Scene Presentation Code here
}
}
(Objective - C)
-(void)deletView:(BOOL)deleteEveryThing {
if (deleteEveryThing) {
[self removeAllNodes];
[self removeAllActions];
}
else {
[self removeAllNodes];
[self removeAllActions];
}
}
So what I did was create a function called deleteView and deleteView has a parameter that is a Boolean (True or false) and if it is trure then it will remove all actions : self.removeAllActions() or [self removeAllActions]; and all children: self.removeAllChildren() or [self removeAllChildren];in the SKScene. This can help in freeing up memory and once those two lines of code ran then you can handle the scene presentation code. There is also an else part which does the same thing but leaves the SKActions in the SKScene.
In the game I'm making I have a heart count at the bottom. The idea is to remove one heart when the user touches a "bomb". Right now, I create all hearts at the beginning of the program and name each one "heart1" "heart2" and "heart3". In the doDamage method, add 1 to the hitCount when the user touches a bomb. Then I proceed to remove the heart. I have this code right now. Is there any better and more efficient solution for this? Heere's the code I'm using right now.
- (void) doDamage {
//Sets the isDamaged value to yes and adds a hit point
isDamaged = YES;
hitCount++;
NSLog(#"The hit count is %i ", hitCount);
//calls the damageDone method after a second to avoid being damaged all the frames the bomb touches the basket
[self performSelector:#selector(damageDone) withObject:nil afterDelay:0.5];
//This lengthy code is used to test what heart should be removed each time...
[self enumerateChildNodesWithName:#"heart1" usingBlock:^(SKNode *node, BOOL *stop){
//If the hitCount is the same as the heart we want to remove, then we'll remove it from the parent
if (hitCount == 1) {
[node removeFromParent];
}
}];
[self enumerateChildNodesWithName:#"heart2" usingBlock:^(SKNode *node, BOOL *stop){
if (hitCount == 2) {
[node removeFromParent];
}
}];
[self enumerateChildNodesWithName:#"heart3" usingBlock:^(SKNode *node, BOOL *stop){
if (hitCount == 3) {
[node removeFromParent];
}
}];
}
I would suggest hiding the heart nodes instead of removing them, because then you can bring them back anytime (powerups or something).
SKNode *heartNode;
switch (hitCount)
{
case 1:
heartNode = [self childNodeWithName:#"heart1"];
break;
case 2:
heartNode = [self childNodeWithName:#"heart2"];
break;
case 3:
heartNode = [self childNodeWithName:#"heart3"];
break;
}
if (heartNode)
{
[heartNode setHidden:TRUE];
}
Appreciate this already has an accepted answer, but if code speed is important wouldn't this be quicker...
SKNode *heartNode = [self childNodeWithName:[NSString stringWithFormat:#"heart%d",hitCount]];
[heartNode setHidden:TRUE];
If heartNode is nil then the setHidden does nothing
Just not sure if doing stringWithFormat is quicker than a switch statement
I have a sprite-kit game where I need to be checking often to see if the player has lost
- (void)update:(NSTimeInterval)currentTime
{
for (SKSpriteNode *sprite in self.alienArray ) {
if (sprite.position.y < 10) {
LostScene *lostScene = [[LostScene alloc] initWithSize: CGSizeMake(CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds))];
NSLog(#"about to present");
[self.view presentScene:lostScene transition:[SKTransition fadeWithDuration:0.5]];
}
}
}
but when this method gets called (which I know is happening), no scene presents. What am I doing wrong? I believe it has something to do with the transition, because when I take it out, it works fine
You should add a property of the existing scene like: BOOL playerHasLost and edit your update method:
- (void)update:(NSTimeInterval)currentTime
{
if(!playerHasLost)
{
for (SKSpriteNode *sprite in self.alienArray )
{
if (sprite.position.y < 10)
{
playerHasLost = YES;
LostScene *lostScene = [[LostScene alloc] initWithSize: CGSizeMake(CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds))];
NSLog(#"about to present");
[self.view presentScene:lostScene transition:[SKTransition fadeWithDuration:0.5]];
break;//to get out of for loop
}
}
}
}
So this way as soon as the sprite get to position that you treat as lost position it will set the variable to YES, present the scene and it will not do it again.
The reason is simply that the update method gets called every frame, with a running transition the scene will be presented anew every frame and thus it appears as if nithing is happening. You should see the NSLog spamming the log console.
In my app I've use the particle designer to produce an animation when bodies collide,
the code is....
-(void)collision
{
for (IceCream *iceCream in [iceDict allValues]) {
if (CGRectIntersectsRect(iceCream.sprite.boundingBox, player.boundingBox)) {
if(actualType == 0)
{
[self increaseLooseCounts];
[self updateLives];
}
else{
[self increaseWinCounts];
[self updateLives];
}
//DEFINING THE PARTICLE ANIMATION
particle = [CCParticleSystemQuad particleWithFile:#"part.plist"]; //alt plist working with rainbow.plist
particle.position = ccp(iceCream.sprite.boundingBox.origin.x,iceCream.sprite.boundingBox.origin.y);
[self addChild:particle z:2];
[particle release];
//CALLING SELECTOR TO END THE PARTICLE ANIMATION
[self performSelector:#selector(killBlast) withObject:nil afterDelay:0.3f];
int icecreamKey = iceCream.sprite.tag;
[self removeChild:iceCream.sprite cleanup:YES];
[iceDict removeObjectForKey:[NSNumber numberWithInt:icecreamKey]];
}
}
}
-(void)killBlast{
[particle removeFromParentAndCleanup:YES];
}
But as soon as the killBlast is called the app crashes.
Please help !!
Remove this line:
[particle release];
You don't own particle object so you don't need to release it. You get it from autoreleased method and then it's retained when becomes added as a child. It will be released once it's removed from parent
I instance a sprite, and then when it collides with a second sprite, the child of that sprite is removed:
if (CGRectIntersectsRect(spriteOne.boundingBox, self.swat.boundingBox))
{
if (spriteOne.tag == 0){
[self removeChild:spriteOne cleanup:YES];
}
if (spriteOne.tag == 1){
[self removeChild:spriteOne cleanup:YES];
}
}
This works how I want it to, and the sprite disappears off the screen. However, it seems that the boundingBox is still there, even if the image is not, and this causes trouble with scoring, etc... So, what I would like to know is how to 'dis-activate' the sprite's boundingBox so that when the first time that the two sprites colide, the collision is detected, but any time after that, it is not.
Thanks in advance.
As far as I understand, you should have some issue with removing the sprite after a collision.
Would you try this?
if (CGRectIntersectsRect(spriteOne.boundingBox, self.swat.boundingBox))
{
if (spriteOne.tag == 0){
[spriteOne removeFromParentAndleanup:YES];
}
if (spriteOne.tag == 1){
[spriteOne removeFromParentAndleanup:YES];
}
}
Have you tried adding some NSLog traces to see whether the sprite is really removed?
You must be retaining spriteOne. If there is a good reason to keep it around, do this:
if ( spriteOne.visible && CGRectIntersectsRect(spriteOne.boundingBox, self.swat.boundingBox))
{
if (spriteOne.tag == 0){
spriteOne.visible=NO;
}
if (spriteOne.visible && spriteOne.tag == 1){
spriteOne.visible=NO;
}
}
Later, when you need spriteOne in play again, just set its visibility to YES;
If not, you have a leak, and do this:
if ( spriteOne && CGRectIntersectsRect(spriteOne.boundingBox, self.swat.boundingBox))
{
if (spriteOne.tag == 0){
[self removeChild:spriteOne cleanup:YES];
self.spriteOne=nil; // assumes you have a property for spriteOne
}
if (spriteOne && spriteOne.tag == 1){
[self removeChild:spriteOne cleanup:YES];
[spriteOne release]; // assumes no property for spriteOne
spriteOne=nil; // dont forget this ! beware of zombies
}
}