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
}
}
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 been going through a SpriteKit tutorial that makes a Flappy Bird Style Game. One of the issues I am having, is that it is firing off the code for collision detection incorrectly.
Sometimes, this goes perfect...it hits the ground, it fires the method for when it collides with the ground. However, at seemingly random times, it will hit the ground, and fire off the method for ground collisions anywhere from 2-6 times. It doesn't matter if any other nodes are present on the screen or not. I can sit and let it drop immediately, and sometimes I get the collision code correctly ran once, other times it runs several times. Is there something wrong in this code causing it to do that?
UPDATE: It seems to be where the two objects meet on multiple intersecting points. If object A intersects with object B at 3 points, it will fire 3 times. How do you keep it from doing this?
- (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 & pillerCategory) != 0 &&
(secondBody.categoryBitMask & flappyBirdCategory) != 0)
{
[self pillar:(SKSpriteNode *) firstBody.node didCollideWithBird:(SKSpriteNode *) secondBody.node];
}
else if ((firstBody.categoryBitMask & flappyBirdCategory) != 0 &&
(secondBody.categoryBitMask & bottomBackgroundCategory) != 0)
{
[self flappyBird:(SKSpriteNode *)firstBody.node didCollideWithBottomScoller:(SKSpriteNode *)secondBody.node];
}
}
- (void)pillar:(SKSpriteNode *)pillar didCollideWithBird:(SKSpriteNode *)bird
{
NSLog(#"Did collide with bird");
[self showGameOverLayer];
}
- (void)flappyBird:(SKSpriteNode *)bird didCollideWithBottomScoller:(SKSpriteNode *)bottomBackground
{
NSLog(#"Did collide with scroller");
[self showGameOverLayer];
}
The easiest way i would solve this problem is by using this.
1st
Create a BOOL called running.
BOOL running;
2nd
Set running to YES when the game started
running = YES;
3rd
Place an if statement around your collision code like so,
if(running == YES)
{
//do collision detection
}
else
{
//do nothing
}
You can also use this running bool to control various other useful parts such as your update method.
I can see here how to use the update() function to monitor properties like "position" on an SKNode but I don't see how I would know how methods like [node.physicsBody applyImpulse:vector] has finished.
-(void)someMethod {
_monitorOn = YES;
[_node.physicsBody applyImpulse:CGVectorMake(10,10)];
}
-(void)update:(CFTimeInterval)currentTime {
if( _monitorOn == YES ) {
NSLog(#"node position: %f,%f", _node.position.x, _node.position.y);
}
// When will this be turned off?
}
Here are two ways to check if the effect of applyImpulse is completed:
if (_node.physicsBody.resting) {
// Node is at rest, do something
}
You'll often find that the resting property is never set because your sprite is moving very slowly (particularly with circle nodes). Therefore, it's better to check if the speed is nearly zero.
static inline CGFloat speed(const CGVector v)
{
return sqrtf(v.dx*v.dx+v.dy*v.dy);
}
if (speed(_node.physicsBody.velocity) < kSmallValue) {
// Node is moving very slowly, do something
}
applyImpulse simply adds some velocity to your _node; it only does so when you call it and is "finished" after one frame. I think what you're really looking for is when _node stops moving (which will happen the physics engine determines that _node's velocity is zero). To check for this, you can look at SKPhysicsBody's resting property. Simply check for it in your update: loop; when it's true your _node has stopped.
-(void)update:(CFTimeInterval)currentTime {
if( _monitorOn == YES ) {
NSLog(#"node position: %f,%f", _node.position.x, _node.position.y);
}
if( _node.physicsBody.resting ) {
NSLog(#"node is stopped");
}
}
Note: You'll probably want to set an addition flag somewhere that you can use to see if you should be checking if _node is resting, otherwise you're going to get a ton of "node is stopped" messages.
-(void)someMethod {
_monitorOn = YES;
_appliedImpulse = YES;
[_node.physicsBody applyImpulse:CGVectorMake(10,10)];
}
-(void)update:(CFTimeInterval)currentTime {
if( _monitorOn == YES ) {
NSLog(#"node position: %f,%f", _node.position.x, _node.position.y);
}
if( _appliedImpulse && _node.physicsBody.resting ) {
_appliedImpulse = NO;
NSLog(#"node is stopped");
}
}
I have two collisions in my game that happen when the main characters hits an obstacle, game over appears and one where it touches the ground and nothing happens. This are the codes
-(void)didBeginContact:(SKPhysicsContact *)contact
{
if ([contact.bodyA.node.name isEqualToString:#"ground"] || [contact.bodyB.node.name isEqualToString:#"ground"]) {
[hero land];
} else {
[self gameover];
}
How can I add a different main character/hero collision where it doesn't lead me to game over but a whole different outcome (Like a different reaction for the main character)
Try detecting contact for the other special cases:
-(void)didBeginContact:(SKPhysicsContact *)contact
{
BOOL isContactWithGround = ([contact.bodyA.node.name isEqualToString:#"ground"] || [contact.bodyB.node.name isEqualToString:#"ground"]);
BOOL isContactWithHero = ([contact.bodyA.node.name isEqualToString:#"hero"] || [contact.bodyB.node.name isEqualToString:#"hero"]);
BOOL isContactWithOtherThing = ([contact.bodyA.node.name isEqualToString:#"otherThing"] || [contact.bodyB.node.name isEqualToString:#"otherThing"]);
if (isContactWithHero && isContactWithGround) {
[hero land];
} else if (isContactWithHero && isContactWithOtherThing) {
[self doSomethingElse];
else {
[self gameover];
}
}
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