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
Related
I'm trying to set alpha of a SKNode every time it is clicked either to 0 or 1. My code currently turns it off but won't turn it back on. Any idea why?
- (void)handleTouchedPoint:(CGPoint)touchedPoint {
touchedNode = [self nodeAtPoint:touchedPoint];
// Detects which node was touched by utilizing names.
if ([touchedNode.name isEqualToString:#"play"]) {
isOnPlay = true;
NSLog(#"Touched play");
}
if ([touchedNode.name isEqualToString:#"light1"]) {
//NSLog(#"%.2f", touchedNode.alpha);
if(touchedNode.alpha != 0.0)
{
NSLog(#"Off");
touchedNode.alpha = 0.0;
//[touchedNode setAlpha:0.0];
}
else{
NSLog(#"On");
touchedNode.alpha = 1.0;
//[touchedNode setAlpha:1.0];
}
NSLog(#"Touched light");
}
}
You might be running into the famous float rounding issue. Use debug and check the values. The alpha might not be exactly zero.
I have a Cat node, and a Bird node. The bird nodes are nested together in a container node called a birdBlock. Everything is contained in a WorldNode. If I add a bird to the WorldNode, the Cat can interact with it appropriately, but when the birds are in the birdBlock, the Cat just shoves them out of the way and they go flying.
I am using the following to find my birds:
[worldNode enumerateChildNodesWithName:kBirdName usingBlock:^(SKNode *node, BOOL *stop)
{
SKSpriteNode *newBird = (SKSpriteNode *)node;
if (CGRectIntersectsRect(newBird.frame,cat.frame))
{
//Do Something
//This is never reached when the birds are in the SKSpriteNode birdBlock.
//They just get shoved all over the screen.
}
}];
The birds in the block have the correct name.
They are now being enumerated, but still do not interact with the cat other than flying around the screen.
Now I am doing this:
[[worldNode children] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
SKSpriteNode *blockNode = (SKSpriteNode *)obj;
if ([blockNode.name isEqualToString:kBirdBlockName])
{
[blockNode enumerateChildNodesWithName:kBirdName usingBlock:^(SKNode *node, BOOL *stop)
{
SKSpriteNode *nbird = (SKSpriteNode *)node;
NSLog(#"FOUND BIRDS HERE");
//THIS IS FOUND! But below still does not work
if (CGRectIntersectsRect(nbird.frame, cat.frame))
{
NSLog(#"Hit BIRD");
[nbird removeFromParent];
}
}
}
}];
So this does not work either. How do you change the coordinate system of a sprite?
You can search the node tree in many ways. The SKNode documentation explains it clearly under the section Searching the Node Tree.
If you have a node named #"node" then you can search through all descendants by putting a // before the name. So you would search for #"//node".
Try changing your constant kBirdName to equal #"//my_bird_name"
An example using a string literal would be -
[worldNode enumerateChildNodesWithName:#"//nodeName" usingBlock:^(SKNode *node, BOOL *stop)
{ // Do stuff here... }
I could not get this to work, so I just gave all the birds a physicsBody, and moved the detection to didBeginContact
Now it does not matter what their parent is and I don't need to worry about changing coordinates.
- (void)didBeginContact:(SKPhysicsContact *)contact
{
SKSpriteNode *firstNode = (SKSpriteNode *)contact.bodyA.node;
SKSpriteNode *secondNode = (SKSpriteNode *) contact.bodyB.node;
if ((firstNode.physicsBody.categoryBitMask == catCategory && secondNode.physicsBody.categoryBitMask == birdCategory) || (firstNode.physicsBody.categoryBitMask == birdCategory && secondNode.physicsBody.categoryBitMask == catCategory))
{
NSLog(#"DID HIT BIRD");
if (firstNode.physicsBody.categoryBitMask == catCategory) {
//do something to secondNode.
}
if (firstNode.physicsBody.categoryBitMask == birdCategory) {
//do something to firstNode.
}
}
}
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
I am new to objective C, and I am trying to get into speed by doing some tutorial adding my own bit to it. I am working now with Genie effect from Ciechan / BCGenieEffect. I think I understand the implementation since it seems to work for me, however I have a question, How could I trigger a segue after the end of the animation.
The code i am using is as follow:
- (void) genieToRect: (CGRect)rect edge: (BCRectEdge) edge
{
NSTimeInterval duration = 0.97;
CGRect endRect = CGRectInset(rect, 5.0, 5.0);
[self.buttons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) {
button.enabled = NO;
}];
if (self.viewIsIn) {
[self.draggedView genieOutTransitionWithDuration:duration startRect:endRect startEdge:edge completion:^{
self.draggedView.userInteractionEnabled = YES;
[self.buttons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) {
button.enabled = YES;
}];
}];
} else {
self.draggedView.userInteractionEnabled = NO;
[self.draggedView genieInTransitionWithDuration:duration destinationRect:endRect destinationEdge:edge completion:
^{
[self.buttons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) {
button.enabled = YES;
}];}];
}
self.viewIsIn = ! self.viewIsIn;
}
I put a button to call the animation:
- (IBAction)leftButtonTapped:(UIButton *)sender
{
[self genieToRect:sender.frame edge:BCRectEdgeTop];
}
Everything is working fine there, but now, I would like, when the animation ends, to call a segue to move to the next view (like Apple is doing in mail after you click for instance Trash - Once the mail arrive to the trash, the view change)
I tried to call a method inside the button method like this:
[leftButton addTarget:self action:#selector(prepareForSegue:sender:) forControlEvents:UIControlEventTouchUpInside];
Then I added my method like this:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
[self performSegueWithIdentifier:#"public" sender:self];
}
Unfortunately, it does not work...
Thank you in advance for any help.
performSegueWithIdentifier:sender will call prepareForSegue:identifier. If you have something to do there (like passing information to the next ViewController at the end of the segue), you put your code in prepareForSegue:identifier.
So, remove performSegueWithIdentifier:sender in prepareForSegue:identifier.
To perform the segue after the animation, put it in the block. Here, you have ...completion:^{}. You put there what you want to do a the completion. So call here performSegueWithIdentifier:sender.
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