Cocos Studio Linking button with action

How do I link the button with an action. In this case I would link to change scenes. I have already loaded my scene from Cocos Studio into my cocos2dx project but im having trouble with adding a pressed action.

Use the below code

auto rootNode = CSLoader::createNode(“res/Hud.csb”);
addChild(rootNode);
auto button = static_castcocos2d::ui::Button*(rootNode->getChildByName(“buttonNameInCocosStudio”));
if(button)
{
button->addClickEventListener(={
//here you do what ever you need to do
CCLOG("WORKS ");

    });
}

Thanks for your response but it seems to be giving me errors :confused:

auto rootNode = CSLoader::createNode("res/Hud.csb");

addChild(rootNode);

auto vbutton = static_cast< cocos2d::ui::Button*>(rootNode->getChildByName(“v_button”));
if(vbutton)
{
vbutton->addClickEventListener([=](Ref *){

        CCLOG("Pressed ");
    
    });
}

What errors ? syntax or runtime ?

Running it in Eclipse returns the following:


Ignore the warnings(9), they always come up and the apk compiled/ran just fine. The problem is with the errors which prevent a successful build.

You could use the widget reader factory to deal with touch/click events, there is an example of widget reader callbacks in the cpp tests project.

Thank you for your help as well! I think I found it but its a bit confusing :frowning: Could you please post what goes where? Im kind of new to Cocos2dx and dont quite understand it all :confused:

For a widget reader factory, you need to decide the name of your widget first. Let’s assume it’s called “MyWidget” for now.

  1. Firstly in cocos studio you need to click on the “scene” in the animation box at the bottom, that’s the top level layer. Then click on advanced in the properties window and set the “Custom Class” in the “CallBack Feature” area to the name of your widget “MyWidget” for example.

  2. Now for all the objects in your scene that you want to have touch/click events you need to click on each one and in the properties window make sure that the touchable option is checked and in the advanced section you must changed the method to Touch or Click (touch in most cases) and set the callback name. This is the name of the callback that is recognized by the reader, a good example would be “on_button_touched”.

  3. Now save and publish your scene, copy it in to “Resources” folder in your project ensuring that the folder structure for any assets in cocos studio matches the folder structure in your “Resources” folder or you will run in to problems. So if you have a folder in your scene in cocos studio called “images” and all your assets for the scene are in there, then your “Resources” folder must also have a folder named “images” so it would be like, “resources\images\asset.png” you can place the .csb file straight in to the resources folder or any other subfolder, it doesn’t affect the folder paths for the assets if you do.

  4. You now need to create 2 classes to handle the callbacks, firstly implement MyWidget class (same name as we set for the scene class in cocos studio), this example is just setup for touch events but I’ve commented out the other events so they are there if you need.

#ifndef __MyWidget_H__
#define __MyWidget_H__

#include "cocos2d.h"
#include "cocostudio/CocoStudio.h"
#include "cocostudio/WidgetCallBackHandlerProtocol.h"

class MyWidget: public cocos2d::Node, public cocostudio::WidgetCallBackHandlerProtocol
{

public:
CREATE_FUNC(MyWidget)

MyWidget();

virtual cocos2d::ui::Widget::ccWidgetTouchCallbackonLocateTouchCallback(const std::string &callBackName);
//virtual cocos2d::ui::Widget::ccWidgetClickCallbackonLocateClickCallback(const std::string &callBackName);
//virtual cocos2d::ui::Widget::ccWidgetEventCallbackonLocateEventCallback(const std::string &callBackName);

void onButtonTouched(cocos2d::Ref* sender, cocos2d::ui::Widget::TouchEventType type);
//void onClick(cocos2d::Ref* sender);
//void onEvent(cocos2d::Ref* sender, int eventType);

//private:std::vector<std::string> _touchTypes;
//std::string _click;std::vector<std::string> _eventTypes;
};

#endif

And the cpp implementation, where you handle the callbacks

#include "MyWidget.h"

USING_NS_CC;
using namespace std;
using namespace cocos2d::ui;

MyWidget::MyWidget() {}

Widget::ccWidgetTouchCallback MyWidget::onLocateTouchCallback(const string &callBackName)
{
    // Callback names must match the ones you set in cocos studio
    if (callBackName == "on_button_touched")
    {
        return CC_CALLBACK_2(MyWidget::onButtonTouched, this);
    }
    if (callBackName == "some_other_callback")
    {
        // Add declaration in to header for other callbacks
        return CC_CALLBACK_2(MyWidget::onSomeOtherTouch, this);
    }
    
    return nullptr;
} 

void MyWidget::onButtonTouched(cocos2d::Ref* object, cocos2d::ui::Widget::TouchEventType type)
{
    CC_UNUSED_PARAM(object);
    //End of touch
    if (type == cocos2d::ui::Widget::TouchEventType::ENDED)
    {
        // do something
    }
} 
5. Now create another class called MyWidgetReader, header as follows
#ifndef __MyWidgetReader_H__
#define __MyWidgetReader_H__

#include "cocos2d.h"
#include "cocostudio/CocosStudioExport.h"
#include "cocostudio/WidgetReader/NodeReader/NodeReader.h"

class MyWidgetReader : public cocostudio::NodeReader
{
public:
    MyWidgetReader () {};
    ~MyWidgetReader () {};
 
    static MyWidgetReader* getInstance();
    static void purge();
    cocos2d::Node* createNodeWithFlatBuffers(const flatbuffers::Table* nodeOptions); 
};

#endif

And now the implementation

#include "MyWidgetReader.h"
#include "MyWidget.h"
    
USING_NS_CC;
using namespace flatbuffers;
 
static MyWidgetReader* _instanceWidgetReader = nullptr;
 
MyWidgetReader* MyWidgetReader::getInstance()
{
    if (!_instanceWidgetReader)
    {
        _instanceWidgetReader = new MyWidgetReader();
    }
    return _instanceWidgetReader;
}
 
void MyWidgetReader::purge()
{
    CC_SAFE_DELETE(_instanceWidgetReader);
}

Node* MyWidgetReader::createNodeWithFlatBuffers(const Table *nodeOptions)
{
    MyWidget* node = MyWidget::create();

    setPropsWithFlatBuffers(node, nodeOptions);

    return node;
} 
6. Finally we must load the cocos studio scene and register the reader together, so firstly select the layer that you want to contain your cocos studio scene and in the the onEnter function (don't do it in the init function, it doesn't like that). Add this code: CSLoader* instance = CSLoader::getInstance(); instance->registReaderObject("MyWidgetReader", (ObjectFactory::Instance)MyWidgetReader::getInstance); // Must match the name of the widget reader class ^
_rootNode = instance->createNode("nameOfYourScene.csb"); // in resources folder
//rootNode->getChildByName("name")->
addChild(_rootNode);

Oh and declare Node* _rootNode in the header file of your layer.

That’s it if you followed the instructions correctly it should work fine.

4 Likes

The easiest way to visualize it, is that for every cocos studio scene, you must have a new widget reader (at least that’s how I do it), so when you create a new scene/project in cocos studio then repeat steps 1-6.

Thank you for the guide! It was very detailed and helpful :smiley: but there is another problem :open_mouth:

Okay a few things, don’t take this the wrong way but it seems you are maybe lacking some basic programming skills that are preventing you from being able to fix errors in your project.

  • _rootNode should be declared in the header file of the layer, like this cocos2d::Node* _rootNode;

  • So for you it would be a case of adding it to MainMenuScene.h

  • If it is saying Node is undefined then you haven’t included cocos2d in the header. #include “cocos2d.h”. Anytime anything is saying undeclared you haven’t included the header file (.h) file for it.

  • I think it may be a good idea for you to do some tutorials on C++ and object oriented design.

  • There’s no point in linking a screenshot of errors if the description is half cut off…

  • Cocos Studio with a widget reader factory is an advanced topic and you appear to be struggling with the basics

  • You shouldn’t really be using eclipse for programming the project, the android project should be compiled in cocos console only now, if you’re on windows I would recommend using the visual studio and the proj.win32 to program your game

  • A lot of those errors you are getting may be purely because you are using eclipse, it’s not a good development environment and a massive pain to set up correctly.

If your still having issues after trying all those points, then come back here but make sure you have exhausted your options/knowledge first, it’s not a good way to learn by getting others to tell you the answers.

I agree my C++ is weak. Much better with some javascript but Im trying to use C++ as cocos2d-js takes up a lot of storage once compiled. Im running Mac with Eclipse as Xcode didn’t like cocos2dx for some reason

When I do everything according to these instructions for some reason _rootNode is null and App crashes. When I remove Custom Class name than I can run project but callbacks don’t work. Any idea what can be wrong?

the only suggestion I have is that you didn’t follow the instructions clearly enough. The custom class name in cocos studio must match the class name in your project. If it is called “Tree” then your classes must be “Tree.h.” and “TreeReader.h”.

And this step 6. must look like this

instance->registReaderObject(“TreeReader”, (ObjectFactory::Instance)TreeReader::getInstance);

the name of the .csb file is irrelevant. It must match the .csb file you have in your resources folder.

I already figured it out months ago, thanks though(it was no way as difficult as you described above)

@UKDeveloper99 this might be a stupid question, but can I ask what is the advantage of using a widget reader instead of just doing the bindings the old Cocos2d-x (ie. cast the button, add a listener)? I currently do things the Cocos2d-x way and am considering switching to a widget reader but am unsure what the value is considering the amount of code seems to offset the simplicity of assigning actions in the Cocos Studio GUI.

The only thing I can think of is that a widget reader is conducive to MVC, but then you would want to make the WidgetReader into a more general controller.

There is no advantage.
Keep on doing stuff the way you do.

@gkapur I don’t think there is any big advantage, it’s just the recommended system to use as far as I know. I seem to remember running in to some problem when I had a setup with the old cast a button add a listener method. Can’t remember what it was, if that works for you then stick with that. When you say the cocos2d-x way I believe this is the preferred method, it’s how they’ve done it in the UITests for example in Cocos2dTests. Or rather it was in v3.4 last time I checked. May have changed now they change stuff so often. My best advice would be, if it works then stick with it. This works for me so this is what I use. I make no claims of it being the best or most efficient method out there, but it seems to be popular with a lot of the chinese developers and I think their cocos community is a lot larger than this one.

Well Found this after whole 1 day frustration…
Need to add this into official docs somewhere.
This is worth it…
Thanks A Lot for this…!!

1 Like

I know i am replying to very old post but i feel @UKDeveloper99 is the right person to ask for.
I am a new developer in cocos2d-x world so need a bit guidance.
I have used your above reference to develop most of my widget designed as separate node from cocostudio.
What I am trying is Widget to widget call which I am able to achieve partially but not sure is it a correct way to do it and is there any other way around this.
So I have Node1 and Node2 added on HelloworldScene.
HelloWordScne has a button which display Node1.
Node1 has a button which should display Node2.
My Both nodes are registered on HelloWorldScene.
The problem start at Node1->Node2 Call.
I have tried following 2 ways.

  1. An instance of static Node2* _currentInstance = nullptr; in Node2Reader with initialized as

    Node* Node2Reader::createNodeWithFlatBuffers(const Table *nodeOptions)
    

    {
    Node2* node = Node2::create();
    _currentInstance = node;
    setPropsWithFlatBuffers(node, nodeOptions);

    return node;
    

    }

and a getInstance method with

Node2* Node2Reader::currentInstance()
{

    return _currentInstance;
}

This works perfect and shown the Node2 when setVisible of Node2 is called from Node1.
I am not sure is it the right way to store a instance in static pointer as above and use it.

  1. In this i am adding the Node2 as child in node1 Click event

    void Node1::onButtonTouched(cocos2d::Ref* sender, cocos2d::ui::Widget::TouchEventType type)
    {
    CC_UNUSED_PARAM(sender);
    //End of touch
    if (type == cocos2d::ui::Widget::TouchEventType::ENDED)
    {
    CCLOG(“Touch Ended Node1”);
    auto node = CSLoader::createNode(“Node2.csb”);
    addChild(node);
    }
    }
    While this also adds a node and displays it but it has call to onEnter() twice once while regissterreaderobject and another while create so this seem improper to me meaning two instance of same node are there on scene.
    So anybody who have worked on widget development can guide me on this.