Node that was "touched" wasn't actually touched - ios

i trying to make an app with spriteKit in which when the user touched a sprite node, there's a NSLog message that says that the node was touched.
I tried to do it with comparing the name of the touched node with the name of the node that i check if was touched. The problem is that the system recognise that the node was touched although it wasn't touched. I think that there is a problem with the node area, the system thinks that a node is somewhere while it's absolutely not.
-(void)selectNodeForTouch:(CGPoint)touchLocation
{
if([[touchedNode name] isEqualToString:#"menuPlayButton"]){
NSLog(#"Pressed");
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
CGPoint positionInScene = [touch locationInNode:self];
[self selectNodeForTouch:positionInScene];
}
}
Any ideas what might the problem be?
Thanks in advance

Your selectNodeForTouch: method is completely wrong. It uses touchedNode variable, which is not defined anywhere in the code you posted. This method should look like this:
- (void)selectNodeForTouch:(CGPoint)touchLocation
{
NSArray *nodes = [self nodesAtPoint:touchLocation];
for (SKNode *node in nodes) {
if([node.name isEqualToString:#"menuPlayButton"]) {
NSLog(#"Pressed");
}
}
}

Related

SKSpriteNode button stuck (iOS) after letting go. Advice needed

So... I am working on this small Mario-like game for iOS and have run into a problem that I cannot seem to fix.
When using my controls (see the screenshot) they sometime get "stuck" as in, the iOS device did not notice that I let go of a button. This causes the player to keep on moving in a direction after you have let go of the button.
Edit: It seems that the malfunction happens when I click two buttons at the exact same time.
Hopefully, someone will be able to spot what I can do differently in the following code:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInNode:self];
[self touchStateChanged:touchLocation buttonState:YES];
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
CGPoint prevTouchLocation = [touch previousLocationInNode:self];
[self touchStateChanged:prevTouchLocation buttonState:NO];
CGPoint touchLocation = [touch locationInNode:self];
[self touchStateChanged:touchLocation buttonState:YES];
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
// Some other logic... {...}
[self touchStateChanged:touchLocation buttonState:NO];
}
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInNode:self];
[self touchStateChanged:touchLocation buttonState:NO];
}
}
-(void)touchStateChanged:(CGPoint)location buttonState:(BOOL)state
{
SKNode *n = [self nodeAtPoint:location];
if ([n.name isEqualToString:#"LeftMove"]) {
self.player.moveLeft = state;
}
else if ([n.name isEqualToString:#"RightMove"]) {
self.player.moveRight = state;
}
else if ([n.name isEqualToString:#"Jump"]) {
self.player.didJump = state;
}
}
Screenshot showing buttons:
Thanks :-)
Partly Solved...
Current solution:
Added an instance variable called _touchEvent of type UIEvent and added the following line to touchesEnded:
_touchEvent = event;
Added the following code to my update method:
if (_touchEvent && (int)[[_touchEvent allTouches]count] == 0) {
self.player.moveLeft = NO;
self.player.moveRight = NO;
self.player.didJump = NO;
}
if (_touchEvent && (int)[[_touchEvent allTouches]count] == 1) {
for (UITouch *touch in [_touchEvent allTouches]) {
SKNode *n = [self nodeAtPoint:[touch locationInNode:self]];
if ([n.name isEqualToString:#"Jump"]) {
self.player.moveLeft = NO;
self.player.moveRight = NO;
}
}
}
I think the problem is the location of touch (in -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event method) is outside any node.
Try to implement something like this
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (SKNode *node in yourButtonsArray) {
// Some other logic... {...}
[node setState:NO];
}
}
Or you should take a look at KKButtonBehaviour from KoboldKit framework.

moving 2 objects at same time

In my current ios project, I have dedicated one side of the screen to one object and the other side of the screen to the other object and i have made it so that if you swipe your finger across one side of the screen on the designated object would move. However, I want to make it so that you can move both objects at the same time in different movement but i cannot figure out how to do so. Below is my current code.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
if (location.x <= 270 ) {
[Person setCenter:CGPointMake(location.x, Person.center.y)];
}
else {
[Person1 setCenter:CGPointMake(location.x, Person1.center.y)];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
if (location.x <= 270 ) {
[Person setCenter:CGPointMake(location.x, Person.center.y)];
}
else {
[Person1 setCenter:CGPointMake(location.x, Person1.center.y)];
}
}
you should start handling multiple touches that are delivered in the touches set - loop through all the UITouch objects and do your handling.
Edit:
here's your code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for(UITouch *touch in [event allTouches]) {
CGPoint location = [touch locationInView:touch.view];
if (location.x <= 270 ) {
[Person setCenter:CGPointMake(location.x, Person.center.y)];
}
else {
[Person1 setCenter:CGPointMake(location.x, Person1.center.y)];
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
for(UITouch *touch in [event allTouches]) {
CGPoint location = [touch locationInView:touch.view];
if (location.x <= 270 ) {
[Person setCenter:CGPointMake(location.x, Person.center.y)];
}
else {
[Person1 setCenter:CGPointMake(location.x, Person1.center.y)];
}
}
}
If you move the -touchesBegan & touchesMoved code into the Person view class rather than the view/or viewController class it is in currently then those views can handle touches independent of each other and simultaneously.
*Edit: More info:
Currently you are handling touches events (in I'm guessing a UIViewController) using the code you pasted above, if you moved that code into your Person class you have created you could make the code simpler and achieve the result you desire.
This will have the effect that the Person will decide if it is being touched and where and will move itself accordingly.
In your Person.m file add this code,
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:self.superview];
[self moveToLocation:location];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:self.superview];
[self moveToLocation:location];
}
-(void)moveToLocation:(CGPoint)location{
CGFloat halfOfPersonWidth = self.bounds.size.width /2.0f;
CGFloat halfOfSuperviewWidth = self.superview.bounds.size.width/2.0f;
// Stop Person from going off left screen edge
if ((location.x - halfOfPersonWidth) <= 0.0f){
return;
} // Stop Person from going off right screen edge
else if ((location.x + halfOfPersonWidth) >= self.superview.bounds.size.width){
return;
}
if (self.center.x < halfOfSuperviewWidth) {
// Person is on the left of the screen and should not move to right side
if ((location.x + halfOfPersonWidth) > halfOfSuperviewWidth) {
return;
}
} else{
// Person is on the right of the screen and should not move to left side
if ((location.x - halfOfPersonWidth) < halfOfSuperviewWidth) {
return;
}
}
// If we have made it this far then we can move the Person
// move to touch location on x axis only
[self setCenter:CGPointMake(location.x, self.center.y)];
}
Now in your View Controller class you can delete the original -touchesBegan & -touchesMoved code that you pasted here, Or just comment it out if you want to be cautious
/* put slash asterisks above the code and asterisks slash below the code to comment out */
If you build and run you should be able to move each Person view around as before but if you put a finger on each Person view at the same time you should be able to move them simultaneously.

Dragging 1 sprite node relevant to finger movement?

I have created a node called and i have amanged to make it drag from across the screen when the node it touched and dragged. For some reason this one method (code below) lets me drap any node on the screen. How can i make effect only the one "testNode2".
Also i would the node to drag to the movement of the finger but this can work if the finger is touch anywhere on the screen, not just when the node itself is touched? (but not jump to the position of the finger, just move relevant to the finger movement). For example is the screen is pressed anywhere then dragged 100 pixels left the node will move 100 pixels left.
my code is below
-(void) colourSprite2:(CGSize)size {
self.testNode2 = [SKSpriteNode spriteNodeWithColor:[SKColor greenColor] size:CGSizeMake(30, 30)];
self.testNode2.position = CGPointMake(self.size.width/2, self.size.height/1.1);
self.testNode2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.testNode2.frame.size];
[self addChild:self.testNode2];
}
-(void)touchesBegan:(NSSet*) touches withEvent:(UIEvent*) event
{ self.testNode2 = [self nodeAtPoint:[[touches anyObject] locationInNode:self]]; }
-(void)touchesMoved:(NSSet*) touches withEvent:(UIEvent*) event
{ self.testNode2.position = [[touches anyObject] locationInNode:self]; }
-(void)touchesEnded:(NSSet*) touches withEvent:(UIEvent*) event
{ self.testNode2 = nil; }
The touch delegate returns an NSSet of touches which holds multiple UITouch objects, each of which correspond to a touch relevant to the object the delegate methods are implemented in.
In your case, the node will move itself to the position of any touch the delegate encounters. This includes multiple touches on the screen.
You should read about UITouch and the UIResponder classes.
The solution for your problem will be to keep a track of the specific touch that is being used to move the node.
Maintain a UITouch object as an instance variable:
#implementation MyScene
{
UITouch *currentTouch;
}
Then keep a track of the specific touch as follows:
-(void)touchesBegan:(NSSet*) touches withEvent:(UIEvent*) event
{
UITouch *touch = [touches anyObject];
SKNode *node = [self nodeAtPoint:[touch locationInNode:self]];
if (currentTouch == nil && [node isEqual:self.testNode2])
{
currentTouch = touch;
}
}
-(void)touchesMoved:(NSSet*) touches withEvent:(UIEvent*) event
{
UITouch *touch = [touches anyObject];
if ([touch isEqual:currentTouch])
{
self.testNode2.position = [touch locationInNode:self];
}
}
-(void)touchesEnded:(NSSet*) touches withEvent:(UIEvent*) event
{
UITouch *touch = [touches anyObject];
if ([touch isEqual:currentTouch])
{
self.testNode2 = nil;
currentTouch = nil;
}
}

How to detect touches in physicsBody area?

I did create SKSpriteNode using this code and physicsBody is circle. How I could check that user did touch eare in the physicsBody?
self = [super initWithColor:[SKColor clearColor] size:CGSizeMake(200, 200)];
//...
self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.size.width/2.0f];
self.userInteractionEnabled = YES;
I did found solution for my problem using this code:
CGMutablePathRef pathRef;
// Fill pathRef with points and create phisycs body using pathRef.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self.scene];
if (CGPathContainsPoint(pathRef, nil, touchLocation, NO)) {
// If we are here it means user did touche physic body.
}
}
The easiest solution would be to create a second invisible sprite, with the size you need, and place it directly over the area you want to record touches on.
The other option is to store ALL the coordinates which trigger a valid touch and compare them against the actual touch coordinate in your touch method, like this:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self.scene];
NSLog(#"%#", NSStringFromCGPoint(touchLocation));
// Compare the touchLocation coordinate against your list of acceptable coordinates...
}

Detect touch on UIView

I have a node that acts like an UIButton and a node that performs an action when the screen gets touched.
The problem is that the lion jump action does not get fired.
In addition: both names of the nodes are correct.
My code for the UIEvent:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode: self];
SKNode *node = [self nodeAtPoint:location];
UITouch *lionTouch = [touches anyObject];
CGPoint locationLion = [lionTouch locationInView:[self view]];
SKNode *lionNode = [self nodeAtPoint:locationLion];
if ([lionNode.name isEqualToString:#"veggieLion"]) {
if (!lionIsJumping) {
[self lionJump];
lionIsJumping = YES;
}
}
if ([node.name isEqualToString:#"repalyButton"]) {
// Button action
}
}
Apple documentation for SKNode says the method nodeAtPoint "Returns the deepest descendant that intersects a point."
I've had this trouble in the past. where the method would return the SKSpriteNode that I had used for the background, even if I were definitely tapping the target sprite/node!
Since you are checking for two nodes, I would approach it in the following way:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode: self];
SKNode * lionNode = [self childNodeWithName:#"veggieLion"];
SKNode * replayButton = [self childNodeWithName:#"repalyButton"];
if ([self.lionNode containsPoint:location]) {
if (!lionIsJumping) {
[self lionJump];
lionIsJumping = YES;
}
return;
}
if ([self.replayButton containsPoint:location]) {
// Button action
return;
}
/* test for other targets */
}

Resources