How to implement mouse joint in Sprite Kit?

岁酱吖の 提交于 2019-12-14 01:43:38

问题


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


回答1:


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".




回答2:


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.



来源:https://stackoverflow.com/questions/19348419/how-to-implement-mouse-joint-in-sprite-kit

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!