cocos2d-xcclayer

Difference between CCLayer::init() and CCLayer::onEnter()?


Here is the topic on Difference between CCNode::init() and CCNode::onEnter(). However I followed the advice they gave.

void MyLayer::onEnter() {
    CCLayer::onEnter();
    //your code goes here
}

I got the Assertion failed! error!
MyLayer code:

class MyLayer : public CCLayerColor

Should I add CCLayerColor::onEnter() in my MyLayer::onEnter() code? And what is the difference between CCLayer::init() and CCLayer::onEnter(). Which part of code should I put into init() and which part should put into onEnter()?


Solution

  • Cocos2d-x has its memory allocation model as a two-step process, like objective-c. Each object has memory allocated (usually using a "create" method) and then has its state initialized (usually using a method called "init"). So in the create/init methods, you allocate the memory and do any object initialization necessary for it to run.

    When the object starts to be put onto the display, or when it is added to another container, its "onEnter" method is called. This gets called when a CCScene/CCLayer (either of which may be containing your CCNode derived object) is displayed by the framework itself.

    There are at least 2 patterns for memory allocation and object creation, I tend to follow the pattern of having a class contain a static factory method and a private constructor so that it is unambiguous that you must create the objects through the factory and cannot create one yourself.

    For example, I am currently working on this "button" class:

    class ActionButton;
    
    class ActionButtonTarget
    {
    public:
       virtual void ActionButtonActivated(ActionButton* button) = 0;
    };
    
    class ActionButton : public CCNode, public CCTargetedTouchDelegate
    {
    private:
       ActionButton();
    
       CCNode* _node; // Weak Reference
       CCRect _testRect;
       ActionButtonTarget* _target;
    
       bool init(ActionButtonTarget* target, CCNode* node, CCRect rect);
       bool IsTouchInside(CCTouch* touch);
    
       void NotifyTarget();
    
    protected:
       virtual CCAction* CreateAction();
    
    public:
    
       virtual ~ActionButton();
    
       // The class registers/unregisters on entry
       // or exit of the layer.  This
       virtual void onEnterTransitionDidFinish();
       virtual void onExitTransitionDidStart();
    
       virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
       virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
       virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
       virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent);
    
       static ActionButton* create(ActionButtonTarget* target, CCNode* node, CCRect rect);
    };
    

    Note that the "create" method is the only way to create it. In this case, it takes arguments for parameters of the button. Other CCNode derived objects (e.g. CCScene) usually do not.

    Internally:

    ActionButton::ActionButton()
    {
    }
    
    ActionButton::~ActionButton()
    {
    
    }
    
    // The class registers/unregisters on entry
    // or exit of the layer.  This
    void ActionButton::onEnterTransitionDidFinish()
    {
       CCNode::onEnterTransitionDidFinish();
       CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
    }
    
    void ActionButton::onExitTransitionDidStart()
    {
       CCNode::onExitTransitionDidStart();
       CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
    }
    
    bool ActionButton::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
    {
       if(IsTouchInside(pTouch))
       {
          return true;
       }
       return false;
    }
    
    void ActionButton::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)
    {
       // Nothing to do here.
    }
    
    void ActionButton::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
    {
       NotifyTarget();
    }
    
    bool ActionButton::IsTouchInside(CCTouch* touch)
    {
       CCPoint point = touch->getLocationInView();
       point = CCDirector::sharedDirector()->convertToGL(point);
       return _testRect.containsPoint(point);
    }
    
    
    void ActionButton::ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent)
    {
       _target->ActionButtonActivated(this);
    }
    
    ActionButton* ActionButton::create(ActionButtonTarget* target, CCNode* node, CCRect rect)
    {
       ActionButton *pRet = new ActionButton();
       if (pRet && pRet->init(target,node,rect))
       {
          pRet->autorelease();
          return pRet;
       }
       else
       {
          CC_SAFE_DELETE(pRet);
          return NULL;
       }
    }
    
    CCAction* ActionButton::CreateAction()
    {
       return NULL;
    }
    
    void ActionButton::NotifyTarget()
    {
       CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect(Constants::SOUND_EFFECT_BUTTON_CLICK());
       _target->ActionButtonActivated(this);
    }
    
    
    
    bool ActionButton::init(ActionButtonTarget* target, CCNode* node, CCRect rect)
    {
       assert(target != NULL);
       assert(node != NULL);
       assert(dynamic_cast<ActionButtonTarget*>(target) != NULL);
    
       _target = target;
       _node  = node;
       _testRect = rect;
    
       addChild(_node);
    
       return true;
    }
    

    Note the OnEnterXXX and onExitXXX methods call the parent class method. YOU MUST DO THIS or it will not work as expected.

    This class is a button that will run an action on the node (maybe make it grow/shrink to indicate it got pressed). It takes touches from the user. I cannot add it to the touch manager before it has entered the scene. So I use the onEnterTransitionDidFinish method to add it and also use the onExitTransitionDidStart to remove it so it will not continue to receive touches after the scene has been removed. I don't know if the container will be destroyed or not, so I must remove it when the button exits the display.

    Was this helpful?