UITapGestureRecognizer doesn't work when lots of sprites touched fast enough - cocos2d-iphone

I have initialized UITapGesture as follows:
tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
tapGestureRecognizer.numberOfTapsRequired = 1;
tapGestureRecognizer.cancelsTouchesInView = YES;
[[[CCDirector sharedDirector] view] addGestureRecognizer:tapGestureRecognizer];
As per code, if you tap on a sprite, it moves to new location using CCMoveTo action with duration = 0.3.
This works fine when a Sprite is tapped and you wait till it reaches new destination.
So far so good.
But if you tap on sprites fast enough to get an effect of multiple sprites moving to new destinations, I noticed some of the sprites are left inbetween and stop some random location.
Why this would be happening? Any thoughts?

Related

Cocos2d - Allow touches to be used by multiple classes (sneakyjoystick)

Evening all,
I'll start this question in the time honoured tradition by saying that I've had a good old search on SO and also in the wider world but I'm not quite getting my head around this...
I've implemented a sneakyJoystick which works wonderfully (It moves my sprite around quite happily) however I've now done myself a mischief in thinking about it's positioning.
What I'd like to do is simply change it's position to a touch location and have it move my sprite around but this seems to be out of my knowledge pool. I might be being an idiot but I cannot work it out.
The touch events are already sorted in the sneakyjoystick classes (available on github https://github.com/0xPr0xy/sneaky-joystick-cocos2d). At the moment if I create the joystick during the init method in a class called controlsLayer then everything works fine; Joystick appears and it allows me to move to sprite around
-(id) init
{
if( (self=[super init]) ) {
myJoystickBase = [[[SneakyJoystickSkinnedBase alloc] init] autorelease];
myJoystickBase.backgroundSprite = [CCSprite spriteWithFile:#"dpad.png"];
myJoystickBase.thumbSprite = [CCSprite spriteWithFile:#"joystick.png"];
myJoystickBase.joystick = [[SneakyJoystick alloc] initWithRect:CGRectMake(0, 0, 128, 128)];
myJoystickBase.position = ccp(64, 64);
myJoystickBase.backgroundSprite.opacity = 100;
myJoystickBase.thumbSprite.opacity = 100;
[self addChild:myJoystickBase];
myJoystick = [myJoystickBase.joystick retain];
[self scheduleUpdate];
}
return self;
}
So to begin with I thought about looking at how I could simply get it to show and hide itself and set it's location. To do this I created a ccTouchesbegan method which contains pretty much the same code as my init method did before. This works fine up to a point (the joystick appears centred wherever I touch) but the issue now is that I cannot interact with it. The joystick appears where i want but it will not recognise my movements (the stick on the joystick does not move which in turn means that my sprite is not being told to move either)
if( (self=[super init]) ) {
self.isTouchEnabled = YES;
[self scheduleUpdate];
}
return self;
}
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint location = [[CCDirector sharedDirector] convertToGL:[touch locationInView:[touch view]]];
location = [self convertToNodeSpace:location];
myJoystickBase = [[[SneakyJoystickSkinnedBase alloc] init] autorelease];
myJoystickBase.backgroundSprite = [CCSprite spriteWithFile:#"dpad.png"];
myJoystickBase.thumbSprite = [CCSprite spriteWithFile:#"joystick.png"];
myJoystickBase.joystick = [[SneakyJoystick alloc] initWithRect:CGRectMake(0, 0, 128, 128)];
myJoystickBase.position = location;
myJoystickBase.backgroundSprite.opacity = 100;
myJoystickBase.thumbSprite.opacity = 100;
[self addChild:myJoystickBase];
myJoystick = [myJoystickBase.joystick retain];
}
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
[self removeChild:myJoystickBase cleanup:YES];
}
So to my uninitiated brain this is saying that when I touch the controlsLayer class it happily does what I've asked it to but it will not then pass this touch to other classes.
In a nutshell can a touch event be passed to multiple classes at the same time? The sneaky joystick class uses CCTargetedTouchDelegate which worked fine when it was the only thing looking for a touch. However now that I've added a -(void)ccTouchesBegan: in another class it's not happy.
Can anyone tell me if the problem is with the way I'm handling touches or is it possibly an issue with the way that I allocate the joystick in the touch method? Should I be allocating the joystick in the init method and be doing something else in the touchesBegan method? Trial and error isn't getting me anywhere useful at the moment. Feel like I'm banging my head against a brick wall. I'm happy to post up the full class files if necessary. Does this make sense?
The problem is that you create the joystick during touch began, and remove it on touch ended. This does not allow the joystick to receive a touch began event, since the event is already being processed at the time the joystick is being created.
You're also leaking the joystick due to the unnecessary retain (use ARC, please!).
Try creating the joystick in init, disable & hide it until a touch began event is received. This is also faster than recreating the joystick on every touch.
Also, should you have multiple touches enabled, keep in mind that you can receive up to 5 touch began events (5 fingers) without any touch ended in between. That would create 5 joysticks, but only remove one! Every time!

Cocos2d animations in main loop

Anyone that can give any hint of the smartest way to do a main loop animation? I don't want to use CCAnimation because I want to control the animations frame by frame.
Shall I store the sprite rect (relative to the sprite sheet) for each individual frame in an array, and then look up the suiting rect in each animation step? I tried to find out how this is done in CCAnimation, but I didn't succeed...
How to get the rect for each frame at initialization?
How to set the rect at each animation step?
Do I need to use CCSpriteBatchNode? I guess not, eh?
Cannot crealry understand, why you don't want to use CCAnimation, but anyway, to get answer for your questions you can check creation code of the CCSprite instance. Then, check creation of CCSpriteFrame instance. There you will find the answer for at least your first question.
Actually if you just want to manage animation frames differently from CCAnimate, you can just store array of CCSpriteFrames and show them as you want(in CCAnimate action these frames are just changed one by one in equal time intervals).
And if you do not want to show more than one frame of your animation, there is no difference will you use CCSpriteBatchNode or not. It saves a lot of processor time if you need to draw several parts of one texture, as it draws them in one draw call instead of send draw message to all of these sprites.
As you want animate sprite frame by frame I think using CCSpriteBatchNode would be a better option as it give you frame by frame access of animation.Making plist of sprites using any tool like "Zwoptex" will give an efficient way to animate using CCSpriteBatchNode.
Hope you know the animation using plist file with CCSpriteBatchNode.
I did the following with inspiration from Morions answer:
In the game tick function:
_animationFrames.legFrame = (_animationFrames.legFrame + 1) % _animationFrames.legFrames.count;
[_legs setDisplayFrame: [_animationFrames.legFrames objectAtIndex: _animationFrames.legFrame]];
And in the init function:
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode
batchNodeWithFile:#"Player.png"];
[self addChild:spriteSheet];
_animationFrames.legFrames = [[NSMutableArray array] retain];
for(int i = 0; i <= 15; ++i)
{
[_animationFrames.legFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"Player_legs-%d.png", i]]];
}
_legs = [CCSprite spriteWithSpriteFrameName:#"Player_legs-0.png"];
[_sprite addChild: spriteSheet];
[spriteSheet addChild:_legs z:1];

Issues with adding an array of moving sprites to screen?

In my program, I have created an NSMutable Array which contains a series of sprites that move across the screen (to the left)
However, while implemented in my code, they do not seem to appear on the screen simulator when tested.
Any ideas?
-(void) addBlocks
{
NSMutableArray *_blocks; blocktest=[CCSprite spriteWithFile:#"blocksquare.png"];
blocktest.tag = 1;
// add the block to the array of blocks
[_blocks addObject:blocktest];
blocktest.position=ccp(500,100);
id repeat2 =[CCRepeatForever actionWithAction:[CCSequence actions:
[CCMoveTo actionWithDuration:7 position:ccp(-180,100)],nil]];
[blocktest runAction:repeat2];
}
That's because you haven't added blocktest to your scene ( [self addChild:blocktest] ).
Adding it to _blocks, will work as a reference to fetch your sprites later, but will not add the sprites on any layer or scene, so you won't see them anywhere

Stars for scrolling shooter with CCParticleSystemQuad?

My code for init function:
NSArray *starsArray = [NSArray arrayWithObjects:#"Stars1.plist", #"Stars2.plist", #"Stars3.plist", nil];
for(NSString *stars in starsArray) {
CCParticleSystemQuad *starsEffect = [CCParticleSystemQuad particleWithFile:stars];
[self addChild:starsEffect z:-1];
}
The problem is that these particles appear and fully fill the screen rectangle during few seconds. But I need the sky full of stars from the beginning.
According to the answer at cocos2d starting particles from a specific time in the future , you can manually update the particle system. Example in cocos2d-x:
CCParticleSystemQuad *particle = CCParticleSystemQuad::create("res/Particles/Stars1.plist");
for (int i = 0; i < 10; ++i) {
particle->update(.1);
}
You may need to change the interval to suit the particles.
Add them to a layer, hide the layer, then unhide the layer after everything is done loading. That way you can set stuff up and not have it display right away.
That's just one approach. Another idea is to load all your images into Cocos before game play and game logic processes begin. That way there is no pause and delay while images are loading.

How to keep fps rate constant in cocos2d

I have 3 question.
How to keep fps rate constant(almost) in cocos2d.When lots of CCSprite created and removed within a small interval like (5-8) per second is it possible to keep frame rate lmost constant??
Is [self removeChild:sprite cleanup:YES] is enough or i should also use
CCTexture2D *texture = [sprite texture];
[[CCTextureCache sharedTextureCache] removeTexture: texture];
The following Part of code is responsible for my frame drop.How to accomplish same task in better way-
id fadeout = [CCFadeOut actionWithDuration:1.4f];
id call = [CCCallFunc
actionWithTarget:self
selector:#selector(RemoveSmashedSprite:)];
CCSequence* sequence= [CCSequence actions:fadeout, call, nil];
[smash runAction:sequence];
and
-(void)RemoveSmashedSprite:(id)sender
{
CCSprite *sp = (CCSprite *)sender;
[self removeChild:sp cleanup:YES];
}
This is called 5-8 times per second.So the frame rate goes down.Can any one help me.
I am guessing that the "smashed sprite" in question is probably one of a number of sprites visible on the screen, more than likely sharing the same texture (part of a sprite sheet perhaps, or all the same image, etc?).
If this is the case, I would recommend allocating the number of sprites your games requires to have on screen and rather than "destroy" (remove them, re-create them) you should "re-use them".
Store a reference to all the CCSprite's in an NSMutableArray, populate this array with the number of sprites you think is appropriate (how many sprites are removed per second? how many sprites are added per second? how many sprites are on the screen to start? - use the answers to these questions to determine the most appropriate size of your array).
Then, rather than removing the sprite as a child, simply [sprite setVisible: NO] to hide it, then ... when you need to display a new one on screen, use the next "available" (hidden) sprite in the array (keep track of the last "used" sprite with an index, and simply increment it by 1 to find the next "available". Once the index exceeds the bounds, reset it to 0 - at this point, that sprite should have been "smashed"). Once you have the next "available" sprite, change it's properties (position, scale, texture?, visibility) and apply any actions that are appropriate (don't forget to "stop all actions" on it when "smashing it" - this is what "cleanup: YES" does).
If your game does not allow you to go around in a "circle" like this, because the user decides what may or may not be visible by interacting with the sprites in a varied order then you can store a reference to the "smashed sprites" in a separate array (while keeping them in the original array as well - then you can just pick anyObject from the "unused" array, remove it from that array and then alter it as mentioned above).
This should reduce your frame rate drops dramatically, as you won't be creating and destroying sprites all the time.
Another thing that may be occurring is that you are using different images, and each image is stored separately (image1.png, image2.png, image3.png) and the creation/destruction of these is causing the textures to be removed and re-added ... to resolve this, create a sprite sheet and add the sprite sheet to the Texture Cache, then create your images with 'spriteFromSpriteCacheName:#"image1.png"'