(cocos2d sneaky input) when setting joystick position with touchLocation, sprite won't move - iphone

i want to let user choose where the joystick should be. i.e., when user touch at one location, the joystick will appear there and ready to use and will remove when finger released.
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if ([self getChildByTag:kTagJoyStick] == nil) {
[self addJoystickWithPosition:[Helper locationFromTouches:touches]];
}
}
-(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if ([self getChildByTag:kTagJoyStick] != nil) {
[self removeChildByTag:kTagJoyStick cleanup:YES];
}
}
-(void) ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self ccTouchesEnded:touches withEvent:event];
}
(do nothing in ccTouchesMoved method)
the update methods for joystick is:
-(void) sneakyUpdate {
if ([self getChildByTag:kTagJoyStick] != nil) {
if (joystick.velocity.x < 0) {
[self controlLeft];
}
else if (joystick.velocity.x > 0) {
[self controlRight];
}
else {
[self controlStop];
}
}
else {
[self controlStop];
}
}
but the result is, the joystick will appear and auto remove. but my sprite won't move. ( i set the break point, the sneakyUpdate method did get called. but the joystick.velocity is always 0. (and the thumbSprite didn't follow our finger.
please help me.
update:
and it turns out that i have to use 2 fingers (one for touch once and let the joystick show up, move my finger away, and then use another finger to control the joystick)

I'm not 100% sure, but I think you should use ccTouchBegan instead ccTouchesBegan, because sneakyJoystick classes use ccTouchBegan/Moved/Ended/Cancelled. Also, there are for a single touch, that is what you want.
I hope it works!

It looks like the problem is in your joystick class. Every joystick implementation I've seen uses the ccTouchesBegan method to activate the joystick, then in the ccTouchesMoved method, it makes sure its activated before using it. The problem I am seeing is that you create and add the joystick AFTER the touches began method, meaning your joystick never 'activates'. One way of bypassing this is to do all of the joystick's ccTouchesBegan functions in the method that creates the joystick, and 'activate' it from there by passing a reference to the touch that will be using it.

Related

SKSpriteNode removeFromParent in touchesEnded

I have an issue removing an SKSpriteNode from it's parent when a touch is detected on that SKSpriteNode. Here is the code:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint sceneLocation = [touch locationInNode:self];
if ([_tableDoneButton containsPoint:sceneLocation]) {
NSLog(#"here");
[self removeItemsFromView];
_headerNode.portLabel.text = [IPGameManager sharedGameData].world.player.port.name;
[_portNode show];
}
SKNode *nodeInPort = [_portNode nodeAtPoint:[touch locationInNode:_portNode]];
if ([nodeInPort.name isEqualToString:#"marketButton"]) {
[self showMarket];
}
}
}
In the showMarket function, a 'Done' SKSpriteNode is added so the user can leave the market when he/she wants to. As you can see above, if the _tableDoneButton contains the touch location, it should call the removeItemsFromView function which contains [_tableDoneButton removeFromParent];. Do I have to do something special when removing nodes this way?
Another issue: It seems to get removed ~1 second later. What exactly happens when you run [node removeFromParent]? If I set the SKSpriteNode to nil after I call [self removeItemsFromView] it works fine. But I shouldn't have to do that?

How to Implement Touch Up Inside in touchesBegan, touchesEnded

I'm wondering if someone knows how to implement the "touch up inside" response when a user pushes down then lifts their finger in the touchesBegan, touchesEnded methods. I know this can be done with UITapGestureRecognizer, but actually I'm trying to make it so that it only works on a quick tap (with UITapGestureRecognizer, if you hold your finger there for a long time, then lift, it still executes). Anyone know how to implement this?
Using the UILongPressGesturizer is actually a much better solution to mimic all of the functionality of a UIButton (touchUpInside, touchUpOutside, touchDown, etc.):
- (void) longPress:(UILongPressGestureRecognizer *)longPressGestureRecognizer
{
if (longPressGestureRecognizer.state == UIGestureRecognizerStateBegan || longPressGestureRecognizer.state == UIGestureRecognizerStateChanged)
{
CGPoint touchedPoint = [longPressGestureRecognizer locationInView: self];
if (CGRectContainsPoint(self.bounds, touchedPoint))
{
[self addHighlights];
}
else
{
[self removeHighlights];
}
}
else if (longPressGestureRecognizer.state == UIGestureRecognizerStateEnded)
{
if (self.highlightView.superview)
{
[self removeHighlights];
}
CGPoint touchedPoint = [longPressGestureRecognizer locationInView: self];
if (CGRectContainsPoint(self.bounds, touchedPoint))
{
if ([self.delegate respondsToSelector:#selector(buttonViewDidTouchUpInside:)])
{
[self.delegate buttonViewDidTouchUpInside:self];
}
}
}
}
I'm not sure when it was added, but the property isTouchInside is a life saver for any UIControl derived object (e.g. UIButton).
override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
super.endTracking(touch, with: event)
if isTouchInside {
// Do the thing you want to do
}
}
Here's the Apple official docs
You can implement touchesBegan and touchesEnded by creating a UIView subclass and implementing it there.
However you can also use a UILongPressGestureRecognizer and achieve the same results.
I did this by putting a timer that gets triggered in touchesBegan. If this timer is still running when touchesEnded gets called, then execute whatever code you wanted to. This gives the effect of touchUpInside.
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSTimer *tapTimer = [[NSTimer scheduledTimerWithTimeInterval:.15 invocation:nil repeats:NO] retain];
self.tapTimer = tapTimer;
[tapTimer release];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([self.tapTimer isValid])
{
}
}
You can create some BOOL variable then in -touchesBegan check what view or whatever you need was touched and set this BOOL variable to YES. After that in -touchesEnded check if this variable is YES and your view or whatever you need was touched that will be your -touchUpInside. And of course set BOOL variable to NO after.
You can add a UTapGestureRecognizer and a UILongPressGestureRecognizer and add dependency using [tap requiresGestureRecognizerToFail:longPress]; (tap and long press being the objects of added recognizers).
This way, the tap will not be detected if long press is fired.

iphone cocos2d v2.0.0-rc0 - can't disable touch

I've been trying to resolve a problem for a long time, unfortunately i couldn't find a solution. I hope you can help me...
I'm trying to develop a turn based game. Player 1 is the user, player 2 is the iphone. I want to receive touches when it's the player1's turn.
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
if (self.playerNo==1) { //control player1
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
[self selectSpriteForTouch:touchLocation];
return YES;
}
//if it's not player1's turn, return NO
return NO;
}
When it's iphones turn, I want to disable touches during 5 seconds. I tried all the ways below, but no success.
-(void)iphonesTurn
{
[NSThread sleepForTimeInterval:5];
//
//game logic...
//
}
During 5 seconds I touch screen several times. I realised that all the touches looks like stored and the app reacts to all of my touches when it's the player1's turn.
How can I completely disable touches?
Thanks for your help.
I don't know where is located your snippet of code but if it runs into the main thread you must NEVER block (pause) it!
Try to do something like that (it works with the final version of cocos2d 2.0):
- (void)iphonesTurn
{
[[[CCDirector sharedDirector] touchDispatcher] setDispatchEvents:NO];
[self scheduleOnce:#selector(enableTouch) delay:5.0f];
}
- (void)enableTouch
{
[[[CCDirector sharedDirector] touchDispatcher] setDispatchEvents:YES];
}

iOS Loses Touches?

I can't find anything to explain lost UITouch events. If you smash your full hand on the screen enough times, the number of touchesBegan will be different than the number of touchesEnded! I think the only way to actually know about these orphaned touches will be to reference them myself and keep track of how long they haven't moved.
Sample code:
int touchesStarted = 0;
int touchesFinished = 0;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
touchesStarted += touches.count;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
touchesFinished += touches.count;
NSLog(#"%d / %d", touchesStarted, touchesFinished);
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
Don't forget about touchesCancelled: UIResponder reference
Editing in response to the poster's update:
Each touch object provides what phase it is in:
typedef enum {
UITouchPhaseBegan,
UITouchPhaseMoved,
UITouchPhaseStationary,
UITouchPhaseEnded,
UITouchPhaseCancelled,
} UITouchPhase;
I believe that if a touch starts and ends in the same touch event set, -touchesBegan:withEvent: will be called but will contain touches which have ended or cancelled.
You should change your counting code, then, to look like this:
int touchesStarted = 0;
int touchesFinished = 0;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self customTouchHandler:touches];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self customTouchHandler:touches];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self customTouchHandler:touches];
}
- (void)customTouchHandler:(NSSet *)touches
{
for(UITouch* touch in touches){
if(touch.phase == UITouchPhaseBegan)
touchesStarted++;
if(touch.phase == UITouchPhaseEnded || touch.phase == UITouchPhaseCancelled)
touchesFinished++;
}
NSLog(#"%d / %d", touchesStarted, touchesFinished);
}
Every single touch event will go through both phases of started and finished/cancelled, and so your counts should match up as soon as your fingers are off the screen.
One thing to remember... You may receive some touches that have multiple taps. Don't forget to take tapCount into account.
However, if you still have the problem, you can consider all touches from the event, though it presents some other management issues...
HACK ALERT
I have coded the following HACK to work around this. Sometimes the touchesEnded does not get called, BUT, the touches show up as part of all the touches in the event.
Note, that you can now process the same "canceled" or "ended" touch multiple times. If that is a problem, you have to keep your own state of "pending" touches, and remove them when done.
Yeah, it all is pretty bad, but I don't know how to overcome this issue without a similar hack. The basic solution is to look at all the touches in each event, and process them based on their phase, calling the appropriate ended/canceled when they are seen.
- (void) touchesEndedOrCancelled:(NSSet *)touches
{
__block NSMutableSet *ended = nil;
__block NSMutableSet *canceled = nil;
[touches enumerateObjectsUsingBlock:^(UITouch *touch, BOOL *stop) {
if (touch.phase == UITouchPhaseEnded) {
if (!ended) ended = [NSSet setWithObject:touch];
else [ended addObject:touch];
} else if (touch.phase == UITouchPhaseCancelled) {
if (!canceled) canceled = [NSSet setWithObject:touch];
else [canceled addObject:touch];
}
}];
if (ended) [self touchesEnded:ended withEvent:nil];
if (canceled) [self touchesCancelled:canceled withEvent:nil];
}
Then, call it at the end of touchesBegan and touchesMoved...
[self touchesEndedOrCancelled:event.allTouches];
For this to work, touchesEnded/Canceled needs to not choke on a nil event. Also, the "other" needs to be handled. In touchesEnded...
[self touchesCancelled:[event.allTouches objectsPassingTest:^BOOL(UITouch *touch, BOOL *stop) {
return touch.phase == UITouchPhaseCancelled;
}] withEvent:nil];
and in touchesCanceled...
[self touchesEnded:[event.allTouches objectsPassingTest:^BOOL(UITouch *touch, BOOL *stop) {
return touch.phase == UITouchPhaseEnded;
}] withEvent:nil];
You may need to provide more detail but....
If you run your finger off the edge of the screen this event will show touch started and touch moved but no end as it didn't actually end (lift finger). This could be your problem, that the event didn't happen. Plus there is a limit to the amount of touches in multi touch if you press say 10 times its then up to the system to determine what events are really and what are false, it seems like you are using the hardware incorrectly.

How to call a method only once during -(void)touchesMoved?

I am using - (void) touchesMoved to do stuff when ever I enter a specific frame, in this case the area of a button.
My problem is, I only want it to do stuff when I enter the frame - not when I am moving my finger inside the frame.
Does anyone know how I can call my methods only once while I am inside the frame, and still allow me to call it once again if I re-enter it in the same touchMove.
Thank you.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event touchesForView:self.view] anyObject];
CGPoint location = [touch locationInView:touch.view];
if(CGRectContainsPoint(p1.frame, location))
{
//I only want the below to me called
// once while I am inside this frame
[self pP01];
[p1 setHighlighted:YES];
}else {
[p1 setHighlighted:NO];
}
}
You can use some attribute to check if the code was already called when you were entering specific area. It looks like highlighted state of p1 object (not sure what it is) may be appropriate for that:
if(CGRectContainsPoint(p1.frame, location))
{
if (!p1.isHighlighted){ // We entered the area but have not run highlighting code yet
//I only want the below to me called
// once while I am inside this frame
[self pP01];
[p1 setHighlighted:YES];
}
}else { // We left the area - so we'll call highlighting code when we enter next time
[p1 setHighlighted:NO];
}
Simply add a BOOL that you check in touchesMoved and reset in touchesEnded
if( CGRectContainsPoint([p1 frame],[touch locationInView:self.view])) {
NSLog (#"Touch Moved over p1");
if (!p14.isHighlighted) {
[self action: p1];
p1.highlighted = YES;
}
}else {
p1.highlighted = NO;
}
try using a UIButton and use the 'touch drag enter' connection in Interface Builder.