I'm kind of a newb in SpriteKit, game dev, as a matter of fact I'm just learning. So I got to a point where I what to move a bunch of nodes towards a users tap location. So far I fought that I might calculate a virtual right triangle and get sin and cos of angles based on the sides. Unfortunately that left me with a very strong impulse that doesn't really consider the user tap location.
Any ideas?
Thanks in advance.
Look up the shooting projectiles section in the tutorial by Ray Wenderlich here:
http://www.raywenderlich.com/42699/spritekit-tutorial-for-beginners
Change the code from the tutorial as follows:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
// 1 - Choose one of the touches to work with
UITouch * touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
// 2 - Set up initial location of projectile
SKSpriteNode * projectile = [self childNodeWithName:#"desirednode"];
//make projectile point to your desired node.
// 3- Determine offset of location to projectile
CGPoint offset = rwSub(location, projectile.position);
// 4 - Bail out if you are shooting down or backwards. You can ignore this if required.
if (offset.x <= 0) return;
// 5 - OK to add now - we've double checked position
[self addChild:projectile];
// 6 - Get the direction of where to shoot
CGPoint direction = rwNormalize(offset);
// 7 - Make it shoot far enough to be guaranteed off screen
float forceValue = 200; //Edit this value to get the desired force.
CGPoint shootAmount = rwMult(direction, forceValue);
//8 - Convert the point to a vector
CGVector impulseVector = CGVectorMake(shootAmount.x, shootAmount.y);
//This vector is the impulse you are looking for.
//9 - Apply impulse to node.
[projectile.physicsBody applyImpulse:impulseVector];
}
The projectile object in the code represents your node. Also, you will need to edit the forceValue to get the desired impulse.
Related
I am trying to draw a circle (small like a dot) at the exact point of a UITouch.
The problem is, when I get the touchPoint from the user touch, I have to then draw the Rect in which to place the circle.
To work out an origin for the Rect, to place it such that, a circle drawn within it will have a centre point at the original touchpoint, I have used Pythagorus' theorem.
However, this approach is not elegant and is not exact.
Is there a better way to do this?
UITouch *touch = obj;
CGPoint touchPoint = [touch locationInView:self.view];
CGContextAddEllipseInRect(context,(CGRectMake ((touchPoint.x - 5.7), (touchPoint.y - 5.7)
, 9.0, 9.0)));
you can do like below
CFFloat radius=10;
UITouch *touch = obj;
CGPoint touchPoint = [touch locationInView:self.view];
CGContextAddEllipseInRect(context,(CGRectMake ((touchPoint.x - radius/2), (touchPoint.y
- radius/2)
, radius, radius)));
I think your approach is fine. What could be more elegant than Pythagorus? As for accuracy, add a few more digits to the constants and you can be more accurate than both the user's sense of his own finger position and the system calculations giving you the centroid of the touch.
What's more, I can't think of a good alternative. (Maybe one: do this with an image view, where the image is a circle, and set the view's anchor point so that it positions in the middle of the touch).
I can't work out how to pass the touch location in touch began into my method that is ran when touch began starts.
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
[self schedule:#selector(moveSprite:)];
return TRUE;
}
-(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
[self unschedule:#selector(moveSprite:)];
}
-(void)moveSprite:(ccTime) delta
{
CGPoint spriteCenter = CGPointMake(player.contentSize.width /2,player.contentSize.height /2);
CGPoint touchPoint; //how to get this touch began?
float distanceX = touchPoint.x - spriteCenter.x;
float distanceY = touchPoint.y - spriteCenter.y;
float angle = atan2f(distanceY,distanceX); // returns angle in radians
player.rotation = angle;
}
I also have a question about [self schedule:#selector: Will this continuously call my move sprite method? As I'm going to want the sprite to continuously move and change rotation accordingly as the touch is held down and the sprites position changes.
My final question is about moving the sprite to the x coordinate of the touch. If I use ccmoveto I can't use velocity to make the sprite slowly increase its speed can I? How would I move the sprite to the touched point increasing velocity? I'm guessing its something to do with delta.
Actually, I don't see any need in your scheduled method. You just can implement
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
method and place your sprite rotation logic there.
To get changeable move velocity, you can use one of CCActionEase subclasses. Wrap your move action in one of them and you will see velocity changes during movement. Something like
id move_ease_in = [CCEaseIn actionWithAction:yourMoveAction rate:3.0f];
[player runAction: move_ease_in];
you can see few examples here
I am working on an app that has a rotating image (the user tapps and drags and the image rotates in a circle tracking their finger). What I am trying to keep track of is how many times the user makes a complete circle. An additional "hitch" is that I also need to know if the user is circling clockwise vs counter clockwise.
Here is the Code that is rotating the image... Please feel free to request additional information.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
long double rotationNumber = atan2(touchPoint.y - originY, touchPoint.x - originX);
totalRotationCount ++;
schedulingWheel.transform = CGAffineTransformMakeRotation(rotationNumber);
offset = (rotationNumber * 100)/14;
dateRibbon.center = CGPointMake(offset, 24);
}
Thanks for the help!
My solution isn't elegant and there might be a cleaner solution I'm missing but this is what I did recently. The trick is to keep track of the angle from the last time touchesMoved: is called. Then, add the delta of your current angle and the stored angel value to your total.
The problem is the "boundaries" that atan2 creates needed ugly code to overcome. Say your lastAngle is 359 and you cross the origin so your next angle is 1. The difference will not be 2 but -358, so whenever you cross that boundary your total will be reset to 0.
Here is what I did:
CGFloat angle = atan2f(center.y - point.y, point.x - center.x);
//Translate to Unit circle
if (angle > 0) {
angle = (M_PI - angle) + M_PI;
} else {
angle = fabsf(angle);
}
CGFloat delta = angle - lastAngle;
//Adjust for boundaries
if (fabsf(delta) > (2*M_PI)-fabsf(delta)) {
BOOL greaterThanZero = (delta > 0);
delta = (2*M_PI)-fabsf(delta);
if (greaterThanZero) {
delta = -1 * delta;
}
}
totalAngle += delta;
lastAngle = angle;
The big/ugly conditional under "Adjust for boundaries" basically just looks to see if there is a shorter angle to get to the new point (So, 2 instead of -258) & assumes that if there is it probably means you crossed that origin and adjusts the delta accordingly.
I translated the Atan2 results so that it represents a full unit circle from 0 to 2π. Bonus side affect, it then accounts for clockwise/counter clockwise movement better than the standard -π to π of Atan2.
To find out what is the total number of the rotations simply sum all the rotation angles in either directions. For clockwise the value of the rotation angle will be positive and for the counter clockwise it will be negative. Then divide it by a pi (~3.14) to get your total number of rotations.
long double rotationNumber = atan2(touchPoint.y - originY, touchPoint.x - originX);
long double totalRotationsAngle += rotationNumber;
Then whenever you want to get the number of full rotations:
double numberOfRotations = floor(totalRotationsAngle/M_PI);
I´ve got a problem with my Xcode-Project and hope you can help me!
I just began coding, so my problem should be very easy to solve.
I wanna make a Pong Game with two paddles and a ball. I have got a value, which is between 0 and 1. If the value is high the paddle should also go up. The paddle position can be changed with "CGPoint", but how can I convert my value to a point?
Please help me.
Thanks and greets from Germany :)
CGPoint is a 2-dimensional point.
struct CGPoint {
CGFloat x;
CGFloat y;
};
typedef struct CGPoint CGPoint;
You can create a CGPoint with CGPointMake(x,y).
When your value is between 0 and 1 you may want to scale either x or y by multiplying with a constant factor.
I have a better idea for the paddle controll:
You can use the users touch imput to controll the paddle:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint pointToMove = [touch locationInView:self.view];
yourImage.center = CGPointMake(yourImage.center.x, pointToMove.y);
}
When the user touches the screen, the paddle moves to the .y position of the touch but stays on its defaulf x cordinates!
That way, the controlls would be more fluid. Just try it out!
Viel Spass beim programmieren :D
This is exact code from my app to do this job
for (id obj in points) {
[self moveToPoint:[((NSValue*) obj) CGPointValue]];
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.