CCSprite runAction and Remove - objective-c

I have a ccLayer where I'm trying to make it rain.
On init i schedule the following:
[self schedule:#selector(throwRain) interval:0.1f];
And here is the rest of the code:
-(void) throwRain {
CCSprite *gota;
for (int i = 1; i <= 6; i++){
gota = [CCSprite spriteWithFile:#"4_gota.png"];
gota.position = ccp(arc4random() % 768, 1060);
gota.scale = (arc4random () % 25 + 50.0f) / 100.0f;
gota.rotation = 35 ;
[self addChild:gota z:arc4random() % 5 + 7];
[gota runAction:[CCSequence actions:[CCEaseRateAction actionWithAction:[CCMoveTo actionWithDuration:3.0f + (arc4random() % 200) / 100.0f position:ccp(gota.position.x, 0)] rate:3] , [CCCallFunc actionWithTarget:self selector:#selector(spriteDone:)], nil]];
}
}
-(void) spriteDone:(id)sender {
[self removeChild:sender cleanup:YES];
}
However, the drops gets to the bottom and just stays there, and never gets removed. Any idea?
Thanks.

Try changing from CCCallFunc to CCCallFuncN. The 'N' stands for Node, and will pass the Node that is performing the action to the selector.

Related

Sprite Kit Objective C

I am creating a game using sprite kit but I seem to have trouble with the bodyWithTexture when using it with collisions. bodyWithRectangle and circleOfRadius work fine, but when i use bodyWithTexture it looks like the didBeginContact method is being called more than once.
here is an example of the code i'm using
-(SKNode *) createPlayer
{
level3Player = [SKNode node];
player3Sprite = [SKSpriteNode spriteNodeWithImageNamed:#"character.png"];
level3Player.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:player3Sprite.size.width/2];
level3Player.physicsBody.categoryBitMask = playerCategory3;
level3Player.physicsBody.contactTestBitMask = platformCategory3 | rockCategory;
[level3Player setPosition:CGPointMake(self.size.height/2, screenHeightL3 *11)];
level3Player.physicsBody.affectedByGravity = NO;
player3Sprite.physicsBody.dynamic = YES;
level3Player.zPosition = 2;
[level3Player setScale:0.6];
[level3Player addChild:player3Sprite];
return level3Player;
}
-(void) addRocksL3
{
int randomNumber = arc4random_uniform(300);
rock1 = [SKSpriteNode spriteNodeWithImageNamed:#"AsteroidFire.png"];
rock1.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:rock1.size.width/2];
rock1.position = CGPointMake(self.size.width * 3, randomNumber);
rock1.physicsBody.categoryBitMask = rockCategory;
rock1.physicsBody.contactTestBitMask = playerCategory3;
rock1.physicsBody.dynamic = NO;
rock1.physicsBody.affectedByGravity = NO;
rock1.zPosition = 2;
[rock1 setScale:0.3];
[foregroundLayerL3 addChild:rock1];
[self addChild:rock1];
}
-(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((secondBody.categoryBitMask == platformCategory3) | redPlatformCategory)
{
level3Player.physicsBody.velocity = CGVectorMake(0, 100);
level3Player.physicsBody.affectedByGravity = YES;
player3Sprite.texture = [SKTexture textureWithImageNamed:#"goo5.png"];
SKAction *sound1 = [SKAction playSoundFileNamed:#"squish.wav" waitForCompletion:NO];
[self runAction:sound1];
gestureRec3.enabled = YES;
}
if(secondBody.categoryBitMask == rockCategory)
{
gestureRec3.enabled = YES;
playerL3.physicsBody.velocity = CGVectorMake(0, 200);
SKAction *playSound = [SKAction playSoundFileNamed:#"Hurt.wav" waitForCompletion:NO];
[self runAction:playSound];
hitCountL3++;
}
switch (hitCountL3)
{
case 1:
[health1Level3 removeFromParent];
[self healthNodelevel31];
break;
case 2:
[hit1L3 removeFromParent];
[self healthNodeLevel32];
break;
case 3:
[hit2L3 removeFromParent];
player3Sprite.texture = [SKTexture textureWithImageNamed:#"splat.png"];
[self gameOverSplatLevel3];
didDie3 = true;
SKAction *playSplat = [SKAction playSoundFileNamed:#"splat.wav" waitForCompletion:NO];
[self runAction:playSplat];
break;
}
when i use this code my character will sometimes take 1 hit and sometimes take all 3 hits when i collide with the rock. I could use circleOfRadius which works fine, but it's not what I am really looking for. Is there anyway i could use bodyWithTexture so my character only takes 1 hit each time?
If you are experiencing multiple collisions eg. didBeginContact is called multiple times, you have few options...
Without looking at your code, lets say you have a player and a rock. Each time when player collides with rock you want to remove the rock. So, you remove the rock from its parent. But before that, you make this change in your code (pseudo code):
if([rockNode parent]){
[rockNode removeFromParent];
}
The other way would be to subclass SKSpriteNode and make a Rock class and to make a custom boolean property which will change its value once when first collision happens. But this is just unnecessary complication:
if(rockNode.canCollide){
[rockNode removeFromParent];
rockNode.canCollide = NO;
}
I had many problems with correct amount of collisions. Sometimes it would get one, sometimes none. So I tried this and it works. The only thing to change is in didBeginContact method.
I will presume that you declared categories like this:
//define collision categories
static const uint32_t category1 = 0x1 << 0;
static const uint32_t category2 = 0x1 << 1;
static const uint32_t category3 = 0x1 << 2;
Try to replace your code in didBeginContact with this one. I remember that correct collisions finally got to work after I did this.
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKNode *newFirstBody = contact.bodyA.node;
SKNode *newSecondBody = contact.bodyB.node;
uint32_t collision = newFirstBody.physicsBody.categoryBitMask | newSecondBody.physicsBody.categoryBitMask;
if (collision == (category1 | category2))
{
NSLog(#"hit");
}
}
Hope it helps

Deleting one object at a time every 5 seconds Cocos2d

I have a problem with my cocos2d game. I am trying to delete a projectile shot by an enemy every 5 seconds (each projectile is supposed to have a lifetime of 5 seconds), but I cannot figure out how to do it. I get the error of
Assertion failure in -[CCTimer initWithTarget:selector:interval:]
Here is my code:
-(void)projectileShooting:(ccTime)dt
{
[self schedule:#selector(projectileShooting:) interval:2.5];
projcount++;
if([proj count] <= 15 ){
if(enemy1.position.y < 320){
v = ccp(player.position.x,player.position.y);
for(CCSprite *enemies in enemy){
CCSprite * projectilebullet = [CCSprite spriteWithFile:#"Projectile.png"];
[proj addObject:projectilebullet];
[self addChild:projectilebullet];
CGPoint MyVector = ccpSub(enemies.position,player.position );
MyVector = ccpNormalize(MyVector);
MyVector = ccpMult(MyVector, enemies.contentSize.width/2);
MyVector = ccpMult(MyVector,-1);
projectilebullet.position = ccpAdd(enemies.position, MyVector);
for(CCSprite *projectile in proj){
[self schedule:#selector (deleteprojectile:projectile:) interval:5];
}
}
}
}
for(CCSprite *enem2 in enemytwo)
{
if( [proj count] <= 15){
CCSprite * projectilebull = [CCSprite spriteWithFile:#"Projectile.png"];
CGPoint MyVector = ccpSub(enem2.position,player.position );
MyVector = ccpNormalize(MyVector);
MyVector = ccpMult(MyVector, enem2.contentSize.width/2+10);
MyVector = ccpMult(MyVector,-1);
projectilebull.position = ccpAdd(enem2.position, MyVector);
[self addChild:projectilebull];
[proj addObject:projectilebull];
for(CCSprite *projectile in proj){
}
}
}
}
-(void)deleteprojectile:(CCSprite *)protime:(ccTime)dt{
NSMutableArray *timepro = [[NSMutableArray alloc]init];
[timepro addObject:protime];
for(CCSprite *objecttime in timepro){
[proj removeObject:objecttime];
[self removeChild:objecttime cleanup:YES];
}
}
It's a bit of a hack but this is what I use in my program, until I find a more elegant solution. I have a method in my game layer that I call to remove a node from its parent, like so:
-(void)removeNode:(CCNode*)node {
[node removeFromParentAndCleanup:YES];
}
And when I want to schedule a node for deletion after a delay, I call it like this:
[self performSelector:#selector(removeNode:) withObject:node afterDelay:delay];
Simple, and it works.
Change argument name in your selector to protime instead of projectile. The selector must match the signature defined in you object's class definition.
Your selector was not defined properly and probably the Timer is checking if the object implements the given selector.
I did not have time to test it so thanks to #RamyAlZuhouri for confirming.

How do I run a method when an action is finished

I was wondering how to call a method after a CCAction is finished (I would also like to call it every 2.5 seconds after it is first called). I have a sprite that goes to a random position, and I want it to run this method (a shooting bullet one) after it is done moving to its random position. So far the method is called when it is still moving. Can anyone help?
Here is the enemy create method:
(void)enemy1{
gjk= arc4random()%6;
enemy1 = [CCSprite spriteWithFile:#"enemy1.png"];
int d = arc4random()%480+480;
int o = arc4random()%320+320;
x = arc4random()%480;
if( x <= 480 && x>= 460){
x=x-100;
}
if(x <= 100){
x = x+50;
}
y = arc4random()%320;
if(y <=320 && y >= 290){
y = y-100;
}
if(y < 100){
y = y + 100;
}
enemy1.position = ccp(o,d);
[enemy1 runAction:[CCMoveTo actionWithDuration:3 position: ccp(x,y)]];
CCRotateBy *rotation = [CCRotateBy actionWithDuration:20 angle:1080];
CCRepeatForever * repeatforever = [CCRepeatForever actionWithAction:rotation];
[enemy1 runAction:repeatforever];
[self addChild:enemy1];
[enemy addObject :enemy1];
}
And the method for shooting a projectile:
(void)projectileShooting:(ccTime)dt {
projcount++;
if(enemy1.position.y < 320){
v = ccp(player.position.x,player.position.y);
for(CCSprite *enemies in enemy){
CCSprite * projectilebullet = [CCSprite spriteWithFile:#"Projectile.png"];
[proj addObject:projectilebullet];
[self addChild:projectilebullet];
CGPoint MyVector = ccpSub(enemies.position,player.position );
MyVector = ccpNormalize(MyVector);
MyVector = ccpMult(MyVector, enemies.contentSize.width/2);
MyVector = ccpMult(MyVector,-1);
projectilebullet.position = ccpAdd(enemies.position, MyVector);
}
}
The shooting method is called in the init method by the code, so it is called every 2.5 seconds.
[self schedule:#selector(projectileShooting:) interval:2.5];
I know I tried to make the shooting happen by making it so that is shoots when the y position is < 320, but it is still moving when it passes the position of 320.
You can make a sequence of actions and at the end of sequence you can give a callback function which will be executed at the end. Something like this
[CCSequence actions:
[CCMoveTo actionWithDuration:3 position: ccp(x,y)],
[CCCallFunc actionWithTarget:self selector:#selector(methodToRunAfterAction)],
nil]];
Adding on to Tayyab's answer, you can simply start the actual scheduling of your projectile firing within the startShooting method (or whatever you want to call it) which is fired at the end of your move action:
[CCSequence actions:
[CCMoveTo actionWithDuration:3 position: ccp(x,y)],
[CCCallFunc actionWithTarget:self selector:#selector(startShooting)],
nil]];
where startShooting is defined as follows:
- (void) startShooting
{
// start scheduling your projectile to fire every 2.5 seconds
[self schedule:#selector(projectileShooting:) interval:2.5];
}

schedule update issue

This might sound pretty straightforward. I've created a method and I've called it as below in the init method.
[self createNewSpr:ccp(s.width * 0.25,s.height-200)];
[self createNewSpr:ccp(s.width * 0.50,s.height-200)];
[self createNewSpr:ccp(s.width * 0.75,s.height-200)];
[self scheduleUpdate];
I've defined a for loop in my update method that imposes a gravity higher than that of the world on the sprites. Only the last call is affected by the new gravity but the first and second act on the world gravity. I am not sure what is wrong but I suspect it to be the scheduleUpdate. Please Help.
Edit: Update Method :
-(void) update: (ccTime) dt
{
int32 velocityIterations = 8;
int32 positionIterations = 1;
world->Step(dt, velocityIterations, positionIterations);
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
if (b == sprite)
{
b->ApplyForce( b2Vec2(0.0,20*b->GetMass()),b->GetWorldCenter());
}
}
}
the createNewSpr:
-(void) createNewSpr:(CGPoint)pos {
//CGSize s = [CCDirector sharedDirector].winSize;
b2Vec2 startPos = [self toMeters:pos];
CGFloat linkHeight = 0.24;
CGFloat linkWidth = 0.1;
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position = startPos;
b2FixtureDef fixtureDef;
fixtureDef.density = 0.1;
b2PolygonShape polygonShape;
polygonShape.SetAsBox(linkWidth,linkHeight);
fixtureDef.shape = &polygonShape;
//first
b2Body* link = world->CreateBody( &bodyDef );
link->CreateFixture( &fixtureDef );
PhysicsSprite* segmentSprite = [PhysicsSprite spriteWithFile:#"sg.png"];
[self addChild:segmentSprite];
[segmentSprite setPhysicsBody:link];
b2RevoluteJointDef revoluteJointDef;
revoluteJointDef.localAnchorA.Set( 0, linkHeight);
revoluteJointDef.localAnchorB.Set( 0, -linkHeight);
for (int i = 0; i < 10; i++) {
b2Body* newLink = world->CreateBody( &bodyDef );
newLink->CreateFixture( &fixtureDef );
PhysicsSprite* segmentSprite = [PhysicsSprite spriteWithFile:#"sg.png"];
[self addChild:segmentSprite];
[segmentSprite setPhysicsBody:link];
revoluteJointDef.bodyA = link;
revoluteJointDef.bodyB = newLink;
world->CreateJoint( &revoluteJointDef );
link = newLink;//next iteration
}
PhysicsSprite* circleBodySprite = [PhysicsSprite spriteWithFile:#"cb.png"];
[self addChild:circleBodySprite z:1];
b2CircleShape circleShape;
circleShape.m_radius = circleBodySprite.contentSize.width/2 / PTM_RATIO;
fixtureDef.shape = &circleShape;
b2Body* chainBase =world->CreateBody( &bodyDef );
chainBase->CreateFixture( &fixtureDef );
[circleBodySprite setPhysicsBody:chainBase];
sprite = chainBase;
revoluteJointDef.bodyA = link;
revoluteJointDef.bodyB = chainBase;
revoluteJointDef.localAnchorA.Set(0,linkWidth);
revoluteJointDef.localAnchorB.Set(0,linkWidth);
world->CreateJoint( &revoluteJointDef );
}
The problem is with createNewSpr method...
You have assigned sprite to a body... So When you call this method three times, sprite refers to 3rd object only..
When you compare in update method, it just puts gravity on 3rd object...
Hope this helps.. :)

About the fate of malloc()ed arrays of arrays

my first question on Stackoverflow.
Let me start with a bit of code. It's a bit repetitive so I'm going to cut out the parts I repeat for different arrays (feel free to ask for the others). However, please ignore the code in preference to answering the Qs at the bottom. Firstly: thank you to answerers in advance. Secondly: the freeing of data.
#implementation ES1Renderer
GLfloat **helixVertices;
GLushort **helixIndices;
GLubyte **helixColors;
- (void)freeEverything
{
if (helixVertices != NULL)
{
for (int i=0; i < alphasToFree / 30 + 1; i++)
free(helixVertices[i]);
free(helixVertices);
}
if (helixIndices != NULL)
{
for (int i=0; i < alphasToFree / 30 + 1; i++)
free(helixIndices[i]);
free(helixIndices);
}
if (helixColors != NULL)
{
for (int i=0; i < alphasToFree / 30 + 1; i++)
free(helixColors[i]);
free(helixColors);
}
}
(I will get to the calling of this in a moment). Now for where I malloc() the arrays.
- (void)askForVertexInformation
{
int nrows = self.helper.numberOfAtoms / 300;
int mrows = [self.helper.bonds count] / 300;
int alphaCarbonRows = [self.helper.alphaCarbons count] / 30;
helixVertices = malloc(alphaCarbonRows * sizeof(GLfloat *) + 1);
helixIndices = malloc(alphaCarbonRows * sizeof(GLfloat *) + 1);
helixColors = malloc(alphaCarbonRows * sizeof(GLfloat *) + 1);
for (int i=0; i < alphaCarbonRows + 1; i++)
{
helixVertices[i] = malloc(sizeof(helixVertices) * HELIX_VERTEX_COUNT * 3 * 33);
helixIndices[i] = malloc(sizeof(helixIndices) * HELIX_INDEX_COUNT * 2 * 3 * 33);
helixColors[i] = malloc(sizeof(helixColors) * HELIX_VERTEX_COUNT * 4 * 33);
}
[self.helper recolourVerticesInAtomRange:NSMakeRange(0, [self.helper.alphaCarbons count]) withColouringType:CMolColouringTypeCartoonBlue forMasterColorArray:helixColors forNumberOfVertices:HELIX_VERTEX_COUNT difference:30];
self.atomsToFree = self.helper.numberOfAtoms;
self.bondsToFree = [self.helper.bonds count];
self.alphasToFree = [self.helper.alphaCarbons count];
}
Finally, the bit which calls everything (this is a separate class.)
- (void)loadPDB:(NSString *)pdbToLoad
{
if (!self.loading)
{
[self performSelectorOnMainThread:#selector(stopAnimation) withObject:nil waitUntilDone:YES];
[self.renderer freeEverything];
[renderer release];
ES1Renderer *newRenderer = [[ES1Renderer alloc] init];
renderer = [newRenderer retain];
[self performSelectorOnMainThread:#selector(stopAnimation) withObject:nil waitUntilDone:YES]; // need to stop the new renderer animating too!
[self.renderer setDelegate:self];
[self.renderer setupCamera];
self.renderer.pdb = nil;
[renderer resizeFromLayer:(CAEAGLLayer*)self.layer];
[newRenderer release];
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(setup:) object:pdbToLoad];
[self.queue addOperation:invocationOperation];
[invocationOperation release];
}
}
- (void)setup:(NSString *)pdbToLoad
{
self.loading = YES;
[helper release];
[renderer.helper release];
PDBHelper *aHelper = [[PDBHelper alloc] initWithContentsOfFile:pdbToLoad];
helper = [aHelper retain];
renderer.helper = [aHelper retain];
[aHelper release];
if (!resized)
{
[self.helper resizeVertices:11];
resized = YES;
}
self.renderer.helper = self.helper;
[self.helper setUpAtoms];
[self.helper setUpBonds];
if (self.helper.numberOfAtoms > 0)
[self.renderer askForVertexInformation];
else
{
// LOG ME PLEASE.
}
[self performSelectorOnMainThread:#selector(removeProgressBar) withObject:nil waitUntilDone:YES];
[self performSelectorOnMainThread:#selector(startAnimation) withObject:nil waitUntilDone:YES];
self.renderer.pdb = pdbToLoad;
self.loading = NO;
}
What I'm doing here is loading a molecule from a PDB file into memory and displaying it on an OpenGL view window. The second time I load a molecule (which will run loadPDB: above) I get the Giant Triangle Syndrome and Related Effects... I will see large triangles over my molecule.
However, I am releasing and reallocating my PDBHelper and ES1Renderer every time I load a new molecule. Hence I was wondering:
1. whether the helixVertices, helixIndices and helixColors which I have declared as class-wide variables are actually re-used in this instance. Do they point to the same objects?
2. Should I be setting all my variables to NULL after freeing? I plan to do this anyway, to pick up any bugs by getting a segfault, but haven't got round to incorporating it.
3. Am I even right to malloc() a class variable? Is there a better way of achieving this? I have no other known way of giving this information to the renderer otherwise.
I can't answer your general questions. There's too much stuff in there. However, this caught my eye:
[helper release];
[renderer.helper release];
PDBHelper *aHelper = [[PDBHelper alloc] initWithContentsOfFile:pdbToLoad];
helper = [aHelper retain];
renderer.helper = [aHelper retain];
[aHelper release];
I think this stuff possibly leaks. It doesn't make sense anyway.
If renderer.helper is a retain or copy property, do not release it. It already has code that releases old values when it is assigned new values. Also do not retain objects you assign to it.
You have alloc'd aHelper, so there's no need to retain it again. The above code should be rewritten something like:
[helper release];
helper = [[PDBHelper alloc] initWithContentsOfFile:pdbToLoad];
renderer.helper = helper;
Also, I think your helix malloced arrays should probably be instance variables. As things stand, if you have more than one ES1Renderer, they are sharing those variables.