Thanks to some really hard work from Gustavo Ambrozio from raywenderlich.com we now have a new tutorial to explore the intriguing world of Box2D and Cocos2D.

In the first part of his How To Make a Catapult Shooting Game with Cocos2D and Box2D tutorial, Gustavo takes us through the very first steps of setting up the Box2D world, creating the catapult using revolute joints, arming it and launching the projectile. Although this may seem like quite a lot of work, it is actually quite simple to grasp.

As always, I have ported the tutorial code to Cocos2D-x. I tried to keep in line with Gustavo’s naming convention, however, I could not bear to use member variables without marking them in some way. By the way, I went with Cocos2D-x‘s convention of naming the members with m_ as a prefix.

Lessons learned

git tags

I can also say I learned a couple of new tricks with git, namely creating and managing tags. Gone are the days when I created a new project for each tutorial part. With the help of tags I can now manage the chapters in a way that does not spam GitHub‘s project list.

To create a tag you can do the following:

git tag -a BulletCreation

To view the list of existing tags, just do:

git tag

To push the list of tags to the remote server, you can do:

git push --tags

Using std::vector

Gustavo’s tutorial stored references to the bullet in an NSMutableArray. Since bullet is a b2Body object you cannot directly store it into an NSArray, so we employ a trick and store only a reference to it using [NSValue valueWithPointer:bullet]. Trouble is, you don’t have NSValue in Cocos2D-x and CCMutableArray can only store objects derived from CCObject.

So what do we do?

Make use of C++’s standard std::vector!

When you declare a std::vector, similarly to when you declare a CCMutableArray, you need to decide what kind of objects will be stored inside it. For example, in our case, we want to store b2Body objects, therefore we need to declare our vector as

std::vector<b2Body *> m_bullets;

Take note that m_bullets is not a pointer, but a statically allocated vector that stores pointers to b2Body objects.

To add a new object to the vector we do:

m_bullets.push_back(bullet);

bullet, of course, is declared as b2Body *bullet.

Update: push_back will store a copy of your object and not your actual object. See also erase().

To count the number of items in a vector we use the size() function as follows:

m_bullets.size()

To access an item at a given index we use at():

m_bulletBody = (b2Body*)m_bullets.at(index);

And finally, to delete remove objects from the vector we can use the erase() function:

m_bullets.erase(index);

Edit: after Bob’s comment below, I updated the text above to clarify that erasing an element from the vector will not deallocate it, it will merely remove the reference to that object from the vector. Freeing the memory occupied by the object you just removed from the vector is still your responsibility. So, after calling erase() make sure you always deallocate the object if that’s the case (for example, using delete);

Edit edit: After careful re-reading of the documentation, push_back actually stores a COPY of the object you send as a parameter. When you call erase that object is removed from the vector, its destructor called and the memory freed.

You can learn more about std::vector from the C++ Reference.

Project source code

Here are the tags that we have so far, in order:

Part 1

And this is what the project looks like so far after Part 1 was completed:

You can launch one acorn and the camera will follow it all the way to the right when it hits a wall an bounce back to the squirrels. Cute, ain’t it?

Share this article

Tagged on:             

7 thoughts on “How To Make a Catapult Shooting Game with Cocos2D-x and Box2D

  • September 14, 2011 at 11:10 pm
    Permalink

    Just to let you know you’ve introduced a memory leak.

    vector.erase() removes the element from the container but doesn’t deallocate the object.

    Reply
  • September 14, 2011 at 11:19 pm
    Permalink

    Thanks, Bob, I updated the text to make it clearer.

    Reply
  • September 15, 2011 at 10:23 am
    Permalink

    Actually erase does call the destructor of the erased elements.

    As it is stated in the C++ vector reference:

    This effectively reduces the vector size by the number of elements removed, calling each element’s destructor before.

    Reply
    • October 3, 2011 at 7:38 pm
      Permalink

      It will call destructor if you pass an object, but if you pass a pointer to an object, object’s destructor is NOT called.

      Consider following snippet (bit lame):

      ObjectA* pA1 = new ObjectA();

      if (true)
      {
      ObjectA* pA2 = pA1;
      }

      <= You have to call delete if you want to free memory where pA1 is pointing, right? Same with std::vector.

      Reply
  • September 18, 2011 at 9:01 am
    Permalink

    The STL containers were created to help with memory management and is not really suggested to store dynamically allocated objects. auto type objects are automatically deallocated and no manual deallocation is necessary. Simply put… it is not really a good idea to store pointers in an STL container.

    However, it may be useful in some cases to store a pointer inside an STL container, for example to utilize polymorphism (which is quite handy in video games).

    erase() calls the destructor of the element, but in this case… the element is a pointer, not an object. Pointers do not have destructors.

    What is stored in the vector is not objects, they are just pointers to objects.

    Reply
  • October 2, 2011 at 3:15 am
    Permalink

    Memory leak. std::vector stores a copy of a pointer because you have a vector of pointers not objects. The vector copy points to object. On erase, it deletes your pointer not the object. Then you can never delete your object because your pointer is gone from erase.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Email
Print