CCPanZoomController + Touchable/Clickable sprites - cocos2d-iphone

I am using CCPanZoomController to make my 'map' (one image) zoomable and pan-able.
On this map I would like to have clickable/touchable sprites, which when clicked change the image in the sprite.
The problem is that when the user pinches the screen (to zoom out/in), they may touch the sprite, which changes the image of the sprite, which is something I don't want.
I had an idea to solve this, but as I'm new to Cocos2d I don't know how to implement it:
I thought that I could detect when the user touches the screen/the sprite, and doesn't move their touch (as if to pinch or pan) through detecting when the user first touches the screen, (transform that initial touch into a coordinate), and then when the user stops touching the screen (turn that into a coordinate), and compare the both, and if their is no change (or very little change) then change the image of a sprite?
How would I go about doing this? Big thanks to anyone who can help!!

So I've been working with CCPanZoomController myself in my game and ran into similar issues as you but with many different aspects such as when they touch a sprite, I didn't want to have the background move with it or I'd want the sprite to not move when the background was zooming. So what I did was to make methods to "turn off" touches for the layer that I didn't want to react and re-enable them once the action in the other layer was done.
I created the following method inside each layer to disable it or enable it for touch which I call from the different touch events.
// Public Method: Allows for disabling touch for this layer and re-enabling it
-(void)enableTouches:(BOOL)enable
{
// Check if the bool value is to enable or disable touches
if (enable) {
// Call for the removal of all touch locations in array in the CCLayerPanZoom instance
[_panZoomLayer removeTouchesFromArray];
// Call the touch dispatcher and add the CCLayerPanZoom back as a delegate for touches
[[CCTouchDispatcher sharedDispatcher] addStandardDelegate:_panZoomLayer priority:0];
CCLOG(#"PanZoomWrapperLayer:enableTouches - LayerPanZoom touches enabled");
} else {
// Call the touch dispatcher to remove the CCLayerPanZoom as a delegate to disable touches
[[CCTouchDispatcher sharedDispatcher] removeDelegate:_panZoomLayer];
CCLOG(#"PanZoomWrapperLayer:enableTouches - LayerPanZoom touches disabled");
}
}

I've found a simple solution to this problem. However it may not suit your needs!
I've subclassed the CCMenu class and overridden the -(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event as follows:
-(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
[_selectedItem unselected];
_selectedItem = nil;
}
I've set the touchSwallow property of the instance of that new menu to NO.

Related

QGraphicsScene mouseMoveEvent does not work until QGraphicsView wheelEvent

I have a strange issue that I have been unable to determine the cause. Basically, I created a 2D view with pan and zoom functionality and a scene with items that can moved with grid snapping. To move an item in the scene I extended Scene::mousePressEvent to get a pointer to the item and Scene::mouseMoveEvent to keep the item tracked on the cursor. To drop the item, I used Scene::mousePressEvent again. To pan, I extended View::mousePressEvent, View::mouseReleaseEvent, and View::mouseMoveEvent and to zoom I extended View::wheelEvent.
Now for the symptoms:
I start the application with an item in the Scene. If I click and hold, then move the mouse, the item moves as intended. As soon as I release the mouse button, the item stops moving. I can click to drop and the item is placed according to the drop code in Scene::mousePressEvent. Try again and still the item only moves when the mouse button is pressed.
Then comes the strange part: If I use the mouse wheel to zoom the View, everything performs as expected after that event. The mouse is clicked to select an item, it moves as I move the mouse and drops when I click again.
So the obvious solution:
wheelEvent(new QWheelEvent(QPointF(0,0),0,Qt::NoButton,Qt::NoModifier));
called at the creation of the View and everything works fine. It calls the extended View::wheelEvent with no change to the view and before the scene is even created, but afterwards the programs behaves as expected.
So I'm here to see if any of the excellent Qt experts out there can explain this strange behavior. Any comments or direction are appreciated.
In case it helps, here is the View::wheelEvent override code. tform is a QTransform with which I maintain zoom. Also, I've tried with and without the call to the base method but there is no change in behavior.
void SchematicView::wheelEvent(QWheelEvent* event)
{
// Scale the view / do the zoom
double scaleFactor = 1.1;
if(event->delta() > 0 && tform.m11() < max_zoom) {
tform.scale(scaleFactor,scaleFactor);
} else if (event->delta() < 0 && tform.m11() > min_zoom){
tform.scale(1.0/scaleFactor,1.0/scaleFactor);
}
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
setTransform(tform);
QGraphicsView::wheelEvent(event);
}
Without a SSCCE to look at and test with, it's hard to say for sure, but what you're describing sounds a lot like your mouseMoveEvent() callback is only getting called when the mouse button is being held down during the move. That, in turn, sounds a lot like the expected behavior for mouseMoveEvent(), as documented in QWidget::mouseMoveEvent():
If mouse tracking is switched off, mouse move events only occur if a
mouse button is pressed while the mouse is being moved. If mouse
tracking is switched on, mouse move events occur even if no mouse
button is pressed.
If that is indeed the problem, then a call to setMouseTracking(true) may get you the behavior you are looking for.
On a broader level, note that there are easier ways of obtaining the behavior you are trying to implement -- for example, to allow the user to drag and drop items in a QGraphicsScene, all you really have to do is call setFlags(QGraphicsItem::ItemIsMovable) on any QGraphicsItems that you want the user to be able to drag around. No hand-coding of event handlers is necessary unless you are trying to obtain some non-standard behaviors.

How to get back to the previous CCScene?

I have one CCScene, in which their is About us ICON. When I tap on it, goes to the next CCScene where the information to display.
Now, what happen
When I touch on the back option of the android phone, Game closed.
what I need
when I click the back, It takes me to the First CCscene, where the Icon of ABOUT us present.
What should I do for this ???
Solution : From what I solve
CCDirector.sharedDirector().popScene();
You can use the CCDirector to push scenes on top of running scenes.
Say you have scene1 and scene2 both instances of CCScene (or a subclass of CCScene) then when the user taps the about icon :
[[CCDirector sharedDirector] pushScene:scene2]; // Assuming scene1 is already running
when the user taps the back button :
[[CCDirector sharedDirector] popScene];
EDIT : According to your comment (and in java this time :)) :
public void onBackPressed() {
super.onBackPressed();
if(backPressFlag==1) {
CCDirector.sharedDirector().popScene();
}
}
And of course this will work only if you pushed the About scene with :
CCDirector.sharedDirector().pushScene(AboutScene.scene());
you can do in this way on clicking back button
CCDirector.sharedDirector().getRunningScene().removeAllChildren(true);
Now create new scene
sceneIndex--;
CCScene scene = CCScene.node();
scene.addChild(backAction());
CCDirector.sharedDirector().replaceScene(scene);
Here backAction() is same as in cocos examples

create a class which do some animation and show it on screen

I am trying to create a new class that has many sprites on it , and they perform some actions one after another, and do some other cool stuff on screen.
this whole "show" will be in some class,that appears on screen when i need it .
On my main class Main which is a CCScene, i want to have the ability , to just call that animation class whenever i want to , and that it will be added to my main screen and perform its "show" .
some class :
//FADE ACTIONS
fadeInAction=[CCFadeIn actionWithDuration:0.5];
fadeOutAction=[CCFadeOut actionWithDuration:0.5];
fadeOutFastAction=[CCFadeOut actionWithDuration:0.1];
//circle animation parts
for (int i=0;i<12;i++)
{
circlePartsArray[i]=[CCSprite spriteWithFile:[NSString stringWithFormat:#"c%d.png",i+1]];
circlePartsArray[i].position=ccp(winSize.width/2,winSize.height/2);
[self addChild:circlePartsArray[i]];
}
Then from my main class i want to see on my screen, all the performance that someClass do- the sprites added, than they will fade etc..
How would i define that class, and add it to my main scene ?
Extend the CCNode class. Overwrite the CCNode Class' onEnter method, which you will use to create and add the child sprites, and start the animation. This way whenever you add the Node to your scene, it will begin the animation (probably using action sequences). Your animation action sequences should end with a CCCallBlock action, which you will use to clean up and remove the Node from its parent.

trying to detect touch ended and touch moved for CCSprite

I have a CCSprite subclass, and initially I had set it up with a
So I had the following code:
-(void)onEnter {
[super onEnter];
[[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
}
-(void)onExit {
[super onExit];
[[[CCDirector sharedDirector] touchDispatcher] removeDelegate:self];
}
-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
if ([self containsTouch:touch]) {
// do stuff
return YES;
}
return NO;
}
But then I realized that I actually didn't want to use touchBegan, because I want to detect if a sprite has been dragged downward-- So I wanted to use touchMoved and touchEnded instead of touchBegan.
However, when I implement those methods, they are not called...
How can I tell when the sprite's touch ended, and if it was "swiped"?
Enabling multiple touches: In the applicationDidFinishLaunching:application method in your appdelegate, set multiple touches to YES: [glView setMultipleTouchEnabled:YES];
Then in your CCLayer subclass (the class you are working in for detecting touches), in the init method, add self.isTouchEnabled = YES;
Now your multi touch methods should get called.
Swiping: cocos2d does not support gestures out of the box. You will likely have to work yourself. You can start with the apple event handling guide about gestures. The How To Drag and Drop Sprites with Cocos2D totorial at raywenderlich.com hepled me.
In order for the dispatcher to call your methods (moved, ended, cancelled), you have to first claim the touch ie. you will process the events. That is done in ccTouchBegan, when you return YES. After that you will receive the other events.
CCTouchableSprite - my touchable sublcass of CCSprite with Objective-C blocks, you can use touchMoved to detect what you want.

How to add global CCLayer which is not affected by scene transitions in Cocos2d?

Is it possible to add global layer in Cocos2d, which is not affected by scene transitions?
As I can see, it should be above all scenes hierarchy.
There's an old and short discussion on Cocos2d forum, but there's no answer:
http://www.cocos2d-iphone.org/forum/topic/8071
UPD. 'by scene transitions' I mean 'by animated scene transitions'.
You can use the notificationNode property of CCDirector to place a CCNode (ie.CCLayer, CCLabel, etc.) that will remain above scenes, even during transitions. Something like this:
CCLayer *layer = [CCLayer node];
CCLabelTTF *label = [CCLabelTTF labelWithString:#"Test" fontName:#"Marker Felt" fontSize:32];
[layer addChild:label];
[[CCDirector sharedDirector] setNotificationNode:layer]; // Layer should be placed here
[layer onEnter]; // Schedule for updates (ie. so that CCActions will work)
It's meant for notification purposes (ads, etc.) so I wouldn't suggest trying to do anything too fancy from this node.
My gut says no, my brain says maybe.
The documentation says "It is a good practice to use and CCScene as the parent of all your nodes."
I can't test this right now, but looking at the inheritence diagram of CCNode, it looks like the logic of CCNode and CCScene differs only by anchor point. So, you might be able to create a CCLayer to use as your root layer, and add two children to it - A root CCScene, and a CCLayer for your GUI (with a higher Z order).
However, scene transitions may still be tricky, as you generally call CCDirector replaceScene, which works on the root scene you give it. If you give it the CCScene child of your root CCLayer, it may not draw the CCLayer and its GUI child. If you give it the root CCLayer, you are in the same situation as before.
I would still give it a try.
You can create a CCLayer subclass and turn it into a singleton. You add it to the scene like any other child node.
Whenever you transition from one scene to another, you can then remove the layer from the old scene and add it to the new scene as a child. This will only work if you don't use scene transition animations.
The alternative is to not use the CCDirector replaceScene method, and instead design your app to run as a single scene that never changes. To "fake" the changing of a scene you will use two layers, one global layer and another layer that contains your current scene nodes. If you want to transition you can animate the layer with CCActions to, for example, slide out of the screen while sliding in a new layer with a different node hierarchy. All you really lose is the convenience of the CCSceneTransition classes for animating scene changes.