Touches Began vs. Tap Gesture Recognizer with ARKit - ios

I'm working on a project with ARKit, and I'm trying to add objects when I tap on the screen. I used the touchesBegan function and the app seemed to be working fine, adding objects where I pressed. But I then wanted to add a longPressGestureRecognizer so that I could remove a certain object from my AR SceneView, so I decided to scrap the touchesBegan in favor of a tapGestureRecognizer, since the touchesBegan was taking precedence over the longPressGesture.
My issue is that the tap gesture recognizer started adding the AR Objects in more erratic places and as far as I can tell, the code was similar to my touchesBegan. I can't seem to find the difference, and I would rather work with the tapGestureRecognizer, or if there is a way to have the gestureRecognizer work alongside touchesBegan would also be useful.
My code is as follows for the touches Began function
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSArray <UITouch *> *touchArray = [touches allObjects];
UITouch *touch = [touchArray firstObject];
NSArray <ARHitTestResult *> *resultArray = [_sceneView hitTest:[touch locationInView:_sceneView] types:ARHitTestResultTypeFeaturePoint];
ARHitTestResult *result = [resultArray lastObject];
SCNMatrix4 hitTransform = SCNMatrix4FromMat4(result.worldTransform);
SCNVector3 hitVector = SCNVector3Make(hitTransform.m41, hitTransform.m42, hitTransform.m43);
[self insertCandleWithPosition:hitVector];
}
For the tapGestureRecognizer, my code is as follows:
-(void)addObject:(UITapGestureRecognizer *)recognizer {
CGPoint tapPoint = [recognizer locationInView:self.view];
NSArray <ARHitTestResult *> *result = [_sceneView hitTest:tapPoint types:ARHitTestResultTypeFeaturePoint];
ARHitTestResult *hitResult = [result lastObject];
SCNMatrix4 hitTransform = SCNMatrix4FromMat4(hitResult.worldTransform);
SCNVector3 hitVector = SCNVector3Make(hitTransform.m41, hitTransform.m42, hitTransform.m42);
[self insertCandleWithPosition:hitVector];
}
The only difference with the sets of code is how the touch is initially generated, from what I can tell.
Any help would be highly appreciated.

Related

Can I remove an object from my scene?

Relatively new to ARKit, I was wondering if there's a way to remove a 3D object after it has been placed in a scene.
Just use this function:
Swift:
node.removeFromParentNode()
Objective-C
[node removeFromParentNode];
I suggest reading the documentation of ARKit, SceneKit and their basic classes.
You need to remove a node from the scene graph if you don't want it to appear on screen. You need to remove it from its parent node. Read more about it in SCNNode - Managing the Node Hierachy in Apples documentation.
To remove an object(SCNNode) from your Scene view, you can use Long Press Gesture. Just add the below code in your viewDidLoad.
UILongPressGestureRecognizer *longPressGestureRecognizer =
[[UILongPressGestureRecognizer alloc] initWithTarget:self
action:#selector(handleRemoveObjectFrom:)];
longPressGestureRecognizer.minimumPressDuration = 0.5;
[self.sceneView addGestureRecognizer:longPressGestureRecognizer];
Then handle your gesture recognizer method like below,
- (void)handleRemoveObjectFrom: (UILongPressGestureRecognizer *)recognizer {
if (recognizer.state != UIGestureRecognizerStateBegan) {
return;
}
CGPoint holdPoint = [recognizer locationInView:self.sceneView];
NSArray<SCNHitTestResult *> *result = [self.sceneView hitTest:holdPoint
options:#{SCNHitTestBoundingBoxOnlyKey: #YES, SCNHitTestFirstFoundOnlyKey: #YES}];
if (result.count == 0) {
return;
}
SCNHitTestResult * hitResult = [result firstObject];
[[hitResult.node parentNode] removeFromParentNode];
}
Hope this will help you to solve your problem.
Thanks

How to compare UIImageView name in an if() structure

I have some UIImageView's that i want to move around the screen. But the problem is that the background image is also an UIImageView, and therefor also drag able. I know that this != (not equal to) syntax below is not right, so I hope someone inhere can give tell me how to do it.
ViewController.h :
#interface ViewController : UIViewController{
IBOutlet UIImageView *circle;
IBOutlet UIImageView *star;
IBOutlet UIImageView *triangle;
IBOutlet UIImageView *background;
}
ViewController.m :
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
UITouch *drag = [[event allTouches] anyObject];
if([touch view] != background){ <---- LOOK HERE
[touch view].center = [drag locationInView:self.view];
}
}
Hope to find some help, and thanks in advance :)
You typically don't want to compare objects with normal (in)equality operators, but in this case I think your code will work fine. When you compare objects, the pointer address is compared, so
if (obj1 == obj2) {
// …
}
will only evaluate to TRUE if obj1 and obj2 are the same instance, which they are in this case.
If you want, you can use the isEqual: method:
if ([obj1 isEqual:obj2]) {
// …
}
This test will include the above one, so there's no reason not to use this. This method will also test class equality, and add specific comparisons for the object type. For example, if you compare two NSString objects, the length of the string and the Unicode characters that make up the string must be identical to evaluate to TRUE.
In this particular case, though, either will work.
The easiest way to identify your objects is with tags,
assign a tag to your imageViews
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
[touch locationInView:viewBoard];
if([touch.view isKindOfClass:[UIImageView class]]) {
UIImageView *tmp = (UIImageView *)touch.view;
if (tmp.tag == imgTag) {
// Your Code
}
}
}
Good Luck!

access child nodes in spritekit

Hi all i'm new in spriteKit and objective-c and I would like to create a spriteNode in a method and remove it in another method (same .m file)
In this method i create the sprite:
(void)createSceneContents{ /*in this method i create and add the spaceship spriteNode
SKSpriteNode *spaceship = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
//code...
//Add Node
[self addChild:spaceship];
}
And now i would like to remove the node touching it, but I only know the method for handle touch events is:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
I'm trying to access my spaceship node from there i can't. I've tried everything without success. IS there a way to send a node from a method to another?? Or without sending it, is it
posible to access to a children node from a method where it's not declared??
Try this:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
SKSpriteNode *touchedNode = (SKSpriteNode *)[self nodeAtPoint:positionInScene];
}
You can also set name of your node:
[sprite setName:#"NodeName"];
And you can access it by name:
[self childNodeWithName:#"NodeName"];
Yes, there is a way.
Every SKNode object has a property (of class NSString) called "name". And you can use this name to enumerate children of a node.
Try this:
spaceship.name = #"spaceship";
and in touchesBegan
[self enumerateChildNodesWithName:#"spaceship" usingBlock:^(SKNode *node, BOOL *stop) {
[node removeFromParent];
}];
be careful though, this will remove every node with the name "spaceship" from it's parent. If you want to be more selective about the removal process, you can either subclass SKSpriteNode to hold some values that you can use to figure out if it should be removed or not, or you can add your sprites into arrays based on some logic and then remove using the array.
Good luck!
Using the "name" property works, but if you think you'll only have one instance of that node you should create a property.
#property (strong, nonatomic) SKSpriteNode *spaceship;
Then when you want to add it:
self.spaceship = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
[self addChild:self.spaceship];
Then in touchesBegan:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = touches.anyObject;
SKSpriteNode *node = [self nodeAtPoint:[touch locationInNode:self]];
if (node == self.spaceship) {
[self.spaceship removeFromParent];
}
}

Cocos2d - TMX Collision checking during a CCMoveTo action invoked on an instance of CCSprite

The application responds to touches with the following method - invoking movePlayer:
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[self.player stopAllActions];
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:touch.view];
touchLocation = [[CCDirector sharedDirector] convertToGL:touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
CGPoint diff = ccpSub(touchLocation, self.player.position);
self.distanceToMovePlayer = sqrtf((diff.x*diff.x)+(diff.y*diff.y));
self.playerDestination = touchLocation;
[self movePlayer];
}
movePlayer is defined here. It runs the CCAction that moves the sprite to the touch.
- (void)movePlayer{
CCAction *movePlayer = [CCMoveTo actionWithDuration:self.distanceToMovePlayer/100 position:self.playerDestination];
self.playerMovement = movePlayer;
[self.player runAction:self.playerMovement];
}
I have an invisible TMX Layer called meta on the TMXTileMap that indicates a wall or boundary with the following method that runs every frame:
- (void)checkCollisions:(CGPoint)position{
CGPoint tileCoordinate = [self tileCoordForPosition:position];
int tileGID = [self.meta tileGIDAt:tileCoordinate];
if(tileGID == 49){
NSDictionary *properties = [self.meta properties];
if(properties){
NSString *collision = properties[#"Collidable"];
if(collision && [collision isEqualToString:#"True"]){
[self.player stopAction:self.playerMovement];
}
}
Whenever the sprite touches a boundary, the action stops and the sprite is simply stuck there because the action is immediately stopping whenever it starts as the sprite is still in the boundary.
I have tried setting the collision method to return a boolean which is then tested in the CCMoveTo. Is there a way to call a selector each iteration of a CCAction? Something like CCCallBlockN that runs each frame of the action.
Well, i would probably schedule a selector for a CCAction duration, for a per-frame call back. Assuming you also run some kind of animation on the player sprite, and new in cocos2d 2.0+, you could use a CCAnimation, whereby you could register for a notification to be served with some user-data for each frame.
from the CCAnimation.h file :
/** A CCAnimationFrameDisplayedNotification notification will be broadcasted
* when the frame is displayed with this dictionary as UserInfo. If UserInfo is nil,
* then no notification will be broadcasted. */
#property (nonatomic, readwrite, retain) NSDictionary *userInfo;
ob cit. Not tried, ymmv

Drawing line on touches moved in COCOS2D

I'm developing a game for iPhone using COCOS2D.
In that, I need to draw a line when user drag his finger from a point to another. As far as my knowledge is concern I need to do this in Touches Moved method from where I can get the points.
But I don't know how to do this. Can anybody help me on this?
Kia ora. Boredom compels me to provide an answer on this topic.
Layer part (i.e. #interface GetMyTouches : CCLayer):
-(void) ccTouchesMoved:(NSSet *)inappropriateTouches withEvent:(UIEvent *)event
{
UITouch *touchMyMinge = [inappropriateTouches anyObject];
CGPoint currentTouchArea = [touchMyMinge locationInView:[touchMyminge view] ];
CGPoint lastTouchArea = [touchMyMinge previousLocationInView:[touchMyMinge view]];
// flip belly up. no one likes being entered from behind.
currentTouchArea = [[CCDirector sharedDirector] convertToGL:currentTouchArea];
lastTouchArea = [[CCDirector sharedDirector] convertToGL:lastTouchArea];
// throw to console my inappropriate touches
NSLog(#"current x=%2f,y=%2f",currentTouchArea.x, currentTouchArea.y);
NSLog(#"last x=%2f,y=%2f",lastTouchArea.x, lastTouchArea.y);
// add my touches to the naughty touch array
naughtyTouchArray addObject:NSStringFromCGPoint(currentTouchArea)];
naughtyTouchArray addObject:NSStringFromCGPoint(lastTouchArea)];
}
Node part (i.e. #interface DrawMyTouch: CCNode) :
#implementation DrawMyTouch
-(id) init
{
if( (self=[super init]))
{ }
return self;
}
-(void)draw
{
glEnable(GL_LINE_SMOOTH);
for(int i = 0; i < [naughtyTouchArray count]; i+=2)
{
start = CGPointFromString([naughtyTouchArray objectAtIndex:i]);
end = CGPointFromString([naughtyTouchArray objectAtIndex:i+1]);
ccDrawLine(start, end);
}
}
#end
Layer part II (i.e. #interface GetMyTouches : CCLayer):
-(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
DrawMyTouch *line = [DrawMyTouch node];
[self addChild: line];
}
Remember touching is easy. Knowing what you're doing while touching isn't rocket science.
Finally, if you don't understand anything i've posted ... take up baking. The world needs more chocolate cake producers.
Clarification:
No one learns from cut 'n paste ~ this code was never meant to work without caressing
If you fail to see the humour, you're in the wrong profession
Of note, I love a good chocolate cake. The world really does need more fantastic bakers. That's not an insult, that's encouragement.
"Look outside the square, to find the circle filled with the knowledge that makes life worth living" ~ Aenesidemus.
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *theTouch = [touches anyObject];
CGPoint touchLocation = [theTouch locationInView:[theTouch view] ];
cgfloat x = touchLocation.x;
cgfloat y= touchLocation.y;
printf("move x=%f,y=%f",x,y);
}
Try the above code. It will get the coordinate points when touches moved in iphone.
To draw line, use something like this:
-void draw
{
here is the code for line draw.
}
Update this function in update method.

Resources