I have the following issue:
If i tap the touch screen with one finger (i.e. middle finger) and close to the moment that the finger is going up i tap with a second finger (i.e. index finger) close to the area where the other finger was, instead of getting a down event, i get a move of the first finger to
the down position of the second finger. It is a little tricky to reproduce but it is reproducible.
I have a UIView and for the touches i have the following:
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event
{
for (UITouch *touch in touches) {
int index = (int)touch;
CGPoint position = [touch locationInView:self];
NSLog(#"DOWN %d x:%f y:%f", index, position.x, position.y);
}
}
- (void) touchesMoved: (NSSet *) touches withEvent: (UIEvent *) event
{
for (UITouch *touch in touches) {
int index = (int)touch;
CGPoint position = [touch locationInView:self];
NSLog(#"MOVE %d x:%f y:%f", index, position.x, position.y);
}
}
- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event
{
for (UITouch *touch in touches) {
int index = (int)touch;
CGPoint position = [touch locationInView:self];
NSLog(#"UP %d %f %f", index, position.x, position.y);
}
}
When i reproduce it i have the following output
DOWN 510365696 x:891.500000 y:197.000000
MOVE 510365696 x:774.000000 y:263.000000
MOVE 510365696 x:766.000000 y:268.500000
UP 510365696 764.500000 270.000000
where the above MOVE is the down position of the second finger. As you can also see the UP from the first finger which should be in pos (891,197) is totally lost...
I know that i could detect these "jumps" but there is no way to differentiate them from a fast moving finger.
I appreciate any help.
When I tried with your same code, what I observed is that, even the slightest movement in your finger is detected.
When you tap using your first finger and while holding it, if you tap using the second finger, the second finger position is not logged. Actually, what gets logged is the slight variation in point of your first finger which is observed as a move action.
The up position which gets logged is also somewhere near to the first finger touch point.
The down action of the second finger is never detected.
I think the problem is that Multiple Touch option is not enabled.
With your view selected, in the Utilities pane, select Attributes Inspector and from there tick the Multiple Touch checkbox.
Here is a screen shot :
The actual Log when Multiple Touch is enabled will look like this:
DOWN 475396224 x:229.000000 y:6.500000
DOWN 475411840 x:102.000000 y:50.000000
MOVE 475396224 x:231.500000 y:8.000000
MOVE 475411840 x:104.500000 y:51.500000
UP 475396224 231.500000 8.000000
UP 475411840 104.500000 51.500000
Hope this helps !
Related
i followed this wonderful guide about mario-style game:
http://www.raywenderlich.com/62053/sprite-kit-tutorial-make-platform-game-like-super-mario-brothers-part-2
however, i wanted to convert the movement controls to arrow keys, implemented by SKSpriteNodes with names that are detected by:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode* node = [self nodeAtPoint:location];
// other left, up, down arrows with same code here
if ([node.name isEqualToString:#"rightArrow"]) {
self.player.moveRight = YES;
self.rightOriginalTouchLocation = location;
...
}
}
self.player.moveRight is a boolean value (much like moveForward in the guide), that tells the character at update to move.
it is terminated at:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode* node = [self nodeAtPoint:location];
// other left, up, down arrows with same code here
if ([node.name isEqualToString:#"rightArrow"]) {
self.player.moveRight = NO;
}
...
}
however, i encounter the following problem - when i start the touch on the arrow, drag it outside the arrow, and then release the tap, it is not recognized as 'touch ended' for the arrow node (and it doesn't stop moving because of that).
i tried to solve it in many ways (even calculating touch move distance from original location and see if its too far, then cancel movement), but i always manage to reproduce the constant motion problem.
the issue lies with the fact that i can tap two arrows at the same time, so it is not enough to remember the last node tapped.
since i want to allow movement for different directions at the same time, i cant stop all movements in case one button is dismissed. i need to specifically know which button was released so i can stop that direction's movement only.
do you have any ideas for me? should i implement it in another way considering i want the arrow keys, or is there a method to detect which node is released even though it is not at its original location (of the tap)?
Thank you very much!
if anyone is interested about the issue - i had a problem where touching and moving from SKSpriteNode didnt call the touchesEnded for that SKSpriteNode (since the 'release' of the touch was not in the node).
i solved it by keeping the CGPoint of the touch at touchesBegan, and when touchesEnded called, i calculated distances to nearest key using simple distance function:
-(int)calculateDistanceWithPoints:(CGPoint)point1 andPoint:(CGPoint)point2 {
float d = sqrtf(pow((point1.x - point2.x), 2) + pow((point1.y - point2.y), 2));
return (int)d;
}
and then, in touchesEnded, i checked to which key the distance is minimal(i have 3 keys, so which key is most likely to be the key that was 'released') - and did the action required for that key release.
I want to shift the position of some UILabels when the user swipes the screen--specifically, move them the same distance the user swiped their finger.
How would I go about detecting and implementing this?
Thanks
Override touchesBegan:withEvent and touchesMoved:withEvent:. Keep track of the starting location in touchesBegan:withEvent. If repeated calls to touchesDragged (it is called while the drag is happening) show that you are moving fast enough for the action to be a swipe versus just a drag, use the difference between the starting location and the current touch location to animate changing the UILabels.
You can do that by using following methods for UIView.
In touchesBegan you should store the position where the user first touches the screen. So you can identify the left or right swipe when touches ends.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
self.startPoint = [touch locationInView:self];
}
Record the final position on touchesEnded method. By comparing this two positions you can determine the direction of movement.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint endPosition = [touch locationInView:self];
if (startPoint.x < endPoint.x) {
// Right swipe
} else {
// Left swipe
}
}
I hope this helps.
I'm building a game using Sprite Kit that needs quick precise movements following the user's touch. I need to detect if the user has touched on the "Player" in the view, and if they have, when they move, the player sprite needs to move with the touch accordingly.
I have this working now, however, it's not very precise... the more movement input (without lifting your finger), the more offset the sprite gets from the touch location.
Here's the code I'm using right now.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
if (self.isFingerOnPlayer) {
UITouch* touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self];
CGPoint previousLocation = [touch previousLocationInNode:self];
// Calculate new position for player
int playerX = player.position.x + (touchLocation.x - previousLocation.x);
int playerY = player.position.y + (touchLocation.y - previousLocation.y);
// Limit x and y so that the player will not leave the screen any
playerX = MAX(playerX, player.size.width/2);
playerX = MIN(playerX, self.size.width - player.size.width/2);
playerY = MAX(playerY, (player.size.width/2)-3+inBoundsOffset);
playerY = MIN(playerY, (self.size.height - ((player.size.width/2)-3) - inBoundsOffset));
// Update position of player
player.position = CGPointMake(playerX, playerY);
}
}
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
UITouch* touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self];
SKPhysicsBody* body = [self.physicsWorld bodyAtPoint:touchLocation];
if (body && [body.node.name isEqualToString: #"player"]) {
NSLog(#"Began touch on player");
self.isFingerOnPlayer = YES;
}
}
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
self.isFingerOnPlayer = NO;
}
It detects the touch location, checks to make sure that you're touching the player sprite, if you are, then when you move, so does the sprite... but it can get off quite quickly if you're slinging your finger around (as playing this game will cause the player to do).
Can anybody suggest a more accurate way of accomplishing this, that will keep the sprite under the user's finger even when moving around a lot without lifting your finger?
You have to keep in mind that -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event ONLY gets called when the moving finger writes (sorry couldn't help the Inspector Clouseau reference).
In effect what happens is that the user can very quickly move his/her finger from one location to the next and as soon the the finger is lifted, your position updates stops.
What I suggest you do is create a CGPoint property and have -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event store the location in the CGPoint property.
Then in your -(void)update:(CFTimeInterval)currentTime you add the code that actually moves the player to the finger coordinates. This should make things a lot smoother.
I have a UIViewController that has a touchesBegan function and outputs the positions.
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"in");
NSArray *touchesArray = [touches allObjects];
for(int i=0; i<1; i++)
{
UITouch *touch = (UITouch *)[touchesArray objectAtIndex:i];
CGPoint point = [touch locationInView:touch.view];
NSLog(#"point = %f %f",point.x,point.y);
}
}
If I double tap quickly towards the middle of the screen, I get the following output
2012-02-12 21:47:13.522 MoreMost[479:707] in
2012-02-12 21:47:13.523 MoreMost[479:707] point = 698.000000 86.000000
2012-02-12 21:47:13.617 MoreMost[479:707] in
2012-02-12 21:47:13.619 MoreMost[479:707] point = 39.000000 22.000000
why is that second tap being registered as (39,22)...which is like the top left corner of the iPad. However, I was tapping in the middle.
So, I'd like to solve this in two ways:
1) somehow, not let the user double tap (however it seems even when I double tap fast, the touchesBegan function is called on two separate occassions)
2) figure out why that 2nd tap is being registered with the wrong coordinates.
It should be CGPoint point = [touch locationInView:self.view];...not "touch.view"
I want to detect a touch while one finger is still on the screen means that still touch canceled for that is not called... is it possible because i want to ignore that touch and perform operation on the next finger......
Thanks
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//those are all touches on this began call
for (UITouch *touch in touches) {
CGPoint location = [touch locationInView: [touch view]];
CGPoint convertedLocation = [[CCDirector sharedDirector] convertToGL:location];
//those are ALL the touches on screen
NSSet *allTouches = [event allTouches];
You can also keep track of touches by reading the hash code of the touch and saving it using int firstTouchHash = [touch hash].
You can check in the TouchesBegan if the touches set and the allTouches set have the same count (in this case we are touching the screen for the first time), in that case we can save the hash int firstTouchHash = [touch hash]
When we touch the screen again, the allTouches contains more elements than touches and we always know wich was the first touch because we have the hash saved.
(without saving the hash, if we touch with 1 finger, then with a second one and then with a third, when the third touch occurs we dont know which of the 2 previous touches is the first, because they both are in the allTouches set (and a set is an unordered collection of elemenents) )
Also make sure multiple touch is enabled on the view, either through the multipleTouchEnabled property or the Multiple Touch checkbox in the storyboard editor.