Dragging a sprite with TouchMoved - cocos2d-iphone

I'm using Xcode with Cocos2d version 3.0.
I want to drag sprites around the screen. I've done so successfully using the following code:
(void) touchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [touch locationInNode:self];
sprite1.position=touchLocation;
sprite2.position=touchLocation;
sprite3.position=touchLocation;
sprite4.position=touchLocation;
}
However, sometimes the sprites stop moving after a second. It's not a lag, because they never catch back up with my movement. They just stop! If I let go and start moving my touch again, the sprites start moving fine again / sometimes do the 'freeze thing' again.
Is it a memory issue?
Ok, I'm sure it must be memory. I copied this code onto a simple game with hardly any sprites and it worked perfectly.

Ok I've got it!
I had to unEnable the UISwipeGestureRecognizers while I moved the sprite.
The game was registering my touchesMoved movement as a swipe, and cancelling the touchesMoved commands.

Related

Cocos2D: CCPhysicsNode and child sprite move with different speed

I'm following the tutorial here to mimic Flappy Bird. At the part to scroll the game scene:
- (void)update:(CCTime)delta {
_hero.position = ccp(_hero.position.x + delta * scrollSpeed, _hero.position.y);
_physicsNode.position = ccp(_physicsNode.position.x - (scrollSpeed *delta), _physicsNode.position.y);
...
}
Ideally the whole world will scroll left, and _hero (which is a child node of _physicsNode) move right with the same speed it would stay still on the screen. But when I run the code on simulator the _hero sprite just blast off to the right at the speed of light (about 10~20 times faster than the scrolling speed of _physicsNode). The _physicsNode and every other things inside it is scrolling left at normal speed as intended.
If I don't give _hero any movement it would scroll along with _physicsNode normally.
I have tried other method like using CCAction at game start:
CCAction *moveAction = [CCActionMoveBy actionWithDuration:1 position:ccp(-scrollSpeed,0)];
CCActionRepeatForever *repeatAction = [CCActionRepeatForever actionWithAction:(CCActionInterval *)moveAction];
[_physicsNode runAction:repeatAction];
And still get the same result. The speed value that _hero receive is always different from the speed that _physicsNode receive.
Could anyone explain to me why this is happening and how to fix it?
I'm using cocos2d 3.3 if it helps.
I finally figured it out.
The _hero node had its positionType set to CCPositionTypeNormalized (because I was trying to center him on the screen) while the _physicNode has its positionType as CCPositionTypePoints. Any change to the node's position (including movement actions) are based on the node's positionType. The result is that when I move the _hero node by 2, it doesn't move 2 points but 2% of the parent node's width instead.
I fixed it by aligning the _hero on SpriteKit using CCPositionTypeNormalized, then switching it to CCPositionTypePoints at game start.

Side scrolling game background

I am working in Cocos2d and xcode to create a side scrolling game.
I have an issue with my coding for adding a background to the levelscene.
I tried to implement a parallax background to no avail so have opted to just have a background the player will scroll across.
But at the moment the background follows the player across the screen, which frankly looks rubbish.
The code I have is
(void)setupParallax {
NSString *backgroundName = [self.tilemap propertyNamed:#"BackgroundImage"];
CCSprite *background = [CCSprite spriteWithFile:[AssetHelper getDeviceSpecificFileNameFor:[NSString stringWithFormat:#"background-noon.png]]];
background.anchorPoint = ccp(0,0);
background.position = CGPointMake(0, 0);
[self addChild:background z:0];
}
I know it must be something with either the position or anchorPoint but these values only change with reference to the screen they are loaded on.
Have you looked at this tutorial on doing parallax scrolling in cocos2d-x?
You an do parallax scrolling by hand...but it will probably easier, at least if you are just starting out, to do it using the CCParallaxNode and let the framework do the heavy lifting for you.

Cocos2D - CCSprite runaction not finishing with low delay

Problem: I have a CCSprite animation that uses 7 frames. It works fine when I run the action with a delay of .04f, however, I need to run this animation at .02f which causes it to completely stop about 2~3 frames into the sprite animation. Bumping it up to .03f gets about 4~5 frames in before the sprite freezes.
I can't post the code because there's way too much. Really I'm looking for a general idea of where to start looking. The CCSprite I'm calling the runaction on is being retained, so that doesn't seem to be the issue. Thoughts?

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!

Is it possible to "reset" any existing touches?

Cocos2d-iphone 1.0.1.
My main game scene requires the player to have a finger pressing the screen for the character to move.
While the character moves (thus the finger pressing the screen), a battle might occur. Such battle triggers a new CCScene which is pushed into the CCDirector (so the main game scene still exists within memory).
During the battle, naturally, the player will probably release his finger at some point.
When the battle is over, this CCScene is popped. Thus, the main game scene returns.
Now here is a problem: the main game scene still thinks that the finger that was initially pressing the screen before the battle is still pressing it at the same point, thus the joystick is still "functioning" and the player is still moving despite the fact that the finger is no longer pressing the screen.
Is there a way to "reset" the screen touches? I'd like to do so in the onEnter method of the main game scene. I tried something like
self.isTouchEnabled = NO;
self.isTouchEnabled = YES;
Hoping that such would re-register the touch dispatcher and thus causing some kind of reset. It did not work.
Basically, I need a way to tell the main game scene "dude, no one's touching the screen despite whatever you think".
Edit
Note that, if you press the screen again, it is fixed.
To make sure I understand the problem:
After you pop the battle scene, the character continues moving around as if the user never released his finger from the joystick?
My initial thought is that you have a deeper underlying design flaw somewhere in the code. It is my understanding that you want to use at least one layer to handle input. Try shifting everything in that 'game' CCScene to a 'game' CCLayer and add the layer as a child of the scene. Then register the layer to receive touch input.
If that doesn't work, you could always use a flag. Compare the scene you are in to the scene you want to be in, and if not, don't activate the joystick controller. Kind of hack-y but it works.
I've run into this kind of problem myself.
How i've fixed it:
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches){
[touchesOnScreen addObject:touch];
}
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches){
[touchesOnScreen removeObject:touch];
}}
- (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[touchesOnScreen removeAllObjects];
}
So basically iOS keeps a pointer to every touch on the screen. What you do is manage those touches yourself with an NSSet. When you push the scene just set every UITouch from the set to nil. That is equivalent to UITouchPhaseCancelled. This will cancel all your touches on the screen. If the user still has his finger on the screen that touch wont be registered anymore...even if he moves it or something.
Note : you need to remove all objects in ccTouchesCancelled (it's called when the user has 6 or more touches on the screen) or else when he touches again the app crashes. If your app needs more than 5 touches you need to find a way around that.
Possibly the simplest solution is to have your CCDirector, on completion of popping your battle scene, call first resignFirstResponder then 'becomeFirstResponder` on the offending view; off the top of my head anyway it seems that ought to forward on the touches to another responder, hopefully letting it fizzle out when no one else handles it.
A more complicated dive into the UIView methods provides this private call cancelTouchTracking. Most likely it does something similar to what #skytz has put together. Warning: That last will mostly likely get you rejected in the app store process, but for your info there it is.
I'd opt for the simpler solution and hope it works. O_o