I have working implementation of drag and drop feature written in Cocos2d + Box2d for iOS. I need to port it to Sprite Kit. The logic is pretty basic:
when user touching the screen, find sprite under the finger
create mouse joint between found sprite and the scene's physics body, set joint's target to position of touch
when touches moved, update joint's target to new position
when touches ended, remove the joint
Everything is working quite well. I like the way physics is simulated when touches ends - the dragged shape has velocity dependent on dragging speed and direction, so when I left my finger from the screen while moving the sprite, it will continue moving in the same direction, but now affected by gravity and damping.
Unfortunately, I'm not able to reproduce the same effect using Sprite Kit. There is no such joint like mouse joint, but I have tried using other joint types. I almost succeeded with SKPhysicsJointFixed - I'm creating new sprite and joint between it and dragged sprite, when touches moving I'm moving the newly created sprite. Unfortunately it doesn't work like mouse joint in Cocos2d + Box2d - while dragging the sprite, its velocity always equals zero. So, every time I left my finger from the screen, the dragged sprite stops immediately and start falling affected by gravity. No matter how fast I move the finger when dragging, after releasing dragged shape, it behaves exactly the same way.
My question is how to implement mouse joint in Sprite Kit, or how to implement drag and drop feature that works like described above?
Update:
This is a box2d mouse joint example that could give you more clear view on what I am trying to implement using Sprite Kit:
http://blog.allanbishop.com/wp-content/uploads/2010/09/Box2DJointTutorial1.swf
I'm pretty new to iOS development so I guess this might not be the best way, but this is how I solved it and it seems to work pretty smooth actually.
I'm using a UIPanGestureRecognizer to handle the touch event. I set this one up in my didMoveToView: with this code:
UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
An UIPanGestureRecognizer is passed to handlePan: and I check with recognizer.state == UIGestureRecognizerStateEnded if the touched stopped. This is where I want to "release" the object and "let it fly away". For doing so I needed to calculate a few things, first of all I get the velocity of the finger/touch with this code:
CGPoint velocity = [recognizer velocityInView:recognizer.view];
(I use the same method (handlePan:) with other statements for getting the right object when touch starts, and letting the object be under the finger all the time by setting the position of the object to the location of the touch when moving)
Now I know the velocity, but I still don't know how much force I need to apply to the object. You should be able to calculate the force by this formula: Force = mass * acceleration. We know the mass (object.physicsBody.mass) and we know the velocity. To get the acceleration we need the time as well because acceleration = velocity / time.
In my update: method that is called every time a new frame is to be rendered I calculate the difference between the last time a frame was about to be rendered by doing something like this:
self.delta = currentTime - self.lastTime;
self.lastTime = currentTime;
I now can calculate which force that is needed to get the object moving in the velocity I'm "dragging it in". To do this I do the following:
[self.currentNode.physicsBody applyForce:CGVectorMake(velocity.x/self.delta * self.currentNode.physicsBody.mass, -velocity.y/self.delta * self.currentNode.physicsBody.mass)];
I take velocity divided with the difference in time since last frame (self.delta) to get the acceleration, and then multiply it with the mass of the object to get the force needed to keep (or actually getting) the object moving in the same direction and in the same velocity as it was moved by my finger.
This is working for me at the moment and I hope it helps other people too, and please tell me if I got something wrong or if there is a better solution. So far I have not found any "native solution".
If the only issue is velocity, then that is simple to fix.
Subtract the current touch position from the previous touch position to get the velocity between the last and current touch. Use applyForce: to apply this velocity to the body.
In order to prevent the body from moving on its own while dragging, read out the new velocity from the body after calling applyForce: and keep it in an ivar and set the body's velocity to zero.
When the touch ends, set the body's velocity to the one you keep as an ivar.
If this doesn't work, try using applyImpulse: and/or setting the velocity before calling any of the two methods, ie:
sprite.physicsBody.velocity = bodyVelocity;
[sprite.physicsBody applyForce:touchVelocity];
bodyVelocity = sprite.physicsBody.velocity;
sprite.physicsBody.velocity = CGVectorMake(0, 0);
That way you can keep the body's velocity without losing control over it while dragging.
Related
If I had a SKSpriteNode on the screen, how would I be able to flick it so that it would simulate the physics of a soccer ball being kicked. how to throw SKSpriteNode? I am guessing that it would be sort of like this question, but instead of the node following your finger first before it is flicked shouldn't happen. It should look like when you flick you are swiping towards the node and then once your finger hits the node it moves it like the physics of a soccer ball. Can anyone give me a place to start in order to achieve this??
I would recommend using the touchesBegan method and touchesMoved method. Basically, keep track of the last position of the touch, along with the timestamp (using NSTimeInterval), and on each touchesMoved check if the soccer ball has the touch location inside it (using [soccerBall containsPoint:location]. Then, calculate the force that you should impart on the ball (compare timestamps, and compare positions), and then run:
[soccerBall.physicsBody applyImpulse:CGVectorMake(dx, dy)];
Replace dx and dy with the results of your calculations. You may wish to add a scale value to make sure that you get the results you are looking for.
I'm trying to figure out a method for rotating a SKSpriteNode object while it is in-flight (being affected by gravity) along an arc path. I'm using SpriteKit and throw the object using applyImpulse. The problem is that the object, despite traveling in an arc path in the air, stays in the same position.
Imagine an archer shooting an arrow. The arrow is shot upwards and should point upwards in that direction. Once the arrow starts falling along the arc, it should begin to rotate downwards.
Is there some way to automate this using the SpriteKit physics? Should I throw the arrow a different way instead of using applyImpulse? Do I need to come up with some algorithm by myself for the rotation based on the objects velocity?
In your didSimulatePhysics or your update you can rotate your sprite towards its vector. Not sure theres a way to automatically make this happen.
let angle = atan2(mySprite.physicsBody!.velocity.dy, mySprite.physicsBody!.velocity.dx)
mySprite.zRotation = angle
I have a game with a character that can cast fire balls. Right now in my game, when I tap anywhere on the screen, I shoot a fireball at my touch point. For this fireball I'm using an SKEmitterNode where I've created a fireball particle emitter.
The problem I'm running into is, my fireball has an angle set already, but I want that angle to change based on where I tap, so that the trailing flames are behind the fireball, not going up or down or whatever I've set it to in the sks file.
I've never done something like this, is there something built into swift already for calculating angles? I can't find much on google
There are two options:
1) first one is that you can use :
fireBall.moveTo(touchLocation.x, touchLocation.y)
function so you can avoid use angles (if I understand you correctly). touchLocation is CGPoint of location of the touch event.
2) second one use as it was said before you will get an angle:
atan2(deltaY,deltaX)
What I'm attempting to recreate is the draggable arrow that's used in the popular iOS game called "Fragger", complete with both adjusting the rotation AND the strength of the pull all at the same time based on a finger drag but using SpriteKit - I believe they did it in Cocos2D.
I'll start by saying that I've honestly spent many weeks relearning trigonometry (http://www.mathsisfun.com/ is a great resource) and combing through Ray Wenderlich's tutorials (http://www.raywenderlich.com/35866/trigonometry-for-game-programming-part-1) but I can't find a solution for executing a "collective" drag rotation AND strength on a SpriteKit object.
Yes, it's relatively easy to set an anchor point and rotate that object (I'm using an arrow) so that it's pointing at your finger and thanks to Ray, I've got a really smooth action utilizing the following:
float angleRadians = atan2f(firstFingerTouchY - _arrow.position.y, firstFingerTouchX - _arrow.position.x);
float angleDegrees = RADIANS_TO_DEGREES(angleRadians);
float rotateDegreesPerSecond = 180 / 0.5; // Would take 0.5 seconds to rotate 180 degrees, or half a circle
float degreesDiff = (angleCurrent - angleDegrees) * 1.0;
float rotateDuration = fabs(degreesDiff / rotateDegreesPerSecond);
however, that's only part of the equation ...
Again, the first drag is fine as the angle of rotation is derived from the fulcrum of the arrow (it's anchor point) and where ever my finger ends up. However, let's say due to the limit of the screen size I can't get the angle I desire in a single drag, so I lift up my finger and then place it back down to continue the drag? Well I can't use the above mentioned code because as soon as I place my finger down for the second time, the arrow's rotation jumps over to where my finger now starts - again ending the same way.
So I thought the solution would be to not use the anchor point of the arrow at all, but instead use the initial finger touchpoint (as the new anchor) and calculate the arrow's angle based on that touchpoint and where ever my finger moves to? That works in theory, however when you then try to factor in the "strength draw" aspect that's represented in "Fragger" (where pulling the finger closer or farther away from the arrow adjusts the strength of the grenade throw), then you're not moving your finger towards the arrow to weaken the pull, but instead towards where ever you initially touched - which is not only visually difficult but as a bonus creates a lot of rotational jerky-ness as you get your finger closer to the origin.
I've been working on it for about a month now (did I mention that I hate math) trying to create a homogenized method (SIN ... COS ... TAN ... I hate you all!) to do the following:
First pull will rotate the arrow based on the direction of the pull but NOT automatically point to your finger (initial angle)
The ability to lift my finger up, place it back down and drag to ADD TO or SUBTRACT FROM the current rotation of the arrow. (delta angle)
The drag of my finger towards or away from the anchor of the arrow (relative to the current angle of the arrow) will adjust the strength of the "pull" accordingly. For example, if the arrow is already pointing straight up, then continuing the drag straight up would increase the strength and down would decrease the strength respectively. (hypotenuse derived from arrow anchor point).
I also need to be able to use another finger (2 finger touch) to assist in the rotation/strength calculation, so that adds even more chaos into my cluster ...
If you've ever played the 'Fragger' game, you'll recognize the complexity of this single finger action which is also it's functional beauty. I can get pieces of it working - I can rotate it, I can adjust a color mask to indicate strength - but not all of it working together. Perhaps I'm going about it completely wrong, however every example I find online stops at simply rotating an object to point to your finger with every new touch drag ending with the same results.
I am working on a cocos2d-x based game on iOS platform.
In my game, I need to detect circular gesture over the bounding box of a particular sprite.
I have the solution to detect the circle when a touch event is ended at
http://blog.federicomestrone.com/2012/01/31/creating-custom-gesture-recognisers-for-ios/
But, it is required that the circle should be detected during onTouchMove and not onTouchEnded.
Also, I need to know the direction in which circle is being created, i.e. clockwise or anti-clockwise. and change of direction during finger move.
Another requirement is to count the number of circles completed while the finger is moving.
Need some help with this kind of circle gesture detection.
Thanks in advance.
During a touch move event you can determine the angle from the center of the sprite to the touch position using the following (pseudocode):
angle = atan2(touch.y - spriteCenter.y, touch.x - spriteCenter.x);
Then find the angular offset since the last measurement and add it to a running tally:
angleOffset = atan2(sin(angle-lastAngle), cos(angle-lastAngle));
angleRotated += angleOffset;
|angleRotated| will measure how many revolutions were made and in which direction they occurred.
You may also want to take the distance between the touch and the sprite into consideration to ensure the shape is circle-like.