I have a sprite sheet in which there a four images(bat1,bat2,bat3.bat4) , in Image there is a person with holding the bat and when all images are combined in animation then it looks like playing baseball.
below is the code that i have used to add sprite sheet.
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"baseball.plist"];
spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"baseball.plist"];
[self addChild:spriteSheet];
background = [CCSprite spriteWithSpriteFrameName:#"bat4.png"];
background.position = ccp(220, 185);
background.tag = 10;
[self addChild:background];
for(int i = 1; i < 5; i++) {
[walkAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"bat%d.png", i]]];
}
CCAnimation *walkAnim = [CCAnimation animationWithSpriteFrames:walkAnimFrames delay:5.0f];
self.walkAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:walkAnim]];
[background runAction:_walkAction];
And i am detecting the collision of ball with bat. by below code
for (CCSprite *monster in _monsters) {
if (CGRectIntersectsRect(background.boundingBox, monster.boundingBox)) {
if (((background.position.x -5 > monster.position.x + (monster.contentSize.width/2)) && background.position.y > monster.position.y)) {
isCollision = 1;
[monstersToDelete addObject:monster];
}
// [monstersToDelete addObject:monster];
//[self addFishToBoat];
}
}
here wat happen is collision is detected but it always detect collision with rect of bat4.. as the bat is moving and the cgrect of all bat is different so even when bas is very much far away from bat1 then it will detect the collision because ball's rect will intersect with rect of bat4.
But i want the collision will de detect only when the ball will collide wiith different bats, i mean when bal hit with bat1,bat2,bat3,bat4 then only it will detect the collision rather then detect with bat4 always
Here is great tutorials by raywenderlich on sprites collision detection.
Or
You can try this tutorial also Sprite Collision
Hope these will help you.
You try this code it may help you...
_contactListener = new MyContactListener();
_world->SetContactListener(_contactListener);
// Preload effect
[[SimpleAudioEngine sharedEngine] preloadEffect:#"hahaha.caf"];
std::vector<b2Body *>toDestroy;
std::vector<MyContact>::iterator pos;
for(pos = _contactListener->_contacts.begin();
pos != _contactListener->_contacts.end(); ++pos) {
MyContact contact = *pos;
b2Body *bodyA = contact.fixtureA->GetBody();
b2Body *bodyB = contact.fixtureB->GetBody();
if (bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) {
CCSprite *spriteA = (CCSprite *) bodyA->GetUserData();
CCSprite *spriteB = (CCSprite *) bodyB->GetUserData();
if (spriteA.tag == 1 && spriteB.tag == 2) {
toDestroy.push_back(bodyA);
} else if (spriteA.tag == 2 && spriteB.tag == 1) {
toDestroy.push_back(bodyB);
}
}
}
std::vector<b2Body *>::iterator pos2;
for(pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2) {
b2Body *body = *pos2;
if (body->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *) body->GetUserData();
[_spriteSheet removeChild:sprite cleanup:YES];
}
_world->DestroyBody(body);
}
if (toDestroy.size() > 0) {
[[SimpleAudioEngine sharedEngine] playEffect:#"hahaha.caf"];
}
Fore more detail about this code visit
http://www.raywenderlich.com/606/how-to-use-box2d-for-just-collision-detection-with-cocos2d-iphone
Related
I have a contact listener to detect collisions between a bullet and an asteroid. Inside the beginContact function i check for the sprite types and fire a block from within this function.
The block is set by the scene class when it is allocated, before the contact listener is assigned to the box2dworld. The bullets and asteroids both get box2d bodies as well as sprites.
The problem is that i can access the sprites and do whatever i want with them (run actions, stop actions etc.).
However, as soon as i call the removeChild function on the sprite, I get an EXC_BAD_ACCESS in the b2Contact::Update on listener->BeginContact(this).
Can anyone point me in the right direction?
My tick function:
//Physics simulation
_world->Step(dt, 10, 10);
for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *)b->GetUserData();
b2Vec2 b2Position = b2Vec2(sprite.position.x/PTM_RATIO,
sprite.position.y/PTM_RATIO);
float32 b2Angle = -1 * CC_DEGREES_TO_RADIANS(sprite.rotation);
b->SetTransform(b2Position, b2Angle);
}
}
for(CCSprite *sprite in spritesToDelete) {
[self removeSprite:sprite];
[spritesToDelete removeObject:sprite];
sprite = nil;
}
My contact listener callback block:
contactListener->contactBlock = ^(CCSprite *A, CCSprite *B) {
NSLog(#"A: %#", [A class]);
NSLog(#"B: %#", [B class]);
CCSprite *bullet = nil , *asteroid = nil;
if ([(NSString *)A.userData isEqualToString:#"asteroid"] && [(NSString *)B.userData isEqualToString:#"bullet"])
{
asteroid = A;
bullet = B;
} else if ([(NSString *)B.userData isEqualToString:#"asteroid"] && [(NSString *)A.userData isEqualToString:#"bullet"])
{
asteroid = B;
bullet = A;
}
if (asteroid != nil && bullet != nil) {
NSLog(#"Asteroid Hit!");
[asteroid stopAllActions];
[bullet stopAllActions];
//If I keep the line below uncommented, I get the error. Adding actions and stuff does not make an issue, only removal of sprite is when I get the error.
[spritesToDelete addObject:bullet];
}
};
I guess you put a pointer to your sprite in b2UserData of b2Body or b2Fixure in order to retrieve when collision happens. When you delete the sprite, this pointer will point to a deleted instance and thus you receive EXC_BAD_ACCESS.
You should nullify the pointer to eliminate the problem
I usually use box2d for collision detection between two sprites like below code
- (void)beginContact:(b2Contact *)contact {
b2Fixture *fixtureA = contact->GetFixtureA();
b2Fixture *fixtureB = contact->GetFixtureB();
b2Fixture *fixtureC = contact->GetFixtureA();
b2Body *bodyA = fixtureA->GetBody();
b2Body *bodyB = fixtureB->GetBody();
b2Body *bodyC = fixtureC->GetBody();
CCSprite *spriteA = (CCSprite *) bodyA->GetUserData();
CCSprite *spriteB = (CCSprite *) bodyB->GetUserData()
LHBezierNode* bez = (LHBezierNode *) bodyC->GetUserData();
if ((spriteB == _enemy && spriteB == _hero) ||
(spriteA == _enemy && spriteA == _hero)) {
NSLog(#"enemy touched");
}
}
But I am confused how to detect collision detection between bazier and sprite.any help is appreciated.thanks
This is my bezier
LHBezierNode* myBezier = [LH bezierNodeWithUniqueName:#"BezierName"];
note : i use level helper for bezier.
I have solved it by adding tag to brezier, thanks
Let me explain it in depth, when ever if(CGRectContainsPoint([hole1 boundingBox], ball1.position)) condition goes true, i do lots of stuffs, like unscheduled, a selector, destroying a ball body calling an animation (please refer Code below)etc. This work properly most of the time. But sometimes when ball is really near to hole(just touching the hole but but not enough to make the above condition true), or is been throws towards the hole really fast speed, then application got terminated. I have checked, by commenting many actions which are been performed in this section, but got nothing helpful, application keep terminating when some efforts are been done to make it terminate.
if(CGRectContainsPoint([hole1 boundingBox], ball1.position))
{
ballBody->SetLinearVelocity(b2Vec2(0.0f,0.0f));
ballBody->SetAngularVelocity(0.0f);
[self unschedule:#selector(tick:)];
self.isTouchEnabled = NO;
[self removeChild:ball1 cleanup:YES];
world->DestroyBody(ballBody);
// create the sprite sheet
CCSpriteSheet *spriteSheet;
GolfBallsAppDelegate *appDelegate = (GolfBallsAppDelegate *)[[UIApplication sharedApplication] delegate];
if([appDelegate.ballValue isEqualToString:#"cricketball"])
{
spriteSheet = [CCSpriteSheet spriteSheetWithFile:#"cricket_ball_strip.png"];
}
else if([appDelegate.ballValue isEqualToString:#"ironball"])
{
spriteSheet = [CCSpriteSheet spriteSheetWithFile:#"iron_ball_strip.png"];
}
else if([appDelegate.ballValue isEqualToString:#"golfball"])
{
spriteSheet = [CCSpriteSheet spriteSheetWithFile:#"golf_ball_strip.png"];
}
else if([appDelegate.ballValue isEqualToString:#"soccerball"])
{
spriteSheet = [CCSpriteSheet spriteSheetWithFile:#"soccer_ball_strip.png"];
}
else if([appDelegate.ballValue isEqualToString:#"basketball"])
{
spriteSheet = [CCSpriteSheet spriteSheetWithFile:#"basket_ball_strip.png"];
}
spriteSheet.position = ccp(hole1.position.x,60);
[self addChild:spriteSheet];
float frameWidth = 96;
float frameHeight = 84;
CCSprite *sprite = [CCSprite spriteWithTexture:spriteSheet.texture rect:CGRectMake(0, 0, frameWidth, frameHeight)];
[spriteSheet addChild:sprite];
//if(animation)
{
// create the animation
CCAnimation *spriteAnimation = [CCAnimation animationWithName:#"potting" delay:0.1f];
int frameCount = 0;
for (int x = 0; x < 6; x++)
{
// create an animation frame
CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:spriteSheet.texture rect:CGRectMake(x*frameWidth,0*frameHeight,frameWidth,frameHeight) offset:ccp(0,0)];
[spriteAnimation addFrame:frame];
frameCount++;
// stop looping after we've added 14 frames
if (frameCount == 6)
{
//[self removeChild:spriteSheet cleanup:YES];
break;
}
}
// create the action
CCAnimate *spriteAction = [CCAnimate actionWithAnimation:spriteAnimation];
//CCRepeatForever *repeat = [CCRepeatForever actionWithAction:spriteAction];
// run the action
[sprite runAction:spriteAction];
//[sprite runAction:repeat];
}
[self schedule:#selector(loading) interval:0.5];
[self schedule:#selector(holeFinish) interval:1];
//[self removeChild:spriteSheet cleanup:YES];
}
Any suggestion will be highly appreciated.
EDIT: What i found is, problem with folling lines [self removeChild:ball1 cleanup:YES];
world->DestroyBody(ballBody);
(MAY be). but as it not occurs always, (as I mentioned), thus it's being ridiculous.
I think your problem will be that you are trying to delete a body when the b2World is 'locked', (When the world is busy working out collisions).
Try flagging the object as ready for deletion, and deleting it at the start of your next loop:
Replace:
[self removeChild:ball1 cleanup:YES];
world->DestroyBody(ballBody);
with
ball1.isDead = YES;
And at the start of your next game loop:
for (Ball b in balls)
{
if (b.isDead)
world->DestroyBody(b.ballBody);
}
I'm using Cocos2D 0.99.5. I have a CCParallaxNode with background sprites added to it. For some reason, none of them display until I start my map starts moving around a bit. The scroll slowly with the players movement.
I have this added to init:
winSize = [CCDirector sharedDirector].winSize;
bgMountainsMax = floor(winSize.width/240)+1;
if((int)winSize.width%240 > 0){
bgMountainsBumper = ((int)winSize.width%240)/bgMountainsMax;
}else{
bgMountainsBumper = 0;
}
backgroundNode = [CCParallaxNode node];
[self addChild:backgroundNode z:-1];
CGPoint mountainSpeed = ccp(0.5, 0.5);
bgMountains = [[NSMutableArray alloc] initWithCapacity: bgMountainsMax];
for(int i=0; i<bgMountainsMax; ++i){
mountain = [CCSprite spriteWithFile:#"mountainBG1.png"];
mountain.opacity = 80;
[bgMountains insertObject:mountain atIndex:i];
[backgroundNode addChild:[bgMountains objectAtIndex:i] z:0 parallaxRatio:mountainSpeed positionOffset:ccp((240*i)+(bgMountainsBumper*i),98)];
}
And this added to update:
for (CCSprite *mountainNum in bgMountains) {
if ([backgroundNode convertToWorldSpace:mountainNum.position].x < -(240/2)) {
[backgroundNode incrementOffset:ccp(winSize.width+(240),0) forChild:mountainNum];
}
if ([backgroundNode convertToWorldSpace:mountainNum.position].x > winSize.width+(240/2)) {
[backgroundNode incrementOffset:ccp(-(winSize.width+240),0) forChild:mountainNum];
}
}
I think you should be calling the update function stuff once at the end of your init stuff
[backgroundNode incrementOffset:ccp(winSize.width+(240),0) forChild:mountainNum];
[backgroundNode incrementOffset:ccp(-(winSize.width+240),0) forChild:mountainNum];
without the conditionals, to set it up initially.
am trying to jump the different sprite at randomly .
am having 5 disffrent sprites have to display randomly . one sprite to be displayed
am tryed below code but its crashed :- warning: 'CCSprite' may not respond to '+spriteWithName:'
NSString *Sprit;
NSInteger rnd = arc4random() % 6;
if (rnd == 1) {
Sprit = #"Target.png";
} else if (rnd == 2) {
Sprit = #"3.png";
}else if (rnd == 3) {
Sprit = #"5.png";
} else if (rnd == 4) {
Sprit = #"8.png";
} else if (rnd == 5) {
Sprit = #"10.png";
} else {
Sprit = #"13.png";
}
CCSprite *target = [CCSprite spriteWithName:Sprit];
target.position = ccp(winSize.height + (target.contentSize.height/4), actualX);
[self addChild:target ];
Did you mean to use:
CCSprite *target = [CCSprite spriteWithFile:Sprit];
instead? Note that it's spriteWith*File*
There is some documentation for the CCSprite class here:
http://www.cocos2d-x.org/embedded/cocos2d-x/d4/de7/classcocos2d_1_1_c_c_sprite.html