Question

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()?

Was it helpful?

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?

OTHER TIPS

Ist Part

Check out OnEnter method of base class(CCLayer),if some serious stuff is going on ,then you should have called it.

IInd Part

As in the topic... OnEnter is called when "viewDidAppear" and init during initialisation.

Suppose you create layer by this

MyLayer *newLayer=MyLayer::create(); //...init is called before OnEnter..

After that you add it to some scene

X->addChild(newLayer); // ...*Now onEnter Method is called

*X must be added to some scene or parent and so on.

IIIrd Part

In some case you just create multiple objects stored in array,but didn't add them. You are waiting for right time for adding them.This might be case when object enter/add to screen ,you want it to perform specific task.

Ex. In a game you have to attack someone and have 30 troops.All the troops are created in loading scene but none of them add to layer. When battle begins, the deployed troop by user appear on the screen.Troop on enter has a task to go to target building and that method can be called through onEnter.

You call onEnter() if you want to do something the moment it appears on the screen. The init() method will be called even without the layer being on the screen. Something like this:

void FirstScene::methodInit()
{
     customlayer = new CustomLayer();
     customlayer -> init();
     customlayer-> retain();

     // At this point,customlayer (which is a CustomLayer object that is a subclass of CCLayer)
     // is still not on the screen, hence, CustomLayer::onEnter() is still not called
}

void FirstScene::methodEnter 

{
     this -> addChild( customLayer, customIndex, customTag );
     customLayer -> release();
     // At this point, CustomLayer::onEnter() is called, because customLayer is being rendered
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top