I have two sprites in my scene that I'd like to move in different directions. I tried the following method from searching around but I can't get anything to work. This is the best I have to date.
If anyone can lend a hand that would be great.
-(void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [touch locationInNode:self];
if(CGRectContainsPoint([redLeash boundingBox], touchLocation))
{
redLeash.position = touchLocation;
}
else if
(CGRectContainsPoint([blueLeash boundingBox], touchLocation))
{
blueLeash.position = touchLocation;
}
}
try this. I typed it up here so there might be some syntax errors but it should work.
-(void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [touch locationInNode:self];
if(CGRectContainsPoint([redLeash boundingBox], touchLocaiton))
{
[redLeash setPosition:touchlocation];
}
else if (CGRectContainsPoint([blueLeash boundingBox], touchLocation))
{
[blueLeash setPosition:touchLocation];
}
}
Related
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.
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];
}
}
}
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
}
need to be able to store 2 touches, how I go about this i how no idea...
this is how I'm doing a single touch
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/////checks whether the screen has been touched and stores its location and converts the coordinates to be avaible for use////
UITouch* myTouch = [touches anyObject];
CGPoint locationLeft = [myTouch locationInView: [myTouch view]];
locationLeft = [[CCDirector sharedDirector]convertToGL:locationLeft];
how would I be able to store the 2nd touch?
Thanks in advance
You should use ccTouchBegan (notice the singular 'touch' instead of 'touches') when you need to handle multi-touches. (IMO people should just ditch the ccTouchesBegan/Moved/Ended and just use ccTouchBegan/Moved/Ended).
Each of ccTouchBegan/Moved/Ended is called for each touch which mean you can easily differentiate between multiple touches. Example:
- (void)registerWithTouchDispatcher {
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:1 swallowsTouches:YES];
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
if (self.firstTouch == nil) {
// we got the first touch
self.firstTouch = touch;
}
else if (self.secondTouch == nil) {
// we got the second touch
self.secondTouch = touch;
}
// return YES to consume the touch (otherwise it'll cascade down the layers)
return YES;
}
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
if (touch == self.firstTouch) {
// we got the first touch
// do stuff
}
else if (touch == self.secondTouch) {
// we got the second touch
// do stuff
}
}
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
if (touch == self.firstTouch) {
// first touch ended so remove both touches
self.firstTouch = nil;
self.secondTouch = nil;
}
else if (touch == self.secondTouch) {
// second touch ended so remove touch only
self.secondTouch = nil;
}
}
Have you tried iterating through the touches like this?
for (UITouch *touch in touches){
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
location = [self convertToNodeSpace:location];