Home > iPhone Development > How to Implement Grabbing a Sprite with Cocos2d and Box2d

How to Implement Grabbing a Sprite with Cocos2d and Box2d

With Box2d,  it’s not difficult to implement an app in which we can grab a sprite with touches. The core function we should use is called mouse joint. In this article, I want to show the basic ideas and steps of implementation. Note: this example is based on one of my previous articles – Cocos2d Example – Box2d. The only difference is I moved the codes regarding to the sprite ball from MyScene to BallLayer.

Step 1: Enable the touch. Add the following line of code into the init function of BallLayer.

isTouchEnabled = YES;

Step 2:  Response the touch begin event by implementing ccTouchesBegan method. In this method, we first need to use the touch position (Note: a coordinate and unit conversion is required) to construct a very small area (the touch position is within this small area). Then we find all the shapes that intersect with this small area using AABB queries (b2World::Query). If such shapes exist, we should get their associated bodies and test whether the body is not static and the touch position is inside the body. The body which is not static and contain the touch position will be selected to create a mouse joint. The codes are as follows:

- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
   UITouch* myTouch = [touches anyObject];
   CGPoint location = [myTouch locationInView: [myTouch view]];
   location = [[Director sharedDirector]convertCoordinate:location];
   m_mouseWorld.Set(location.x/PTM_RATIO, location.y/PTM_RATIO);
   if (m_mouseJoint != NULL)
   {
      return;
   }
   b2AABB aabb;
   b2Vec2 d;
   d.Set(0.001f, 0.001f);
   aabb.lowerBound = m_mouseWorld - d;
   aabb.upperBound = m_mouseWorld + d;
   const int32 k_maxCount = 10;
   b2Shape* shapes[k_maxCount];
   int32 count = world->Query(aabb, shapes, k_maxCount);
   b2Body* nbody = NULL;
   for (int32 i = 0; i < count; ++i) 	{
     b2Body* shapeBody = shapes[i]->GetBody();
     if (shapeBody->IsStatic() == false && shapeBody->GetMass() > 0.0f)
     {
          bool inside = shapes[i]->TestPoint(shapeBody->GetXForm(), m_mouseWorld);
          if (inside)
          {
              nbody = shapes[i]->GetBody();
              break;
           }
     }
   }
   if (nbody)
   {
      b2MouseJointDef md;
      md.body1 = world->GetGroundBody();
      md.body2 = nbody;
      md.target = m_mouseWorld;
#ifdef TARGET_FLOAT32_IS_FIXED
      md.maxForce = (nbody->GetMass() < 16.0)? (1000.0f * nbody->GetMass()) : float32(16000.0);
#else
      md.maxForce = 1000.0f * nbody->GetMass();
#endif
      m_mouseJoint = (b2MouseJoint*)world->CreateJoint(&md);
      nbody->WakeUp();
   }
}

Step 3: Response the touch cancelled event by implementing ccTouchesCancelled method. In this method, the created mouse joint is destroyed.

- (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
   if (m_mouseJoint)
   {
      world->DestroyJoint(m_mouseJoint);
      m_mouseJoint = NULL;
   }
}

Step 4: Response the touch move event by implementing ccTouchesMoved method. Get the new touch position, then update the position of the joint bodies (b2MouseJoint::SetTarget).

- (void)ccTouchesMoved:(NSSet*)touches withEvent:(UIEvent*)event{
   UITouch* myTouch = [touches anyObject];
   CGPoint location = [myTouch locationInView: [myTouch view]];
   location = [[Director sharedDirector]convertCoordinate:location];
   m_mouseWorld.Set(location.x/PTM_RATIO, location.y/PTM_RATIO);
 
   if (m_mouseJoint)
   {
      m_mouseJoint->SetTarget(m_mouseWorld);
   }
}

All done!

—————————————

Download: You can download the source codes from here.

Categories: iPhone Development
  1. August 5th, 2009 at 23:54 | #1

    AWESOME!
    I just finished a demo game all using chipmunk, but the features you are getting so easily out of box2d put chipmunk to shame…
    I wonder what kind of project you will use all this knowledge on?

  2. admin
    August 6th, 2009 at 05:28 | #2

    @christopher truman
    It’s also not that complex if using Chipmunk, but I often met some annoying problems with it. Box2d is more reliable and has more comprehensive resources (e.g., documentation) I think.

  3. Sandbird
    August 8th, 2009 at 05:44 | #3

    Do we have to use cocos2d and box2d together ?
    I am trying to find a tutorial or some source code where we only have box2d without cocos2d….Can you please provide a project with just that ?
    Thanks.

  4. admin
    August 8th, 2009 at 07:03 | #4

    @Sandbird
    hi, you don’t have to. cocos2d just provides you a pretty good 2d
    framework to save you lots of time. you don’t need to develop
    from scratch. i have no idea where to find such an example that
    uses box2d without cocos2d, but i think if you’re
    familiar with graphics development in iPhone, it won’t be difficult
    to integrate box2d into your project because the core of using
    box2d is just defining an updating scheduler to update the world,
    but you don’t need to know how box2d updates the world. The rest
    things you need to know are reading the documentation to find
    the details of setting parameters and define the bodies, shapes, etc.

  5. Sandbird
    August 8th, 2009 at 07:29 | #5

    I am running this and i get just a black screen.

    No errors in my console and the programs seems to run because i can press the Home button and go back to the menu. Its not crashing or anything.
    I’ve imported the cocos2d and the box2d libraries…Everything seems fine oO
    Oh, i am using 2.2.1 sim

  6. Sandbird
    August 8th, 2009 at 07:33 | #6

    hi again, thanks for your answer. I was asking to just have box2d cause i dont wanna learn a new engine to work on another engine and then we have opengl also and core animation and quartz and its all a big programmatic “soup”.
    I am still in the learning process i know but i dont want to have to learn 10 things to do 1.

    Besides….can we make a box2d object or a cocos2d obj and then use Opengl to manipulate them ? For example make a box with box2d and use quartz to map a texture on it ?

    Thanks again.

  7. admin
    August 8th, 2009 at 07:43 | #7

    Sandbird :

    hi again, thanks for your answer. I was asking to just have box2d cause i dont wanna learn a new engine to work on another engine and then we have opengl also and core animation and quartz and its all a big programmatic “soup”.
    I am still in the learning process i know but i dont want to have to learn 10 things to do 1.

    Besides….can we make a box2d object or a cocos2d obj and then use Opengl to manipulate them ? For example make a box with box2d and use quartz to map a texture on it ?

    Thanks again.

    of course you can:) after you update the world in your updating scheduler, you
    just need to simply use quartz to update the texture’s position. that’s it.
    to be honest, using box2d is not that difficult.

  8. Sandbird
    August 8th, 2009 at 09:01 | #8

    Your example is the only thing i found on the net that its a bit standalone…(no demos and tables stuff)
    But when i am running your project i get a weird error

    Argument list too long: recursive header expansion failed at /Developer/Documentation/DocSets/com.apple.ADC_Reference_Library.CoreReference.docset/Contents/Resources/Documents/documentation/Carbon/Reference/QuickDraw_Ref.

    so i tried to make it in a new project putting together the same project again. Thats why i said its running but i see a black screen. Now i am 99.9f% :P sure that i did it correctly.
    Can you please give it a go yourself and tell me why its not working ? I know i am asking too much but if you want in the end cause i bet alot of people just started with this and dont know how to add Target executables in a project, you can also give mine in your examples. I have included the cocos2d and box2d (all latest) in the project as files and not their project files.

    Here is the project file (zipped): http://tinyurl.com/MyBox2D

    And again thanks for your responses. Its a rare thing to see in our days :)

  9. Sandbird
    August 8th, 2009 at 09:33 | #9

    stike that….its working now :)

    I did the whole project again from the beginning and now its working….Dont know what went wrong. You can delete the last post if you want…and sorry for the spam.I’ve been trying to make this work 2 days now :(

  10. Phil
    August 8th, 2009 at 20:02 | #10

    Thanks for these great tutorials. I’ve just discovered your site after spending the last 2 days trying to decide between box2d and chipmunk, and had pretty much given up on box2d because of the lack of working demos and tutorials I could find for the iphone vs chipmunk ones. You’ve given me new hope : )

    Is it possible to add any bounce/spring to the grabbed object, whilst you drag it, so it feels slightly elasticated?

    I’d also be very interested if you’ve had any experience with non flat surfaces with box2d, I’m liking the sound of the edge shapes feature, but not 100% where to start.

    Thanks for the posts, keep it up!

  11. September 3rd, 2009 at 10:45 | #11

    Can anyone tell me how to modify this example to work with cocos2d-iphone 0.8.1 which has a newer version of box2d which no longer has a “Query” method but rather a “QueryAABB” method? I’m having trouble with QueryAABB’s b2QueryCallback parameter.

  12. eshirt
    October 4th, 2009 at 11:38 | #12

    I’d love to see how this changes with the new build of Box2D in Cocos2d 0.8.1 also.

  13. jigar
    October 12th, 2009 at 02:41 | #13

    hi friends..!!
    i’m currently working on iphone app developing..!
    but i want to start game developing using cocos2d …!!
    from where can i start to boost up easily and fast…?!!!!
    i’m a new bee to iphone game development.

  14. XyrisKenn
    October 19th, 2009 at 05:25 | #14

    Thank you for sharing this great tutorial, it was invaluable to help me start with Box2D.
    Might you think it possible to subclass mousejoint into player/enemy type classes?

  15. rahul
    January 7th, 2010 at 02:48 | #15

    how to skin a body using cocos+box2d
    here is my body and i have a image called wood.png

    [B]b2BodyDef groundBodyDef2;
    groundBodyDef2.position.Set(8, 9);
    groundBodyDef2.angle=28;
    //groundBox2.restitution=1;
    b2Body* groundBody2 = world->CreateBody(&groundBodyDef2);
    b2PolygonShape groundBox2;
    groundBox2.SetAsBox(1.5f, .1f);//SetAsEdge(b2Vec2(3,3), b2Vec2(3,0));
    groundBody2->CreateFixture(&groundBox2);
    [/B]

    any idea thanks

  16. February 24th, 2010 at 00:16 | #16

    For anyone wondering how to do this in a more recent version of Box2D (QueryAABB and friends), check out this forum post: http://www.cocos2d-iphone.org/forum/topic/2298

    - Johannes

  17. August 5th, 2010 at 10:49 | #17

    For even more recent cocos2d-iphone and Box2D, see here: http://www.cocos2d-iphone.org/forum/topic/7219#post-42388

  18. admin
    August 5th, 2010 at 11:29 | #18

    @matt
    Hi matt,

    Thanks for your link.

  19. August 6th, 2010 at 02:16 | #19

    @admin my pleasure. share the knowledge! :)

  1. August 10th, 2009 at 21:22 | #1