I have implemented a drag on a sprite object as follows..
-(BOOL)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch * touch = [touches anyObject];
CGPoint location = [[Director sharedDirector] convertCoordinate: [touch locationInView:touch.view]];
[diskSprite setPosition:ccp(location.x , location.y )];
return kEventHandled;
}
but this dragging is not smooth.....
when i drag fast with my thumb the object left from the path.
Thanks
Probably a little bit late but I was searching for a similar thing.
I found this great Tutorial which explained everything:
http://www.raywenderlich.com/2343/how-to-drag-and-drop-sprites-with-cocos2d
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
CGPoint oldTouchLocation = [touch previousLocationInView:touch.view];
oldTouchLocation = [[CCDirector sharedDirector] convertToGL:oldTouchLocation];
oldTouchLocation = [self convertToNodeSpace:oldTouchLocation];
CGPoint translation = ccpSub(touchLocation, oldTouchLocation);
CGPoint newPos = ccpAdd(mySpriteToMove.position, translation);
mySpriteToMove.position = newPos;
}
I had this same issue with my game. Dragging operations appeared jerky. I believe the reason is that touch events aren't generated fast enough to give a smooth appearance.
To solve the problem I smoothed the motion out by running an action on the sprite toward the desired location, instead of setting the position immediately.
I'm not exactly sure what you mean by "the object left from the path". I suppose what you mean is that if you drag your finger over the screen in an arc or circle, that the sprite will "jump" from point to point, instead of follow your finger precisely. Is this correct?
If you want your sprite to follow an exact path, you will have to create a path and then set the sprite to follow it. What you do now is simply set the sprite's position to the touch position, but a "dragged" touch will not create an event for every pixel it touches.
It is fairly easy to create a path for touches received, and code samples can be found here and there. However, if the sprite's speed (in pixels per frame) is too high, you will always see it "jump", even if you use a smooth path.
Example:
You can animate a sprite over a circular path. If you animate this to complete the path in 1 second, you will likely see smooth animation. But if it runs at a high speed, like a full circle in 4 frames, you will just see your sprite at 4 places, not in a smooth circle.
If you wish to 'correct' that, you will need to look into blending, or determine what the maximum speed is for acceptable motion, and slow your sprite down when it's too fast.
I hope that answers your question. If it's not clear, feel free to edit your question, or add a comment to my answer.
look here, what I suggest you to try in such case:
-(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
if (_binCaptured) {
CGPoint location = [self convertTouchToNodeSpace:touch];
[_sprite stopAllActions];
id move = [CCEaseIn actionWithAction:[CCMoveTo actionWithDuration:0.1 position:ccp(location.x, _sprite.position.y)]];
[_sprite runAction:move];
}
}
And it really work smoothly.
I enjoyed this easy way.
Related
I am trying to make my CCSprite come a gliding stop instead of an abrupt halt however I'm not too sure how to do this. I'm moving a CCSprite using CCActionMoveTo and CCEaseOut based on where the user taps; the sprite does move however doesn't glide to a halt. My code is below:
-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLoc = [touch locationInNode:self];
CCActionMoveTo *actionMove = [CCActionMoveTo actionWithDuration:0.2f position:ccp(touchLoc.x, 150)];
id ease = [CCEaseOut actionWithAction:actionMove rate:2];
[_playerSprite runAction: ease];
}
Increase the easing rate to see a more profound effect. 2 is low, and it might not be obvious at that value. Perhaps you would like CCEaseExponentialOut as well.
I would recommend you to read http://www.raywenderlich.com/24824/introduction-to-ai-programming-for-games tutorial, specifically the part on "Steering"
I'm trying to make the boxes move only in horizontal and vertical lines following the touchesMoved event.
I could easily check on every touch event if the center of the box is in the center of the current grid block and then allow a direction change. But that will require the touches to be very precise, and could also lead to bugs because of speedy fingers etc. and then miss the touch event.
What I essential would like to do is make the box move correctly in the grid, even though your touch path is not "perfect". See image below, blue lines indicates box movement, green line is the path of the touch. The box needs to follow the grid closest to the touch path, so it looks like your moving it.
How can i accomplish this?
Edit: Forgot to mention that the movement of the box should be smooth, not jump from grid to grid.
Video
Bonus info: I will also need to flicks boxes left/right up/down with touch momentum, and of course have some sort of collision detection, so boxes can't move through other boxes on the grid. Am I better off with a 2d physics engine?
Smooth animation based on hacker2007 answer:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *aTouch = [touches anyObject];
CGPoint location = [aTouch locationInView:gridContainerView];
for (UIView *subView in gridContainerView.subviews) {
if (conditionForBeingAGridBlock && CGRectContainsPoint(subview.frame, touchPoint))
{
[UIView beginAnimations:#"Dragging A DraggableView" context:nil];
self.frame = CGRectMake(0, location.y, subview.frame.size.width, subview.frame.size.height);
[UIView commitAnimations];
}
}
}
This will move the view you are dragging under your finger, you can add checks yourself to make sure that it doesn't go where it can't. But this will get your a smooth dragging animation.
Seems the problem is what you're setting the frame to in your anitamion:
Currently every time you move the square a little bit you get a new rect here:
CGRect rect = [self.grid getCellRect:self.grid.gridView x:cell.x y:cell.y];
Then in your animation you do this:
self.frame = rect;
Then you animate the box to that rect. So if you move your finger really quickly then you will get that 'skip' effect. If you change it to this:
self.frame = CGRectMake(location.x - self.bounds.size.width / 2, location.y - self.bounds.size.height / 2, self.bounds.size.width, self.bounds.size.height);
try this approach assuming your grid blocks are located in gridContainerView
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint touchPoint = [touch locationInView: gridContainerView];
for (UIView *subView in gridContainerView.subviews) {
if (conditionForBeingAGridBlock &&
CGRectContainsPoint(subview.frame, touchPoint)) {
box.frame = subview.frame;
}
}
}
}
I am trying to develop a game using openGL where i have used GLTextureLoader class to load images and these sprites are moving from left to right with some calculated velocity , i need to detect touch on these images.
Since your purpose is very simple, all you have to do is draw whatever object you have twice, the visible one and another one with just color on an invisible buffer. then you check for the location where the user pressed in the invisible buffer, see what color it is and there you have your object.
http://www.lighthouse3d.com/opengl/picking/index.php3?color1
That is the basic theory.
OpenGL is a rendering API. It only draws stuff.
Techniques like lighthouse3d do work, but glReadPixels is slow.
You should check this on the CPU; that is, for each drawn sprite, test if the touch position is inside.
I found out how to do it ,as per my requirement , As i said i am not the expert in openGL but managed to do it some way around.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[touches allObjects] objectAtIndex:0];
CGPoint touchLocation = [touch locationInView:self.view];
touchLocation = touchLocation = CGPointMake(touchLocation.x, 320 - touchLocation.y);
//self.player.moveVelocity = GLKVector2Make(50, 50);
//NSLog(#"Location in view %#", NSStringFromCGPoint(touchLocation));
//NSLog(#"bounding box is %#",NSStringFromCGRect([self.player boundingBox]));
GameSprite *temp;
for (GameSprite *tempSprite in self.children) {
if (CGRectContainsPoint([tempSprite boundingBox], touchLocation)) {
NSLog(#"touched the player");
temp =tempSprite;
}
}
[self.children removeObject:temp];
}
- (CGRect)boundingBox {
CGRect rect = CGRectMake(self.position.x, self.position.y, self.contentSize.width, self.contentSize.height);
return rect;
}
I have a UIView that contains a number of CALayer subclasses. I am using the following code to detect which layer a touch event corresponds to:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
NSLog(#"%#,%#",NSStringFromCGPoint(point),[self.layer hitTest:point].name);
}
This works fine until the device is rotated. When the device is rotated all current layers are removed from the superlayer, and new CALayers are created to fit the new orientation. The new layers are correctly inserted and viewable in the correct orientation.
After the rotation the hitTest method consistently returns null for the layer. I have noticed that when rotated 180 degrees, the returned layer is what was in that location before the rotation, i.e. touching the top left layer gives the layer in the bottom right when rotated 180 degrees. The coordinates of the hit test are printed as expected with (0,0) being in the top left. I redraw the layers with every rotation, but for some reason they seem to be mapped to being the "correct" way up, with the home button at the bottom. Am I missing a function call or something after handling the rotation?
Cheers,
Adam
Okay, I've found that the following code works in all orientations without any issues (In my case there are no overlapping views so this is appropriate for me):
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *t = [touches anyObject];
CGPoint point = [t locationInView:self];
for(CALayer *layer in self.layer.sublayers) {
CGPoint convertedPoint = [self.layer convertPoint:point
toLayer:layer];
if([layer containsPoint:convertedPointPoint]) {
NSLog(#"%#",layer.name);
}
}
}
While this manual point conversion works correctly, the question still remains as to why the original method call did not. Can anybody enlighten me?
Adam
I have drawn a circle on the iphone simulator using quartz 2d with fill. Is there a way by which I can detect a touch event on that circle?
Thanks
Harikant Jammi
If you have a CGPath of that circle, you can obtain a CGPoint of where the user's finger fell inside of touchesBegan and check to see whether it falls within this CGPath using the following code.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:self.view];
// Depending on your code, you may need to check a different view than self.view
// You should probably check the docs for the arguments of this function--
// It's been a while since I last used it
if (CGPathContainsPoint(yourCircle, nil, location, nil)) {
// Do something swanky
} else {
// Don't do teh swank
}
}
I haven't tested it, but if the surrounding space around the circle is set to an alpha of 0, then maybe only the circle would accept touches. You'd probably have to turn off isOpaque for the view, and have no background color.
If that doesn't work, then you'll have to process the tap location and write code to see if it is within the circle area or not.
Just put a custom invisible button with 0.0 alpha on that circle, so the button will always be able to detect touch.