Is it possible to "reset" any existing touches? - cocos2d-iphone

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

Related

Dragging a sprite with TouchMoved

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.

Cocos2d Forcefully Ending Touch

Is there a way to call upon the ccTouchEnd method?
Like, in my update, I want it write
If(situation occurs){
[self ccTouchEnd];
}
Or something like that. The reason I'm asking is because this thing happens for 2 different reason. one would be if the user ends the touch. The other would be if the sprite collides with another sprite.

Does replaceScene replace only the current scene or does it replace all scenes in Cocos2d

I have a game in Cocos2d with a main scene (game scene) and a button to go to a "Configuration" scene. When the user clicks on the Configuration button in the main scene I use pushScene to go tho the "Configuration" scene. The reason I use pushScene is to allow the user to resume the game where he was left off.
In the "Configuration" scene there are two options: "Cancel" and "Ok". If the user hits "Cancel" I use popScene and the game resumes where it was left of. If the user hits "Ok" I use replaceScene because I want the game to start from the beginning with the new configuration.
So, when the user hits "Ok" I know that the "Configuration" scene is replaced by the new game scene, but does the old game scene gets replaced too? Otherwise, am I doing things correctly or should I implement another way to let the game scene know whether it should resume or restart.
I want to make sure I am not leaking memory by accumulating unreplaced scenes.
The replaceScene method does what it says. It replaces the current scene. If you have 10 scenes pushed onto one another, it will replace the 10th scene and all previous scenes remain.
It's one of the reasons why I don't recommend using pushScene. It's too easy to forget a situation where scenes might get pushed more than they get popped. The other reason is that popScene can't be animated with a transition.
Btw, you can easily test this behavior if you do replaceScene after pushScene, then popScene in the newly replaced scene. You'll see the old scene popping up. Normally if you popScene with just a single scene in the stack it'll throw an assertion.

cocos2d how do I prevent multi touch propagation to previous layers?

I'm handling the touches manualy through a class that holds every touchable sprite. Also, every menu is a CCLayer, and when I change from one menu to the other, I use the CCTransition### and get the new scene.
So, the layers are "still there" and when I touch one menu, the previous layer also receive it... like when I start the game in a new CCLayer, and then the menus are still affected for the multi touch...
If I use single touch, I can swallow the touches, but the game must have multi touch...
What can I do? Thank you all!
You need to consume the touch event in whatever layer needs it. After a layer consumes it, the touch will no longer be propagated to "previous" layers.
http://www.cocos2d-iphone.org/api-ref/0.9.0-beta/_c_c_touch_delegate_protocol_8h_source.html
To consume a touch event, you return YES in the ccTouchBegan delegate method.
EDIT: changed method name to match CCStandardTouchDelegate
- (BOOL)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
if (condition)
return YES;
else
return NO;
}
You can set up the priority to 0 for the top layer and priority to 1 for the layer under it like this.
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
You could try to use the property
self.isTouchEnabled = NO;
within the layer, you don't want to receive touches.
If you you use CCButton objects you need to disable them.
I hope it helps...
Greetings Anselm

How To Retrieve Actions From Sprite In cocos2d

I have a CCSprite that I'm using in a scene and have created multiple CCAnimation actions to apply to it all using a single CCSpriteFrameCache sharedSpriteFrameCache. While everything is working and I'm able to switch between animations, I feel like I'm doing poorly and would like to simplify my code by retrieving the running action(s) on the CCSprite to stop them individually before running the next action on it.
To help create some context, lets assume the following situation:
We have a CCSprite called mySprite
We have 3 separate CCAnimation actions defined for walking to the right, walking to the left, and sitting looking forward called: actionAnimWalkRight, actionAnimWalkLeft, and actionAnimSitForward respectively.
We want to have the sprite walk to the right when someone touches the screen right of mySprite, walk left when someone touches the screen left of mySprite and sit when someone touches mySprite.
The approach I'm using to accomplish this is as follows:
Place CCSprite as a child in the scene.
Tell the sprite to run an action using: [self runAction:actionWalkRight];
When I want to change the action after someone touches, I have a method called stopAllAnimationActions which I call before I apply a new action that stops any animation action no matter what's running. Basically lists ALL the CCAnimation/CCActions I have defined and stops each one individually since I don't want to use stopAllActions. as follows: [self stopAction:actionWalkRight]; [self stopAction:actionWalkLeft]; [self stopAction:actionSitForward];
Then I apply the new animation after the above method fires using: [self runAction:actionWalkLeft];
While this works, it just seems like a poor design to stop items that I know aren't running just because I don't know exactly what is running. So just looking for advice and the best recommended practice to do something like this within very complex situations so tracking every possible situation is difficult. Any feedback would be appreciated.
When creating the actions set the tag of that action with a constant:
actionWalkRight.tag= kCurrentAction;
[self runAction:actionWalkRight];
Then, retrieve the running action by that tag and stop it.
[self stopActionByTag:kCurrentAction];
I recommend you simplify your process and take advantage of the native Cocos features, including stopAllActions. Don't re-use actions, always create them from scratch as it has been well discussed among Cocos developers that re-using actions can be buggy.
Cocos is well optimized and features like stopAllActions are not performance hogs. It would probably be faster than your approach, actually.