{"id":310,"date":"2015-04-09T07:39:54","date_gmt":"2015-04-09T06:39:54","guid":{"rendered":"http:\/\/www.pixnbgames.com\/blog\/?p=310"},"modified":"2015-04-12T09:49:09","modified_gmt":"2015-04-12T08:49:09","slug":"como-anadir-maneadores-de-eventos-a-botones-en-cocos2d-x-3-x-y-cpp","status":"publish","type":"post","link":"https:\/\/www.pixnbgames.com\/blog\/c\/como-anadir-maneadores-de-eventos-a-botones-en-cocos2d-x-3-x-y-cpp\/","title":{"rendered":"C\u00f3mo a\u00f1adir maneadores de eventos a botones en Cocos2d-x 3.x y C++"},"content":{"rendered":"<p style=\"text-align: justify;\">Recientemente he empezado a desarrollar un port a iOS de mi juego <a title=\"Dark Snake \u2013 Android\" href=\"http:\/\/www.pixnbgames.com\/blog\/libgdx\/dark-snake-android\/\">Dark Snake<\/a>, hecho originalmente para Android. He querido aprovechar la ocasi\u00f3n para aprender algo de C++, por lo que he decidido usar el motor <a title=\"Sitio oficial de cocos2d-x\" href=\"http:\/\/www.cocos2d-x.org\" target=\"_blank\">Cocos2d-x<\/a>. Aunque seguramente lo que explico en este art\u00edculo ser\u00eda algo trivial para un desarrollador que conozca bien\u00a0C++, para un novato como yo ha supuesto un peque\u00f1o\u00a0quebradero de cabeza ^_^<\/p>\n<h2 style=\"text-align: justify;\">Ejemplo\u00a0b\u00e1sico<\/h2>\n<p style=\"text-align: justify;\">Para este ejemplo, voy a suponer que tenemos una scena llamada <code>MyScene<\/code>\u00a0con un s\u00f3lo bot\u00f3n. Necesitamos el fichero <em>MyScene.h<\/em>\u00a0para declarar la clase y <em>MyScene.cpp<\/em> donde se define.<\/p>\n<h3 style=\"text-align: justify;\">MyScene.h<\/h3>\n<pre lang=\"Cpp\" line=\"1\">#ifndef __MyProject__MyScene__\r\n#define __MyProject__MyScene__\r\n\r\n#include \"cocos2d.h\"\r\n#include \"ui\/CocosGUI.h\"\r\n\r\nclass MyScene : public cocos2d::Scene {\r\npublic:\r\n    bool init();\r\n    CREATE_FUNC(MyScene);\r\n\r\n    \/\/ Callbacks\r\n    void buttonPressed();\r\n};\r\n#endif \/* defined(__MyProject__MyScene__) *\/\r\n<\/pre>\n<p style=\"text-align: justify;\">De este trozo de c\u00f3digo se pueden destacar varias cosas:<\/p>\n<ul>\n<li style=\"text-align: justify;\">La linea 4 importa el fichero\u00a0cabecera de cocos2d-x, lo que nos permitir\u00e1 usar sus tipos, clases, funciones y macros principales.<\/li>\n<li style=\"text-align: justify;\">La linea 5 hace la importaci\u00f3n de los componentes de cocos2d-x necesarios para crear Interfaces Gr\u00e1ficas de Usuario (IGU): botones, checkbox, etc.<\/li>\n<li style=\"text-align: justify;\">Nuestra clase hereda de la clase\u00a0Scene de cocos2d.<\/li>\n<li style=\"text-align: justify;\">Se utiliza\u00a0la macro <code>CREATE_FUNC<\/code>, definida por cocos2d. Cuando esta macro se expanda previamente a la compilaci\u00f3n del proyecto, generar\u00e1 un c\u00f3digo tal que as\u00ed:\n<pre lang=\"Cpp\">static MyScene* create()\r\n{\r\n    MyScene *pRet = new(std::nothrow) MyScene();\r\n    if (pRet && pRet->init())\r\n    {\r\n        pRet->autorelease();\r\n        return pRet;\r\n    }\r\n    else\r\n    {\r\n        delete pRet;\r\n        pRet = NULL;\r\n        return NULL;\r\n    }\r\n}<\/pre>\n<\/li>\n<li style=\"text-align: justify;\">Si nos fijamos en el c\u00f3digo generado por la macro, tenemos primero una llamada a un constructor que no recibe par\u00e1metros\u00a0(como no hemos declarado ning\u00fan constructor, el compilador crear\u00e1 uno por defecto). Adem\u00e1s se hace una llamada a un m\u00e9todo <code>init()<\/code> que debe devolver un bool. Este m\u00e9todo no existe, as\u00ed que lo declaramos\u00a0(linea 9).<\/li>\n<li style=\"text-align: justify;\">Finalmente, en la linea 13 tenemos la declaraci\u00f3n del m\u00e9todo\u00a0que manejar\u00e1 los eventos de nuestro bot\u00f3n.<\/li>\n<\/ul>\n<h3 style=\"text-align: justify;\">MyScene.cpp<\/h3>\n<pre lang=\"Cpp\" line=\"1\">#include \"MyScene.h\"\r\n#include \"iostream\"\r\n\r\nusing namespace cocos2d;\r\n\r\nbool MyScene::init() {\r\n    Sprite* background = Sprite::create(\"background.png\");\r\n    this->addChild(background);\r\n\r\n    ui::Button* btn = ui::Button::create(\"button-image.png\");\r\n    btn->setPosition( Vec2(200.f, 200.f) );\r\n    btn->addTouchEventListener( CC_CALLBACK_0(MyScene::buttonPressed, this) );\r\n    this->addChild(btn);\r\n\r\n    return true;\r\n}\r\n\r\nvoid MyScene::buttonPressed() {\r\n    std::cout << \"PRESSED 0\" << std::endl;\r\n}\r\n<\/pre>\n<ul>\n<li style=\"text-align: justify;\">En el m\u00e9todo <code>init()<\/code> a\u00f1adimos los componentes de la IGU. En este caso s\u00f3lo a\u00f1adimos una imagen de fondo (<em>background<\/em>) y un bot\u00f3n (<em>btn<\/em>).<\/li>\n<li style=\"text-align: justify;\">Usando el m\u00e9todo <code>addTouchListener()<\/code> conseguimos que nuestro m\u00e9todo\u00a0<code>buttonPressed()<\/code> maneje los eventos que reciba el bot\u00f3n <em>btn<\/em>.<\/li>\n<li style=\"text-align: justify;\">Para indicar que m\u00e9todo debe manejar el evento, utilizamos la macro <code>CC_CALLBACK_0<\/code> pasando como argumentos el selector del m\u00e9todo y el objeto que debe ejecutarlo (en este caso es el propio objeto actual, con lo que pasamos el puntero <em>this<\/em>).<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h2>Ampliando\u00a0el manejador de eventos<\/h2>\n<p style=\"text-align: justify;\">En el ejemplo descrito anteriomente, el m\u00e9todo que maneja los eventos, <code>MyScene::buttonPressed()<\/code>, no recibe ning\u00fan par\u00e1metro. Sin embargo, puede que necesitemos recibir informaci\u00f3n sobre el evento generado, y para ello existen otras macros <code>CC_CALLBACK_n<\/code> definidas por cocos2d-x. Ejemplos:<\/p>\n<h3 style=\"text-align: justify;\">Conocer el widget que ha generado el evento<\/h3>\n<p style=\"text-align: justify;\">Supongamos que queremos utilizar\u00a0un mismo m\u00e9todo para manejar eventos en m\u00e1s de un componente gr\u00e1fico, y queremos actuar en funci\u00f3n del elemento que haya sido pulsado. En este caso el m\u00e9todo encargado de manejar los eventos debe recibir un par\u00e1metro de tipo <code>cocos2d::Ref*<\/code><\/p>\n<pre lang=\"Cpp\">void buttonPressed(cocos2d::Ref* pSender);<\/pre>\n<p style=\"text-align: justify;\">El par\u00e1metro <code>pSender<\/code> ser\u00e1 un puntero al widget pulsado, y podemos hacer un casting directamente para aprovechar toda su funcionalidad. Por ejemplo:<\/p>\n<pre lang=\"Cpp\">#define ACTION_PLAY 1\r\n\r\n.....\r\n\r\nbool MyScene::init() {\r\n    ....\r\n    ui::Button* btnPlay = ui::Button::create(\"play-image.png\");\r\n    btnPlay->setActionTag( ACTION_PLAY );\r\n    ....\r\n}\r\n.....\r\n\r\nvoid MyScene::buttonPressed(cocos2d::Ref* pSender) {\r\n    if (((cocos2d::ui::Button*) pSender)->getActionTag() == ACTION_PLAY) {\r\n        \/\/ ... do\r\n    }\r\n}<\/pre>\n<p style=\"text-align: justify;\">A la hora de asociar este m\u00e9todo a los componentes gr\u00e1ficos, tenemos que usar la macro <code>CC_CALLBACK_1<\/code><\/p>\n<pre lang=\"Cpp\" line=\"1\">btn->addTouchEventListener(\r\n           CC_CALLBACK_1(MyScene::buttonPressed, this) );<\/pre>\n<p>&nbsp;<\/p>\n<h3>Saber el tipo de evento generado<\/h3>\n<p style=\"text-align: justify;\">Si probamos los maneadores de eventos anteriores, comprobaremos que por cada pulsaci\u00f3n al bot\u00f3n se generan dos eventos. Esto se debe a que se genera uno al pulsar\u00a0el bot\u00f3n y otro al soltarlo. Si nos interesa saber qu\u00e9 tipo de evento es el que se ha generado, podemos utilizar un manejador de eventos que reciba adem\u00e1s un par\u00e1metro de tipo <code>TouchEventType<\/code>:<\/p>\n<pre lang=\"Cpp\">void buttonPressed(cocos2d::Ref *pSender, cocos2d::ui::Widget::TouchEventType eventType);<\/pre>\n<p style=\"text-align: justify;\">El objeto <code>TouchEventType<\/code> es un enumerado\u00a0que puede tomar los valores <code>BEGAN<\/code>, <code>MOVED<\/code>, <code>ENDED<\/code> o <code>CANCELED<\/code>. Si quisi\u00e9ramos actuar, por ejemplo, s\u00f3lo cuando se pulse el bot\u00f3n podr\u00edamos tener un c\u00f3digo tal que as\u00ed:<\/p>\n<pre lang=\"Cpp\">void MyScene::buttonPressed(cocos2d::Ref *pSender, cocos2d::ui::Widget::TouchEventType eventType) {\r\n    if (cocos2d::ui::Widget::TouchEventType::BEGAN == eventType) {\r\n        \/\/ ... do\r\n    }\r\n}<\/pre>\n<p style=\"text-align: justify;\">Para asociar este m\u00e9todo a los componentes gr\u00e1ficos, tenemos que usar la macro <code>CC_CALLBACK_2<\/code><\/p>\n<pre lang=\"Cpp\" line=\"1\">btn->addTouchEventListener(\r\n          CC_CALLBACK_2(MyScene::buttonPressed, this) );<\/pre>\n<p>&nbsp;<\/p>\n<p>Espero que este art\u00edculo te haya resultado \u00fatil.<\/p>\n<p>Saludos!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recientemente he empezado a desarrollar un port a iOS de mi juego Dark Snake, hecho originalmente para Android. He querido aprovechar la ocasi\u00f3n para aprender algo de C++, por lo que he decidido usar el motor Cocos2d-x. Aunque seguramente lo <a class=\"more-link\" href=\"https:\/\/www.pixnbgames.com\/blog\/c\/como-anadir-maneadores-de-eventos-a-botones-en-cocos2d-x-3-x-y-cpp\/\">Seguir leyendo <span class=\"screen-reader-text\">  C\u00f3mo a\u00f1adir maneadores de eventos a botones en Cocos2d-x 3.x y C++<\/span><span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[28,29],"tags":[31,32,30,25,33],"language":[5],"_links":{"self":[{"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/posts\/310"}],"collection":[{"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/comments?post=310"}],"version-history":[{"count":26,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/posts\/310\/revisions"}],"predecessor-version":[{"id":342,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/posts\/310\/revisions\/342"}],"wp:attachment":[{"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/media?parent=310"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/categories?post=310"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/tags?post=310"},{"taxonomy":"language","embeddable":true,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/language?post=310"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}