I have managed to make a CCNode draggable with the following code:
- (void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [touch locationInNode:self];
// start catapult dragging when a touch inside of the catapult arm occurs
if (CGRectContainsPoint([_heroContainer boundingBox], touchLocation))
{
NSLog(#"YUM YUM");
}
_foodNode.position = touchLocation;
}
This works, however if I touch and drag anywhere on the screen it moves the CCNode. How would I make it draggable only if it is being touched?
I assume your touchMoved: method is in the scene itself, this is why you get notified independently from where you touch on the screen (and this is correct).
To move your node only when you touch it, try checking in touchBegan: that you touching your sprite, and set a flag variable, that you'll later check in touchMoved:
Something like this in touchBegan::
if (CGRectContainsPoint(_foodNode.boundingBox, touchLocation))
_shouldDragMyNode = YES;
else
_shouldDragMyNode = NO;
And then in touchMoved: only change position if _shouldDragMyNode is YES:
if (_shouldDragMyNode)
_foodNode.position = touchLocation;
Sorry, can't test the code right now. But something like this should work.
Related
I am trying to make a button in sprite kit using SKSpriteNode. I want the button image to change when it is pressed and revert back to old image as soon as the press ends. What i have done till now is following:-
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
self.startTouch = [[touches allObjects][0] locationInNode:self ];
for (UITouch *touch in touches){
CGPoint position = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:position];
if ([node.name isEqualToString:#"missileButton"]) {
TEMissileButtonNode *button = (TEMissileButtonNode*) node;
button.isPressed = YES;
}
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches){
CGPoint position = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:position];
if ([node.name isEqualToString:#"missileButton"]) {
TEMissileButtonNode *button = (TEMissileButtonNode*) node;
button.isPressed = NO;
}
}
}
inside the update method i am calling this method to check if the touch has ended
-(void)changeMissileButton{
if (self.missileButton.isPressed) {
[self.missileButton addMoreMissileButtons];
[self.missileButton setTexture:[SKTexture textureWithImageNamed:#"missileButtonPressed"]];
}else{
[self.missileButton setTexture:[SKTexture textureWithImageNamed:#"missileButtonDeselected"]];
[self.missileButton hideMissileButtons];
}
}
The issue is that the touch doesn't get registered at times. Sometimes it works the way i want. When i touch it, its texture changes and when i remove my finger, the texture reverts back to old texture. But most of the times, the button doesn't react to my touch. Am i missing something?
Use touchesBegan, touchesMoved, touchesEnded.
touchesBegan - check if the button (SKSpriteNode) contains the touch. If so, change you button texture.
touchesMoved- if button does not contain touch, change texture back.
touchesEnded- if touch stayed within button during touchesMoved, change texture back.
Above is the logic for how to efficiently accomplish what you are trying to do. Yes, it is annoying Spritekit decided to abandon buttons.
I need to know if the user is touching a sprite. I can easily tell if he lifts his finger or moves his finger out:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"button"]) {
// Sprite stopped being touched
return;
}
}
However I'm clueless about how to know if the touch ends because the sprite moves out while the user keeps his finger in place. I image I should somehow check inside my update method but I don't know how:
// Called before each frame is rendered
-(void)update:(CFTimeInterval)currentTime {
// Check if the user is holding the sprite
}
How can this be done?
I think you are asking for something like this below. There are myriad ways to come at the problem, but here's a basic way to think about it. Depending on the actual complexity of your game, you might need to do some state checking to protect performance, but you get the gist:
//in touchesBegan and touchesMoved update a property:
_lastTouchPoint = //the touch point in scene, etc, blah blah
//if you care about a specific sprite you could also store it:
_someMovingSprite = theSpriteIJustTouchedOrCareAbout;
//in update
if (CGRectContainsPoint(_someMovingSprite.frame, _lastTouchPoint)) {
//the touch is inside the rect of the sprite
}
//OR
NSArray *spritesContainingTheTouchPoint = [self nodesAtPoint:_lastTouchPoint];
if (spritesContainingTheTouchPoint.count == 0) {
//no sprites are being touched
}else{
for (SKSpriteNode *spriteContainingTouch in spritesContainingTheTouchPoint) {
//do something to sprite
}
}
-(void)update:(CFTimeInterval)currentTime {
CGPoint location = [_lastTouchPoint locationInNode:self];
if (!CGRectContainsPoint(_sprite.frame, location)) {
#NSLog(#"Sprite Released");
}
}
Apple has the documentation on every touch event they provide. The method you're looking for is touchesEnded
Handle it the same way you handle touchesMoved and call any methods you need in order to notify the node that it is no longer being touched.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"button"]) {
// Notify the node that the touch has stopped
}
}
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'm working on an iOS app.
I have a UIView that is being animated. It keeps moving from left to right.
This view has several UIView children. They move within their parent when this is being animated.
When I try to capture user touches on the children (by using a UITapGestureRecognizer), it doens't work as expected. It sort of detect some touches on the children, but not in the position where they currently are.
I've been struggling with this for a while now. Any ideas about how to solve this? I really need to detect which children the user is touching.
Thanks!
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:self];
// Check every child subview
for (UIView *tickerItem in self.subviews)
{
// Check collision
if ([tickerItem.layer.presentationLayer hitTest:touchLocation])
{
MKTickerItemView *v = (MKTickerItemView*)tickerItem;
NSLog(#"Selected: %#", v.title);
// This button was hit whilst moving - do something with it here
break;
}
}
}
There is a UIViewAnimationOptions value...
UIViewAnimationOptionAllowUserInteraction
You can use this in the [UIView animateWithDuration... code.
This will enable the touch.
Just like the title ,when I touch the UIView I want the touch point to be glow, I came up with a idea that to show a glow image on the touch point ,but if by code?any example or idea?thanks for sharing!!
You can try touchesBegan event below Code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if([touch view] ==self.my_view)//Check click your uiview or not
{
CGRect frame = [your_image_view frame];
CGPoint touchLocation = [touch locationInView:touch.view];
frame.origin.x=touchLocation.x;
frame.origin.y=touchLocation.y;
[your_image_view setFrame:frame];
[self.my_view addSubview:your_image_view];
}
}
Thanks..!
Well the first step would be to detect the touch on your UIView for that I would recommend using the high level API UIGestureRecognizer andd add to your view. Then you can use the call back to do whatever you want, perhaps some sort of animation?. If so you can use an animation block to animate your changes.