I was wondering if there was a more efficient way to changing the property of a SKSpriteNode in touchesBegan than my current method. The following is my method:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"Start"]){
for (SKSpriteNode *node2 in [self children]){
if ([node2.name isEqualToString:#"Start"]){
node2.texture = [SKTexture textureWithImageNamed:#"button_beige_pressed"];
}
}
}
...
}
Your current logic suggest that you have multiple nodes with the name 'start'. If this is actually the case I suggest creating an array and storing all 'start' nodes in said array.
If however you only have a single node with the name 'start', then no loop is needed as you already have a reference to the node with the touch function.
I found a solution:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKSpriteNode *node = (SKSpriteNode *)[self nodeAtPoint:location];
if ([node.name isEqualToString:#"Start"]){
node.texture = startPressed;
}
}
I was using a Node when I should have been using a spritenode.
Related
I am trying to create an app that creates a sprite node when the touch begins, drags it when the user's finger moves, and drops it when the touch has ended. What I have now simply creates a new sprite for every new touch location:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKSpriteNode *gamePiece = [self pickObject];
gamePiece.position = location;
gamePiece.physicsBody.dynamic = NO;
[self addChild:gamePiece];
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKSpriteNode *gamePiece = [self pickObject];
gamePiece.position = location;
gamePiece.physicsBody.dynamic = NO;
[self addChild:gamePiece];
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKSpriteNode *gamePiece = [self pickObject];
gamePiece.position = location;
gamePiece.physicsBody.dynamic = NO;
[self addChild:gamePiece];
}
}
How do I carry the same, single sprite node across the touch methods? I have searched far and wide for tutorials, but nothing seems to fit exactly what I need.
You can do so by maintaining an instance variable for the touch as well as the SKSpriteNode associdated with that touch.
#implementation MyScene
{
UITouch *currentTouch;
SKSpriteNode *currentGamePiece;
}
Then, modify the touch delegates as follows:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKSpriteNode *gamePiece = [self pickObject];
gamePiece.position = location;
gamePiece.physicsBody.dynamic = NO;
[self addChild:gamePiece];
currentTouch = touch;
currentGamePiece = gamePiece;
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
if ([touch isEqual:currentTouch])
{
currentGamePiece.position = location;
}
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInNode:self];
if ([touch isEqual:currentTouch])
{
currentGamePiece.position = location;
currentGamePiece = nil;
currentTouch = nil;
}
}
The app will crash when two sprites are touched at the same time.
-(void)addEnemy
{
enemy = [CCSprite spriteWithFile:#"enemy.png"];
enemy.position = ccp(winsize.width / 2, winsize.height / 2);
[spriteSheet addChild:enemy];
[spritetiles addObject:enemy]; //spritetiles is NSMutableArray
}
touch code
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [self convertTouchToNodeSpace: touch];
for (CCSprite *target in [spriteSheet children]) {
if (CGRectContainsPoint(target.boundingBox, location)) {
[target stopAllActions];
[spriteSheet removeChild:target cleanup:YES];
[spritetiles removeObject:target];
}
}
}
if I touch any one of the sprite, there's no error, but if i touch two sprites(sometime some sprites' position is nearby), the app will crash, at the code line "if (CGRectContainsPoint(target.boundingBox, location)) {", so how can I fix it? thanks
Updated
Use reverseEnumerator in order to iterate through an array when you may need to remove elements as part of the for loop:
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [self convertTouchToNodeSpace: touch];
for (CCSprite *target in [spriteSheet.children reverseObjectEnumerator]) {
if (CGRectContainsPoint(target.boundingBox, location)) {
[target stopAllActions];
[spriteSheet removeChild:target cleanup:YES];
[spritetiles removeObject:target];
}
}
}
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchlocation = [touch locationInView:self.view];
How would you know when a user touched a certain button and how many times if i have four different buttons.
I'm into C# so forgive me if I'm wrong, but couldn't you just increase an integer whenever the user clicks on an image, then if you wanted to know the amount of clicks, you'd look at this integer?
what you will have to do is take one integer in.h file then in .m file write touchesbegan method like below.
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchlocation = [touch locationInView:self.view];
if([touch isKindOfClass:[UIImage class]])
{
myInt++;//your global integer....
}
}
let me know it is working or not!!!
Happy Coding!!!!
Try to use this one..
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
// ----------you can find using tag and first set button's tag in xib ------
if(touch.view.tag == btnRed.tag)
{
redTouch++:
}
}
My view has a UIPanGestureRecognizer added to it.
The problem is that by the time the touch is recognized as a pan gesture, it has to be moved a few points, and I can't extract the original touch location at the UIGestureRecognizerStateBegan state.
In this state the translationInView: is (0,0), and any subsequent moves are calculated from this point, and not from the original.
Is there a way to extract the original location from the gesture recognizer itself, or do I need to override the touchesBegan: method?
You should be able to set the delegate for the gesture recogniser and implement
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
to get the initial touch.
You can solve this problem by implementing a custom PanGestureRecognizer which saves the original touch point and makes it available to the caller.
I went this route as I also wanted to control the distance moved to trigger a pan gesture.
This might be overkill for your situation, but works perfectly and is less difficult than it sounds.
To get the touchpoint coordinates, you just call:
[sender touchPointInView:yourView]
instead of:
[sender locationInView:yourView]
Here's the code for PanGestureRecognizer.m:
#import "PanGestureRecognizer.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
#implementation PanGestureRecognizer
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
// touchPoint is defined as: #property (assign,nonatomic) CGPoint touchPoint;
self.touchPoint = [touch locationInView:nil];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:nil];
// customize the pan distance threshold
CGFloat dx = fabs(p.x-self.touchPoint.x);
CGFloat dy = fabs(p.y-self.touchPoint.y);
if ( dx > 2 || dy > 2) {
if (self.state == UIGestureRecognizerStatePossible) {
[self setState:UIGestureRecognizerStateBegan];
}
else {
[self setState:UIGestureRecognizerStateChanged];
}
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
if (self.state == UIGestureRecognizerStateChanged) {
[self setState:UIGestureRecognizerStateEnded];
}
else {
[self setState:UIGestureRecognizerStateCancelled];
}
}
- (void) reset
{
}
// this returns the original touch point
- (CGPoint) touchPointInView:(UIView *)view
{
CGPoint p = [view convertPoint:self.touchPoint fromView:nil];
return p;
}
#end
If you use locationInView: rather than translationInView: you will get absolute coordinates instead of relative coordinates. However you do have to start the pan to get input... To work around this your view can be a UIButton and you can trigger a - (IBAction)buttonWasTouched:(UIButton *)button forEvent:(UIEvent *)event connected to "touch down" like so:
-(IBAction)shakeIntensityPanGesturebuttonWasTouched:(UIButton *)button forEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view]; //touch location local cordinates
}
You can use the UIGestureRecognizerState
- (void) onPanGesture:(UIPanGestureRecognizer *)sender {
if(sender.state == UIGestureRecognizerStateBegan) {
origin = [sender locationInView];
} else {
current = [sender locationInView];
// Initial touch stored in origin
}
}
Swift (ish):
var state = recognizer.state
if state == UIGestureRecognizerState.Began {
println(recognizer.locationInView(viewForGesture))
//this will be the point of first touch
}
i create an UIButton on the view,and i want to let the touchesMoved only control the UIButton,not the whole view
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint touchMoved = [touch locationInView:self.view];
}
i want to do like this
if i touch the UIButton,and then the UIButton can be moved with my finger,if i touch other view and my finger is moving on the screen,the UIButton does nothing.
that means the function touchesMoved only has the role of the UIButton,so how can i do it? thanks
I presume the code you show takes place in your custom view controller subclass, and the UIButton is a subview of its view.
Define a simple BOOL in your class that you set to NO first. Then update it in the event handling methods.
// .h
BOOL buttonTouched;
// .m
// in the viewDidLoad
buttonTouched = NO;
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// test wether the button is touched
UITouch *touch = [touches anyObject];
CGPoint touchBegan = [touch locationInView:self.view];
if(CGRectContainsPoint(theButton.frame, touchBegan) {
buttonTouched = YES;
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(buttonTouched) {
// do it here
UITouch *touch = [touches anyObject];
CGPoint touchMoved = [touch locationInView:self.view];
CGRect newFrame = CGRectMake(touchMoved.x,
touchMoved.y,
theButton.frame.width,
theButton.frame.height);
theButton.frame = newFrame;
}
}
// when the event ends, put the BOOL back to NO
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
buttonTouched = NO;
}