Recently I have started a port to iOS of my game Dark Snake, originally made for Android. I want to take this opportunity to learn something about C++, so I have decided to use the game engine Cocos2d-x. Probably, which I am going to explain in this post is trivial for a C++ developer. However, for a noob like me it has been a little headhache ^_^
Basic example
For this example, I will supose we have got a scene named MyScene with an only button. We need the file MyScene.h to declare the class and MyScene.cpp to define it.
MyScene.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #ifndef __MyProject__MyScene__ #define __MyProject__MyScene__ #include "cocos2d.h" #include "ui/CocosGUI.h" class MyScene : public cocos2d::Scene { public: bool init(); CREATE_FUNC(MyScene); // Callbacks void buttonPressed(); }; #endif /* defined(__MyProject__MyScene__) */ |
Key issues of this code:
- The line 4 imports the main header file of cocos2d-x. This allows us to use the main types, classes, functions and macros of cocos2d-x.
- The line 5 imports the header file of cocos2d-x in which are declared the graphics components (buttons, checkboxes, etc) to build Graphical User Interface (GUI).
- Our class extends from Scene class (defined in cocos2d).
- It’s used the macro
CREATE_FUNC, defined by cocos2d. When this macro is expanded, it will generate code like this:static MyScene* create() { MyScene *pRet = new(std::nothrow) MyScene(); if (pRet && pRet->init()) { pRet->autorelease(); return pRet; } else { delete pRet; pRet = NULL; return NULL; } }
- Looking the generated code, we see that it makes a call to a contructor which does not receive params (as we have not declared any, the compiler creates a default constructor without arguments). Furthermore, it is made a call to method
init()which must return aboolvalue. As this method does not exist, we have to declare it at line 9. - Finally, at line 13 is the declaration of the method which will manage the events over our button.
MyScene.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include "MyScene.h" #include "iostream" using namespace cocos2d; bool MyScene::init() { Sprite* bg = Sprite::create("background.png"); this->addChild(bg); ui::Button* btn = ui::Button::create("button-image.png"); btn->setPosition( Vec2(200.f, 200.f) ); btn->addTouchEventListener( CC_CALLBACK_0(MyScene::buttonPressed, this) ); this->addChild(btn); return true; } void MyScene::buttonPressed() { std::cout << "PRESSED 0" << std::endl; } |
- Method
init()is used to add GUI components. At this instance, we only add a background image (bg) and a button (btn). - Using
addTouchListener()we get that the methodbuttonPressed()manages the events that button btn is going to receive. - To indicate which method must to manage the event, we use the macro
CC_CALLBACK_0passing as argument to it the selector of the method and a pointer to the object which must execute it (in this case is the current object itself, so we pass the pointer this).
Imrpoving the event listener
In the previous example, the method that manages the events, MyScene::buttonPressed(), does not receive any param. Nevertheless, it’s possible that we need some information about the generated event, and for that purpose there are some other macros CC_CALLBACK_n defined by cocos2d-x. Some examples:
Knowing the widget which has generated the event
suppose we want to use the same method to handle events in several graphical components, and we want to act depending on the pressed component. In this case the method for processing the events manager should receive a parameter of type cocos2d::Ref*
void buttonPressed(cocos2d::Ref* pSender); |
Param pSender is a pointer to the touched widget, and we can do a casting to the widget class in order to utilize all of its features. For example:
#define ACTION_PLAY 1 ..... bool MyScene::init() { .... ui::Button* btnPlay = ui::Button::create("play-image.png"); btnPlay->setActionTag( ACTION_PLAY ); .... } ..... void MyScene::buttonPressed(cocos2d::Ref* pSender) { if (((cocos2d::ui::Button*) pSender)->getActionTag() == ACTION_PLAY) { // ... do } } |
To link this method to a graphic component, we have to use the macro CC_CALLBACK_1
1 2 | btn->addTouchEventListener( CC_CALLBACK_1(MyScene::buttonPressed, this) ); |
Knowing the type of the event
If we test the event managers showed previously, we get two events for each touch on the button. This is beacuse an event is generated when the button is pressed, and another event is generated when the button is released. To know what type of event has been generated, we can use a method which receives a param of type TouchEventType:
void buttonPressed(cocos2d::Ref *pSender, cocos2d::ui::Widget::TouchEventType eventType); |
The TouchEventType object is an enum that can has the values BEGAN, MOVED, ENDED o CANCELED. If we want to proceed, for example, only when the button is pressed, we must do something like this:
void MyScene::buttonPressed(cocos2d::Ref *pSender, cocos2d::ui::Widget::TouchEventType eventType) { if (cocos2d::ui::Widget::TouchEventType::BEGAN == eventType) { // ... do } } |
To link this method with a graphic component, we have to use the macro CC_CALLBACK_2
1 2 | btn->addTouchEventListener( CC_CALLBACK_2(MyScene::buttonPressed, this) ); |
I hope this post is helpful for you.
Regards!
Hi Ángel ,
Thank you very much. Your posting is really helpful to me, it saves my day!!
I’m fresh new to iOS development, I was started from scratch 3 weeks ago.
Thanks!
Best Regards!
Fan
Hi Fan,
Many thanks to you for comment! I like this post is helpfull for you.
Regards,