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
Related
I am making an SpriteKit game involving sorting colored balls with a rotating funnel that rotates based off where you move your touch. Problem is if someone swipes across the screen really fast, balls will move right through the physicsBody on the funnel. I am changing the zRotation of the funnel with each call to touchesMoved(). Both the balls' physicsbody and the funnel's physicsbody has .usesPreciseCollisionDetection to true. .isDynamic is false for the funnel.
I figure when someone swipes across the screen, the animation is happening so fast that the framerate cannot keep up and as the funnel rotates, there is such a large gap in its rotation that the ball ends up on the other side of the physicsbody in a single frame.
Is there any way to fixing this? I am new to SpriteKit, any help is appreciated. My code is here
I've been fiddling with SceneKit recently and I wanted to make the following thing:
When creating a Game template from Xcode, you get a scene with a ship.
I wanted to animate this ship and orient it according to the relative position of my iPhone after I tap the screen. So for instance, if I hold my iPhone horizontally, tap the screen, this takes the reference attitude of my horizontal iPhone. Then, when I lift it (changing the pitch), I want the ship to orient itself as such.
I've been trying to change my ship node eulerAngles with the attitude pitch yaw and roll as in the following:
CMAttitude * attitude = deviceMotion.attitude
_ship.eulerAngles = SCNVector3Make(-attitude.pitch, attitude.yaw, attitude.roll);
Whenever I do that, the ship goes back to its original position in the scene. I can't seem to understand how to give it a speed in the direction it's facing without making it reset to its original position when I change its eulerAngles.
Ideally, the ship would have some sort of engine power accelerating it in the direction it's facing, while it would still be affected by gravity. How should I do that? Thanks!
I am making a basic app that pushes shapes across the screen and detects collision with Sprite Kit. My first attempt was using moveTo on the nodes. The issue I had was with collision, the objects would rotate around each other instead of bounce.
Therefore I found I need to use applyForce OR applyImpulse.
In this situation I have a circle for example that is position off screen at its start of life. We then determine a target exit point, and want to 'flick'/'push' the node in that direction.
I cannot figure out how to applyImpulse towards the target end position I have as a CGPoint. I need to get this to a CGVector but I am not sure what needs to be done. I had a look around and found some Ray tuts but they just show applyForce or moveTo. I am not sure how to calculate this.
I found a site that explains 2D physics well.
http://www.rodedev.com/tutorials/gamephysics/
With this I worked out what the angle needed to be and have a speed that I can control and it works well.
You can move an object by changing manually the x and y position so you can reach your end point. In the update function you change yourObject.position.x and yourObject.position.y if I have understood correctly your question. If not please be more explicit. Hope that helps.
I have a SpriteKit Scene in which I want to have the effect as if a camera zoom and scale. Does anyone know of any libraries or some easy methods of doing this?
It was very easy to do in other 2D engines but does not seem simple.
I was thinking of doing it from the app delegate, and using the window to zoom since my character does stay around the same position.
The desired effect I would like to accomplish is like that of the start of an Angry Bird level when the camera pans into the level and then the launch doc.
http://www.youtube.com/watch?v=_iQbZ3KNGWQ This is an example of the camera zoom and pans I am talking about.
Thanks for the help.
If you add an SKNode to the SKScene, and make your scene content children of that node instead of direct children of the scene, then you can zoom and pan all of the contained content just by adjusting the xScale, yScale and position properties of the added node. (Any content you did not want scrolled e.g. scores or whatever could then be added to a different SKNode or added directly to the scene).
The adjustment could be done by overriding one of update:, didEvaluateActions, or didSimulatePhysics in your SKScene subclass. The choice would depend on if you are just moving your character around by yourself in update:, or if it also gets moved around by running SKActions or by simulated physics.
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.