I am writing a game and when a user touches the left or right side of the screen, a "sustain level" is increased. Currently the increase only happens when the user first touches the screen (my code is below). I want the increase to be applied for as long as the user holds their finger on the screen. What do I have to do?
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event{
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
if (self.x >= touchPoint.x){
self.sustain += 1;
}else if (self.x <= touchPoint.x){
self.sustain += 1;
}
}
increased.Currently the sustain level only increase when you first touch the screen, I want the force to be applied for as long as the user holds their finger on the screen
You won't get any messages while the user holds the finger still; your next message will be touchesEnded. So you need to start a timer and just keep increasing the force as desired every time the timer fires, until you get touchesEnded.
You need to figure out how rapidly you want this increase to happen. Then use a timer mechanism to change the value. This could be an NSTimer that fires repeatedly on a certain interval: you start the timer when you get touchesBegan: and stop it when you get touchesEnded:
If you're using SpriteKit, the scene has a built-in timer that triggers its update: method. You can use a flag to indicate that a touch is present and change the "sustain" value in update:.
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.
So I'm making a minesweeper clone for iOS, and I have an array of UIButtons containing 135 buttons (the minesweeper board). It looks great and theoretically should work great. But I was having trouble detecting which button was being hit. I tried working around the problem by using this code;
UITouch *touched = [[event allTouches] anyObject];
CGPoint location = [touched locationInView:touched.view];
NSLog(#"x=%.2f y=%.2f", location.x, location.y);
int pointX = location.x;
int pointY = location.y;
My goal was to grab the coordinates of the touch and then use some basic math to figure out which button was being pressed. However, it doesn't work. At all. No button is pressed, no function runs, essentially nothing happens. I'm left with a minesweeper board that you can't interact with. Any ideas?
Assign a separate number to the tag of each button. Use the button's target, not the UITouch code. When you get a buttonPress, query the tag.
You could subclass the buttons and then program the what needs to happen when a touch occurs in a button inside of that subclass.
The UIButton * can be accessed by calling:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
on self (a UIView * I imagine). So I suppose you can set the button to the pushed state, and when touchesEnded: is called, set it back.
I am using touchesMoved with a coordinate system to detect and respond to user touches within certain areas of the screen. For example, if I have a virtual keyboard and the user swipes across the keys, it reads the coordinates and responds:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch * touch = [[event allTouches] anyObject];
CGPoint point = [touch locationInView:touch.view];
if(point.y < 333 && point.y > 166 && point.x < 90 && point.x > 20)
{
//do something
}
}
...However, the problem is, if the user slowly drags across the keys, or the border between keys, the method is triggered several times in a row, playing the piano key sound in a stutter.
How can I prevent this stutter? I think setting a minimum delay of 0.25 seconds between each successive if statement triggering would help. Also, this delay would only be for a specific if statement -- I want the user to be able to drag across the keys quickly and trigger different key's if-statement as quick as they want.
Does anyone know how to code something like this?
Try this:
BOOL _justPressed; // Declare this in your #interface
...
- (void)unsetJustPressed {
_justPressed = NO;
}
Then, in your touchesMoved:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if (_justPressed) {
// A key was just pressed, so do nothing.
return;
}
else {
_justPressed = YES;
// Do stuff here
[self performSelector:#selector(unsetJustPressed)
withObject:nil
afterDelay:0.25];
}
}
This way, you set a variable _justPressed to YES every touchesMoved:withEvent: is called (or within a specific conditional in there, depending on what you want to do), and you use performSelector:withObject:afterDelay: to set _justPressed to NO after a certain time period, and so you can just check whether _justPressed is YES when touchesMoved: is called to ascertain whether it was called recently.
Remember, you don't have to return from the method like in the example above, you can simply use _justPressed to check whether you should play the sound, but still perform your other actions. The example is just to give you a basic idea of what to do.
I'm trying to create a program in which when I tap in roughly the same location as where my last touch ended, a circle changes color. I've tried using the previousLocationInView method using logic along the lines of: "if touchesbegan location == previousTouchesLocation, change color to blue." This method didn't work because the touchesBegan and previousTouchLocation coordinates have the same value which always changed the color whenever I tapped the screen regardless of the location. I get the same result if I substitute previousTouchLocation with touchesEnded. Here is the code if anyone thinks that it could help.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
touchLocation = [touch locationInView:self.view];
lastTouchLocation = [[touches anyObject] previousLocationInView:self.view];
distance_x = touchLocation.x - cvx;
distance_y = cvy - touchLocation.y;
previousDistance_x = lastTouchLocation.x - cvx;
previousDistance_y = cvy - lastTouchLocation.y;
if ((previousDistance_x == distance_x) && (previousDistance_y == distance_y)) {
if (([cv.color isEqual:[UIColor redColor]])) {
[cv setColor:[UIColor blueColor]];
}
else
[cv setColor:[UIColor redColor]];
}
}
-previousLocationInView: gives you the previous location of a touch that's currently moving on the screen. It doesn't give you the location of a different, now ended, touch.
What you're going to have to do is, on the first touch, store the touch's location in a property or ivar. Then, on the second touch, compare its location to the stored location. You'll need to allow for some leeway—fingers are not pixel-accurate in the way mice can be—so a touch that's moved ten or twenty pixels should probably count as a double-tap.
Actually, come to think of it, your best bet might be to use a UITapGestureRecognizer with numberOfTapsRequired set to 2. This will handle all the detection logic for you, and you can combine it with other gesture recognizers to build up more complex behaviors.
I asked this question on the Stack Exchange Game Development Site about how to combine the tap and long hold gesture recognizers, and received the following answer:
The way to deal with this is to set a timer once the person taps the phone. The most user friendly scenario that you'd implement would look something like this:
When you detect a tap, set a timer (t = timeToRepeat)
On each frame, decrease the timer by dt
If the timer reaches zero, move the sprite a tile and reset the timer
If the user releases their finger before the first cycle of the timer, move the sprite one tile
Obviously the amount of time that you set your timer to will determine how fast your sprite moves. There are a few variations on this theme depending on the kind of behavior you want as well. For example, you can move the sprite once immediately upon detecting a tap and ignore step #4.
I agree that this is the way to do it, so I am trying to implement this and have come up with the following code to do so:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//This records the time when the user touches the screen
self.startTime = [NSDate date];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSTimeInterval temp = [self.startTime timeIntervalSinceNow];
NSTimeInterval holdTime = temp * -1;
if(holdTime < self.threshold) {
//Tap
}
else {
//Hold
}
}
This code works but I realized that I should call the timer code while the user is holding down on the screen, not after they finish. So is there a way to call the code in touchesEnded while the user is pressing down?
Technically, if the user keeps his/her finger perfectly still there is no method that is called between them. In practice though, touchesMoved gets called a bunch. You should just use an NSTimer though instead of keeping track of the time yourself