How to Make a Platformer Game with Cocos2D-X

Objective C vs C++

Are you a game developer transitioning from Cocos2d iPhone to Cocos2d-X? From Objective C to C++?

C++ used to be my language. It was all I wrote computer games in for over 10 years. There's a certain level of power, control and efficiency in using C++.

When the iPhone came out, the Objective C language became a hot topic. I was so excited about the thought of writing games for iPhone that I forced myself, despite wariness at [[funny foreign] syntax], to learn Objective C and begin writing a game. Boy was I surprised when I came to love Objective C's simple elegance and powerful Foundation classes.

Now, after nearly three years of making games in Objective C with Cocos2D-iPhone, I've discovered Cocos2D-X and unearthed a whole new love for C++.

C++ is a Manual Transmission Car

Tire Reflection

If Objective C is an automatic transmission car, then C++ is a manual.

One might argue that the automatic car is superior because it's easier. However, that one hasn't had the experience of clutching into neutral across a gravelly patch to avoid spinning the tires.

C++ has more speed and control, yet it requires a greater level of skill from the programmer.

For example, C++ saves execution time by not checking pointers before you dereference them. This increases the speed of your game and your level of control, yet it makes assert statements a debug-mode necessity.

Sending Messages to nil Objects

Here we have one of the most fundamental differences between ObjC and C++.

With Objective C, you can safely send messages to nil objects (though this comes at the cost of slightly slower program execution).

// in Objective C
Node* node = nil;
 
// this will not crash, it will just do nothing
[node setPosition:Point(10,10)];

In C++, if you try to dereference a null pointer, you'll trigger a runtime error.

// in C++
Node* node = nullptr; // nullptr introduced in C++11

// this will crash
node->setPosition(Point(10,10));

// using a safeguard, this will not crash
if(node)
  node->setPosition(Point(10,10));

// using an assertion will trigger a debuggable error
assert(node);
node->setPosition(Point(10,10));

Behind the scenes, ObjC basically implements a nil object which essentially returns zero for any method call. This makes programming a little safer and easier. However, it also can allow bugs to go unnoticed.

Null pointer dereference

C++ doesn't check a pointer before dereferencing it. As illustrated above, if the node is null, then C++ will execute the setPosition function given a null this pointer. When a member variable is accessed via the null pointer, your program crashes.

One solution is to use an assert which only gets compiled into non-distribution builds. If the expression being asserted evaluates to false, then the program aborts. If you've got a debugger attached, then it will pause on the assert line so you can see exactly where the code went wrong.

Node* node = nullptr;

// this will pause execution when debugging
assert(node);

Since assert is typically defined as a macro, one of the pros to using it is that in distribution builds (or any build which defines NDEBUG), the macro evaluates to nothing. Essentially, the compiler doesn't produce any binary code for it. This gives you the security of assertions in debug builds and the raw speed of no assertions in distribution builds.

Essentially this makes C++ a little more efficient and a bit easier to catch bugs at the cost of the programmer having to be a bit more pragmatic.

Readability

Here's another fundamental difference. Objective C is inherently more readable than C++ because method parameters are built into the name of the method. Consider:

// Objective C
[layer addChild:sprite z:1];
// C++
layer->addChild(sprite, 1);

You see the difference in readability?

Objective C is a bit easier to read because the 2nd parameter to the addChild method is clearly a z value. In C++, it's anyone's guess what "1" means.

Modern day Integrated Development Environments (IDEs) like Xcode mitigate the issue when you are writing code because of auto-completion. You start typing addChild and it gives you the parameter types and names. This makes it clear what the parameters are when you are writing the code. However, if you take some time off from the project and come back later, you might be a little perplexed when you glance at the addSprite call and wonder about that second parameter.

You can make C++ more readable by using well-named variables or constants like this:

int z = 1;
layer->addChild(sprite, z);

Speed and Portability

portability + protection

We've already talked about how C++ is a faster than Objective C. But what about portability? This is where C++ shines.

C++ is simply more portable than Objective C. It can be natively compiled on many platforms like Windows, Mac, Linux, iOS, Android and even consoles.

When it comes to Android, it is technically possible to compile ObjC if you upgrade the default NDK's GCC compiler, but there's a catch or two. You need all those Foundation classes.

Remember NSObject, NSString and NSArray? Those are all implemented fully and awesomely on the Mac, but not Windows. All those have to be reimplemented in a cross-platform way.

Thankfully, there's projects like Cocotron which aim to do just that. However, when it comes down to areas that are Android-specific, like input and sound, you need a cross-platform library. Something that implements the platform specifics so you don't have to.

That's why C++ and Cocos2dx are so awesome. Cocos2dx handles the platform-specific stuff and does the bulk of it in portable C++.

Beyond speed, portability and readability, we get into more specific differences between the languages, like Objective C's Class and id types.

The Class and id Types

Objective C has a nifty thing called the Class type. It allows you to take the class of an object and store it in a variable.

C++ doesn't have the equivalent of a Class type. It does have runtime type information (RTTI) and can roughly convert a pointer into a string containing the object's class with typeid. However, since the results can vary per platform, the solution is not exact enough to be relied upon.

Objective C's Class type makes creating objects from strings quite nice. Consider the following:

// Objective C
NSString* string = @"Hammer";
Class class = NSClassFromString(string);
id object = [[class alloc] init];
// C++
Tool* tool = nullptr;
std::string string = "Hammer";
if (string == "Hammer")
  tool = new Hammer;

This illustrates the elegance of ObjC's Class type and one possible workaround in C++.

You can also see in the above code we are using ObjC's id type. It is essentially a pointer to any object type. The rough equivalent in C++ is void*, however there are many reasons not to use either.

Laziness

Let's say we have a parent class called Parent, a subclass of it called Child, and Parent has a method called goToSleep.

In C++, you have to redefine goToSleep in Child's interface if you want to override. In Objective C, you can just be lazy and override it in the implementation.

// Objective C
@interface Parent : NSObject
  -(void) goToSleep;
@end

@interface Child : Parent
  // woo! don't have to redefine goToSleep
@end

@implementation Parent
  -(void) goToSleep {}
@end

@implementation Child
  -(void) goToSleep {}
@end
// C++
class Parent
{
  public:
    virtual void goToSleep();
};

class Child : public Parent
{
  public:
    // goToSleep has to be redefined
    // if it is to be overridden
    virtual void goToSleep();
};

void Parent::goToSleep() {}

void Child::goToSleep() {}

The reason C++ forces a programmer to redefine functions that will be overridden is because it makes the job of the compiler easier. The compiler doesn't have to go checking up the class hierarchy for mystery methods.

Virtual

Virtual Meeting Space

In the above example, we saw the use of the C++ virtual keyword. When virtual is used, it specifies to use the function in the lowest class of the hierarchy first (for example Child's goToSleep as opposed to Parent's goToSleep).

In Objective C, all method calls are essentially virtual. This makes it a bit easier on the programmer, but it comes at a possible performance decrease.

Again, C++ comes through on the manual transmission car analogy. You can specify virtual -- or not -- as appropriate.

The Stack

In Objective C, you can only allocate objects from the heap.

In C++, you can allocate objects on the stack. Stack allocation is faster than heap allocation and also guarantees that the destructor will be called when the object goes out of scope, even if an exception has been thrown.

Here's an example:

// Objective C
// Heap allocations only
Tool* tool = [[Tool alloc] init];
// C++
// Sweet! We've got fast, reliable stack allocations
Tool tool;

Operator Overloading

In C++ you can overload operators. For example, if you were programming a Vector object, you could overload the addition operator so that Vector objects could naturally be added together.

// C++
class Vector
{
  const Vector operator+(const Vector& v) const
  {
    return Vector(x + v.x, y + v.y, z + v.z);
  }
}

// this '+' calls the overloaded operator+
// isn't it nice?
Vector v1,v2,v3;
v3 = v1 + v2;

In Objective C, it's not possible to overload operators. You can workaround that by creating worded methods:

// Objective C
v3 = [v1 addVector:v2];

However, this makes Objective C a bit wordy when it comes to some things:

// Objective C
NSString* a = @"something";
NSString* b = @"else";
if ([a isEqualToString:b])
  [self doSomething];
// C++
string a = "something";
string b = "else";
if (a == b)
  doSomething();

Privates

Objective C doesn't technically have private methods. However, the workaround using a blank category in the implementation is quite nice because the private method doesn't have to be declared in the public interface (and hence your code re-indexed or re-compiled because of a header file change):

// In an Objective C header file (.h)
@interface Something : NSObject
  // See? No private method
@end
// In the implementation file (.m)
// Extend the Something interface with a category
// (Blank categories denote private stuff)
@interface Something () // a blank category
  -(void) privateMethod;
@end

In C++, there is the concept of public and private, however the privates have to be declared in the public interface.

If you really don't want to declare a private function in the public interface, you can work around by using static functions in the implementation (which is similar to the Objective C blank category strategy mentioned above).

Class Extensions

As we saw in the above example, Objective C can extend a class with a category.

Categories are pretty neat because a class can be extended anywhere, including the .h, the .m or some other place.

C++ doesn't technically have the ability to extend a class. However, it does have multiple inheritance.

Default Parameters

C++ has one final bit of coolness to discuss: default parameters. A parameter to a function can be given a default, so that supplying it when calling the function is optional.

// C++
class Something
{
  void doSomething(int i, float f = 0.0f) {}
};

Something s;

// can be called with or without the f parameter
s.doSomething(5);
s.doSomething(5, 1.0f);

Objective C doesn't have default parameters, but it can get by with a multiple method workaround:

// Objective C
@interface Something : NSObject
  -(void) doSomething:(int)i;
  -(void) doSomething:(int)i withF:(float)f;
@end

@implementation Something
  -(void) doSomething:(int)i
  {
    [self doSomething:i withF:0.0f];
  }
  
  -(void) doSomething:(int)i withF:(float)f
  {
    // do something with i and f...
  }
@end

Something* s = [[Something alloc] init];

// either method can be called, however
// two separate methods have been implemented
[s doSomething:5];
[s doSomething:5 withF:1.0f];

Conclusion: Mixing C++ & ObjC

So that's about it for the primary differences between ObjC and C++.

Keep in mind that you can always use both. In fact, you can mix them together with Objective C++.

You just rename your implementation file with the .mm extension. That tells your compiler to use Objective C++ and enables either Objective C, C++ or a mix of both to be used.

// In the file AppDelegate.mm
@implementation AppDelegate
  -(void) applicationDidFinishLaunching:(NSNotification*)aNotification
  {
    // Do some Objective C stuff
    window = [[Window alloc] initWithContentRect:...];
    
    // Now switch to C++ and launch Cocos2dx
    Application::getInstance()->run();
  }
@end

Objective C++ is a common thing. As you can see in the example above, it's required to respond to iOS & Mac app notifications and launch Cocos2d-X in the same method.

Got questions? Leave a comment below. You can also subscribe to be notified when we release new chapters.

Next Chapter >

Comments


Comments
  1. alex

    nice tutorial so far! under “Laziness”, just wanted to point out a small error.

    in your C++ parent/child example, I think you meant to have:

    class Child : public Parent {

    }

    • Nat Weiss

      Good catch! Thanks. It’s fixed.

  2. Lalit

    Hello i am fresher in cocos2dx but i love it and i am also learning it. Would you please explain it in more detail. Because i am not getting any documentation regarding it. Thank you

  3. nichos

    Your tutorial is very informative.
    Thanks.

  4. bagusflyer

    Great article. Thanks.

    But there is one thing not very clear. In Objc, we can use something like ARC or we use some conventions so that we know we have to release something which is init, copy, retain, etc. What should I do in cocos2d-x? Should I call delete if I call ::create, new?

    • Nat Weiss

      For non-Cocos2dx objects, yes, that’s the basics of it. Just call delete when you call new.

      For Cocos2dx objects, if you have created something with ::create you do not have to delete or release it because all ::create methods use autorelease.

      If you are not using ::create (instead you are using new and then an init method) then you need to release manually. Releasing an object entirely (reference counter down to zero) will automatically call the CCObject’s delete.

      I prefer to use new, init methods, and manual release so I know exactly when an object’s memory is freed.

  5. Jasper Blues

    Great article – love your enthusiasm for both cool languages.

    Hey, technically an interface with empty brackets is not a “blank category”, but a class extension. There is a subtle difference:

    Class extensions are added to the class at compile time, and so can add ivars (usually in the form of properties). Categories are added at runtime and cannot add ivars (although you could work around this with associative references).

  6. Jack Lin

    great stuff! thanks!

  7. Dan D.

    Ack! NULL dereference is just as illegal in Objective-C as it is in C or C++, but sending a message to nil doesn’t dereference it! Unfortunately the StackOverflow answer linked to is basically completely wrong. nil (and its Class-equivalent cousin, Nil) are not NSObjects. They really are NULLs (though this is technically implementation-defined, it’s unlikely to change), but when you send a message to an object, you’re really calling a function named objc_msgSend, rather than (in the case of C++) dereferencing a pointer to a function and calling it. objc_msgSend checks if the pointer is nil, and returns 0 if so. It’s certainly still possible to dereference NULL though:

    @interface Foo : NSObject { int i; } @end

    //somewhere else…
    Foo *foo = nil;
    int index = foo->i; //crash!

    Also, the bits on privates and categories isn’t quite right. You can have private ivars in Objective-C (though this isn’t used much because it’s trivial to circumvent, and properties have largely supplanted ivars). The blank category you mentioned is called a “class extension”, and it has semantic meaning to be blank (not just convention) – methods in class extensions are required to be implemented in the current translation unit (.m file, for all intents and purposes), whereas methods in ordinary categories aren’t required to be implemented at all. With these having the name “class extension,” it’s confusing to refer to categories in general as class extension (even if that is what they do). Additionally, by using class extensions for private methods, not only does the indexer not have to rerun, but you don’t have to recompile every file #importing that header file.

    Finally, there’s a gotcha in the code in the operator overloading example: under ARC, all object pointers not explicitly initialized at declaration time are implicitly set to nil. So while the C++ code does execute doSomething, in Objective-C, [self doSomething] is not called because [a isEqualToString:b] returns NO because it’s a message to nil. Pre-ARC, the code will work the same way in Debug mode, and probably crash in Release because you’ll likely be sending a message to an uninitialized garbage value.

    • Nat Weiss

      Thanks for the clarifications.
      EDIT: The chapter has been edited to be more correct. Thanks.

  8. John C. Randolph

    Your article misses the key difference between the languages: calling a method versus sending a message.

    -jcr

  9. Zac Bowling

    Not be spammy but with Apportable, Objective-C is just about as portable as C++ to Android, making Cocos2D work on Android without issues. The issues above are moot if going to Android is your issue. Check it out.

    • Nat Weiss

      Yep, I’ve worked with Apportable before. They actually ported on of my Objective C games to Android. I simply prefer to be able to release for Android myself without having to work with another company, cut them a percentage, wait for them to compile, etc.

  10. Peter

    Nice Tutorial, thank you. There’s one small mistake in the „Privates“-section, it has to be „@interface Something ()“ in the second code-block.

    • Nat Weiss

      Hi there. Yep, it already has the empty parentheses (). Does it not show up in your browser maybe?

  11. satoshi

    Qt, a C++ Framework now can also “sing” on iOS and Android, so give it a try, I thing you’ll love it if you already like C++. Nice blog.

  12. Martin

    Hi,

    On the android platform, does this end up running on a JVM? if so, what is the point to write in something complicated like c++ ?

    • Nat Weiss

      Nope. The JVM is for Java code. The point of using C++ is that it is portable. You write code once and it can be compiled for any platform: iOS, Android, Windows, Mac, Linux, Tizen, Windows Phone, etc. All these platforms support C++. They do not all support Java.