Разница между CCLayer::init() и CCLayer::onEnter()?

Вот тема Разница между CCNode::init() и CCNode:: при вводе(). Однако я последовал совету, который они дали.

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

Я получил ошибку Assertion failed!!
Код MyLayer:

class MyLayer : public CCLayerColor

Должен ли я добавить CCLayerColor::onEnter() в свой код MyLayer::onEnter()? И в чем разница между CCLayer::init() и CCLayer::onEnter(). Какую часть кода я должен поместить в init() и какую часть в onEnter()?


person einverne    schedule 25.12.2013    source источник


Ответы (3)


Cocos2d-x использует модель распределения памяти как двухэтапный процесс, как и target-c. Каждому объекту выделяется память (обычно с помощью метода «создать»), а затем инициализируется его состояние (обычно с помощью метода, называемого «инициализация»). Таким образом, в методах create/init вы выделяете память и выполняете любую инициализацию объекта, необходимую для его запуска.

Когда объект начинает выводиться на дисплей или когда он добавляется в другой контейнер, вызывается его метод onEnter. Это вызывается, когда CCScene/CCLayer (любой из которых может содержать ваш производный объект CCNode) отображается самой структурой.

Существует по крайней мере 2 шаблона для выделения памяти и создания объектов, я склонен следовать шаблону, когда класс содержит статический фабричный метод и частный конструктор, поэтому однозначно, что вы должны создавать объекты через фабрику и не можете создать один сами.

Например, сейчас я работаю над этим классом «кнопки»:

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);
};

Обратите внимание, что метод «создать» — единственный способ создать его. В этом случае он принимает аргументы для параметров кнопки. Другие объекты, производные от CCNode (например, CCScene), обычно этого не делают.

Внутри:

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;
}

Обратите внимание, что методы OnEnterXXX и onExitXXX вызывают метод родительского класса. ВЫ ДОЛЖНЫ СДЕЛАТЬ ЭТО, иначе это не сработает должным образом.

Этот класс представляет собой кнопку, которая запускает действие на узле (возможно, заставляет его увеличиваться/уменьшаться, чтобы указать, что он был нажат). Он принимает прикосновения от пользователя. Я не могу добавить его в сенсорный менеджер до того, как он выйдет на сцену. Поэтому я использую метод onEnterTransitionDidFinish, чтобы добавить его, а также использую метод onExitTransitionDidStart, чтобы удалить его, чтобы он не продолжал получать касания после удаления сцены. Я не знаю, будет ли контейнер уничтожен или нет, поэтому я должен удалить его, когда кнопка покинет дисплей.

Было ли это полезно?

person FuzzyBunnySlippers    schedule 25.12.2013
comment
Спасибо за ваш ответ. Но у меня все еще есть вопрос. Если MyCustomLayer не вызывает CCLayer::onEnter, MyCustomLayer работает нормально. А если бы я добавил , некоторые assert показывались. - person einverne; 26.12.2013
comment
Вы должны вызвать реализацию родительского класса. Например, ваш производный слой должен вызывать CCLayer::onEnter, ЕСЛИ вы переопределяете onEnter. Если вы углубитесь в определение, вы обнаружите, что оно выполняет реальную работу, а не просто заполняет слот шаблона дизайна. Если вы покажете код, возможно, утверждение можно отладить... - person FuzzyBunnySlippers; 26.12.2013
comment
lh3.googleusercontent.com/-TN70v0yl9o4/UrwU-kqnyGI/AAAAAAAAaOg/ Assert показывает, что что-то не так с ccTouch . PS. Я переписал все свои Touch Event в ccTouches Standard Touch Event и assert исчез. - person einverne; 26.12.2013
comment
Ну... я не могу сказать наверняка... но я думаю, что могу знать, что произошло... Я нашел место в cocos2d-x (v2.105), где целевой обработчик касания выдаст утверждение, если оно добавлено дважды (вместо того, чтобы просто вернуться, что происходит после утверждения). Когда вы добавляете кнопку (например, выше) в контейнер, для нее вызывается onEnter. Если вы сделаете это в onEnter, он будет вызван дважды и подтвержден. Это то, что ты видел? - person FuzzyBunnySlippers; 26.12.2013
comment
Я изменил метод onEnterTransitionDidFinish(...) так, чтобы это больше не было проблемой. Может быть, это то, что вы испытали... - person FuzzyBunnySlippers; 26.12.2013
comment
Я должен вернуть это редактирование... при дальнейшем тестировании оно не сработало, как ожидалось. Решение, которое у меня изначально работало лучше всего... не похоже, что сенсорный диспетчер должен генерировать исключение, когда вы дважды добавляете один и тот же обработчик... когда код уже просто игнорирует дополнительную попытку. - person FuzzyBunnySlippers; 26.12.2013

Первая часть

Проверьте метод OnEnter базового класса (CCLayer), если происходит что-то серьезное, то вы должны были его вызвать.

Вторая часть

Как в теме... OnEnter вызывается при "viewDidAppear" и init во время инициализации.

Предположим, вы создаете слой этим

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

После этого вы добавляете его в какую-то сцену

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

*X должен быть добавлен к какой-либо сцене или родителю и так далее.

Третья часть

В некоторых случаях вы просто создаете несколько объектов, хранящихся в массиве, но не добавляете их. Вы ждете подходящего времени для их добавления. Это может быть случай, когда объект появляется / добавляется на экран, вы хотите, чтобы он выполнял определенную задачу.

Бывший. В игре вы должны атаковать кого-то и иметь 30 войск. Все войска создаются в сцене загрузки, но ни один из них не добавляется в слой. Когда начинается битва, на экране появляется развернутый пользователем отряд. У отряда при входе есть задача перейти к целевому зданию, и этот метод можно вызвать через onEnter.

person Vivek Bansal    schedule 25.12.2013
comment
Спасибо за ваш пример~~ - person einverne; 26.12.2013
comment
@Bansal Я думаю, что в твоем ответе есть ошибка. X->addChild(Y) не всегда будет вызывать Y.onEnter(), если сам X не добавлен в какую-либо сцену или родительский слой. - person Emadpres; 25.07.2014
comment
@EmAdpres хорошо, я забыл об этом. - person Vivek Bansal; 08.08.2014

Вы вызываете onEnter(), если хотите что-то сделать в тот момент, когда это появляется на экране. Метод init() будет вызываться даже без отображения слоя на экране. Что-то вроде этого:

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
}
person Sumit Kandoi    schedule 26.12.2013