BOOL method to detect if nodes have been removed from scene - boolean

Im making a brick breaker type game and need to know if all the bricks have been broken in order to transition to the win screen.
The way I've been thinking of solving this is to create a BOOL method that is ran each time a brick is removed to calculate how many bricks are left. If there are no bricks left... move to the win scene.
Im struggling with the logic on how to do this.
So far I have:
-(BOOL)isGameWon{
for (SKNode* node in self.children){
if ([node.name isEqual:brickCategoryName]){
//some logic
}
}
return YES;
}
didBeginContactMethod:
if (notTheBall.categoryBitMask == brickCategory) {
[self runAction:_smashSound];
[notTheBall.node removeFromParent];
if ([self isGameWon]) {
NSLog(#"YOU WIN!");
}
}

Related

Collision Detection with an NSMutable array of Objects

I'm attempting to make a game with objects that bounce between the left and right walls of the screen until they reach the bottom.
I create my objects inside these methods which stores the objects in an NSMutable array and is called when the game begins.
-(void)createContent
{
self.backgroundColor = [SKColor colorWithRed:0.54 green:0.7853 blue:1.0 alpha:1.0];
world = [SKNode node];
[self addChild:world];
crabs = [NSMutableArray new];
[self performSelector:#selector(createCrabs) withObject:nil afterDelay:1];
}
-(void)createCrabs {
crab = [HHCrab crab];
[crabs addObject:crab];
[world addChild:crab];
crab.position = CGPointMake(world.scene.frame.size.width/12 , world.scene.frame.size.height/3.2);
[crab moveLeft];
//Next Spawn
[self runAction:[SKAction sequence:#[
[SKAction waitForDuration:10],
[SKAction performSelector:#selector(createCrabs) onTarget:self],
]]];
}
This will endlessly create new objects, however a problem begins with collision detection. Originally I had my collision detection set up like 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==crabCategory && secondBody.categoryBitMask == leftWallCategory) {
NSLog(#"Crab Hit Left Wall");
[crab stop];
[crab moveRight];
} else if (firstBody.categoryBitMask == crabCategory && secondBody.categoryBitMask == rightWallCategory) {
NSLog(#"Crab Hit Right Wall");
[crab stop];
[crab moveLeft];
}
}
But, after more than one object is on the map, when the original object collides with a wall it begins to glitch and stop moving. This results in a pile up which bugs the new objects being spawned.
I've also tried to use the update CurrentTime method to see if the collision detection would improve, however as predicted, only one object will move at a time while the rest will stay still.
-(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==crabCategory && secondBody.categoryBitMask == leftWallCategory) {
NSLog(#"Crab Hit Left Wall");
self.crabTouchLeftWall = YES;
} else if (firstBody.categoryBitMask == crabCategory && secondBody.categoryBitMask == rightWallCategory) {
NSLog(#"Crab Hit Right Wall");
self.crabTouchRightWall = YES;
}
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
for (HHCrab *crabNode in crabs){
if (self.crabTouchLeftWall){
[crabNode stop];
[crabNode moveRight];
self.crabTouchLeftWall = NO;
}
if (self.crabTouchRightWall){
[crabNode stop];
[crabNode moveLeft];
self.crabTouchRightWall = NO;
}
}
}
How can I fix this so when one object collides with the wall, it does not effect the movement of the other objects, only itself?
There are a couple of suggestions I have.
As you are creating multiple instances of crab and adding them into the crabs array, I suggest you give each crab a unique name property. Your can do this by having a running int counter. For example:
#implementation GameScene {
int nextObjectID;
}
Then when you create a crab instance:
nextObjectID++;
[crab setName:[NSString stringWithFormat:#"crab-%i",nextObjectID]];
I personally prefer coding my didBeginContact like this:
- (void)didBeginContact:(SKPhysicsContact *)contact {
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (crabCategory | leftWallCategory)) {
// figure out which crab in the array made the contact...
for(HHCrab *object in crabs) {
if(([object.name isEqualToString:contact.bodyB.node.name]) || ([object.name isEqualToString:contact.bodyA.node.name])) {
NSLog("I am %#",object.name);
}
}
}
}
Your code self.crabTouchLeftWall = YES; is not the right way to go. You should create a crab class property and set that to YES. Reason being that each crab instance needs to have these properties. The way you currently have it, all crab instances all share the same BOOLs self.crabTouchLeftWall and self.crabTouchRightWall.
You are checking for the BOOL values contacts in the update method and then running [crabNode stop];. You could do this directly when the contact is registered in the didBeginContact method.
The reason I earlier suggested you use unique names for each crab instance is because when it comes time to remove them from your array, you need to be able to point to which specific crab instance needs removing. Having a unique name just makes things easier.
Be aware that moving nodes manually like that can produce odd results. When using physics engine in Spritekit you have to let it to do calculation by itself, meaning you have to move objects by using forces. This not applies for contact detection, and you can move nodes around if you only need contact detection. But if you need more than just contact detection eg. to simulate collisions and restrict nodes from penetrating each other, then I suggest you to use forces appropriately (applyImpulse:, applyForce: are suitable methods).
You could probably solve your issue by moving the code from your update: method to didSimulatePhysics method, but that is not a solution...Because even if you move everything manually after simulation is done, you can make a mistake and for example manually initiate collision by positioning your nodes to overlap each other. So, the advice is, if you are using phyisics use it all, or don't use it at all and move nodes manually or using actions.In the case you decide not to use physics engine, you can handle collisions by using methods like CGRectIntersectsRect and similar.
And to point once again , which is not related to your question, but it can be useful: You can use physics engine for contact detection even if you move your nodes, for example by SKAction methods. Read more here.

How to refer to sprites when using Cocos2d collision detection?

Using the method below, how can one refer to specific sprites when checking to see if they intersect?
- (void)update:(ccTime)dt {
for (CCSprite *sprite in movableSprites) {
if (CGRectIntersectsRect(sprite.boundingBox, sprite.boundingBox)) {
break;
}
}
}
It appears that all sprites are available in the moveableSprites object, but I don't know how to check if specific sprites are colliding... I don't know how to refer to them. If there is an easier way to perform collision detection I'm interested.
It appears your code above will always return TRUE because you are checking if the boundingbox of sprite collides with sprite, and since they are the same it always will.
if (CGRectIntersectsRect(sprite.boundingBox, sprite.boundingBox)) {//
break;
}
Should be comparing to a different sprite not the same sprite.
if (CGRectIntersectsRect(sprite.boundingBox, otherSprite.boundingBox)) {//
break;
}
If that does not answer your question maybe you are looking to avoid enumerating through the array? If that is the case try using tags. Someting like below.
CCSprite *aSprite = [CCSprite spriteWithFile:#"hurdle1.png"];
[self addChild:aSprite tag:2];
Now [self getChildByTag:2] can take the place of sprite
and you can just add boundingBox to check collisions, as below.
if (CGRectIntersectsRect([self getChildByTag:2].boundingBox, checkSprite.boundingBox)) {//
break;
}

cocos2d sprite collision

I have an Array of CCSprites that being displayed all at once.
Every sprite has a movement path, a movement path is a random point on screen.
All the sprites are moving all at once to random points on screen.
What I want to do is to detect collision between the sprites and then change their movement path.
Is it possible?
Iterate through every CCSprite in your array (call it A), and for every iteration iterate again through every CCSprite in the array (excluding A itself of course) (call this one B). Now, use CGRectIntersectsRect along with boundingBox to find a collision between them. It goes something like this:
for (CCSprite *first in mySprites) {
for (CCSprite *second in mySprites) {
if (first != second) {
if (CGRectIntersectsRect([first boundingBox], [second boundingBox])) {
// COLLISION! Do something here.
}
}
}
}
Edit: But of course, it is possible that if two sprites collide, the "collision event" will occur twice (first from the point of view of sprite A, and then from the point of view of sprite B).
If you only want the collision event to trigger once every check, you will need to memorize the pairs so that you can ignore collisions that already did happen on that check.
There are countless ways you could check for that, but here's an example (updated code):
Edited again:
NSMutableArray *pairs = [[NSMutableArray alloc]init];
bool collision;
for (CCSprite *first in mySprites) {
for (CCSprite *second in mySprites) {
if (first != second) {
if (CGRectIntersectsRect([first boundingBox], [second boundingBox])) {
collision = NO;
// A collision has been found.
if ([pairs count] == 0) {
collision = YES;
}else{
for (NSArray *pair in pairs) {
if ([pair containsObject:first] && [pair containsObject:second]) {
// There is already a pair with those two objects! Ignore collision...
}else{
// There are no pairs with those two objects! Add to pairs...
[pairs addObject:[NSArray arrayWithObjects:first,second,nil]];
collision = YES;
}
}
}
if (collision) {
// PUT HERE YOUR COLLISION CODE.
}
}
}
}
}
[pairs release];
Have a look at this S.O. answers.
You can do simple collision detection using CGRectIntersectsRect and the node boundingBox. If you need more advanced features, have a look at a physics engine like chipmunk or Box2D.
Ray Wenderlich has written a good tutorial about using Box2D just for collision detection, if you're interested in going that way. http://www.raywenderlich.com/606/how-to-use-box2d-for-just-collision-detection-with-cocos2d-iphone
Check first that your sprites can be approximated by rectangles. If so then #Omega's answer was great. If they can't be, perhaps because they contain a lot of transparency or for some other reason, you might need to approximate your sprites with polys and work with those.

count the number of time of touches

I have a game and I want that every time an image(image1) touchs another image(image2) a counter count one, if it touches a second time it becomes two. So it's something that counts. How can I do this?
Is this a collision detection? This link may help... collision detection
So you may have to write a collision detection method. Then each time it collide, you increment the count.
- (BOOL)collisionCheck:(UIImage *)image1 withImage:(UIImage *)image2 {
return CGRectIntersectsRect(image1.frame, image2.frame);
}
// then in game loop
// code that move the image here
if ([self collisionCheck:image1 withImage:image2] == YES) {
// code that bounce the image here
count++;
}

Disabling user interaction on CCSprite or CCScene in cocos2d for Iphone

I am developing a game on Iphone using cocos2d. I have a CClayer containing 20 CCSprite. I am playing a sound and I would like to disable the touch events on all the CCSprite or on the entire layer while the sound is playing. I looked at the property of CCLayer called isTouchEnabled but the behavior doesn't propagate to the children (all the CCSprite). Unless it is not documented, there seems to be no equivalent property for CCsprite. Does anybody know an easy way to this?
Thanks
I ma using below method to disable touch on CCMenu items on a layer below on a view may be help you out.
Call the below method and disable all sub Menu or subNode.
[self MenuStatus:NO Node:self]; // to disable
method is:
-(void)MenuStatus:(BOOL)_enable Node:(id)_node
{
for (id result in ((CCNode *)_node).children)
{
if ([result isKindOfClass:[CCMenu class]])
{
for (id result1 in ((CCMenu *)result).children)
{
if ([result1 isKindOfClass:[CCMenuItem class]])
{
((CCMenuItem *)result1).isEnabled = _enable;
}
}
}
else
[self MenuStatus:_enable Node:result];
}
}
[self MenuStatus:YES Node:self]; // to enable**
A member of another forum posted this solution
So all your sprites normally receive touch events? If you know when the sound is playing, you just could have them check that and ignore the touch if the sound is playing. For example, if your sprites implement the CCTargetedTouchDelegate protocol, you could do something like:
- (BOOL)ccTouchBegan:(UITouch*)touch withEvent:(UIEvent*)event {
if (soundIsPlaying) {
return NO; // i.e., the sprite is currently uninterested in the touch
}
// Other checks and behaviour here.
return YES;
}