Cocos2d-x RU‎ > ‎

Статья 1

ИТАК... С чего начать… 
Для чего эти статьи? Чтобы помочь начинающим программистам, изучить cocos2d-x. Материалов на русском языке мало, «грабель» много :P 

Несколько слов о движке.
Cocos2d-x – это набор классов, сильно, упрощающих разработку двухмерных графических приложений для устройств на базе Android, iOS, WP8. Вам не обязательно знать OpenGL, так как в движке реализовано практически все, для разработки графических приложений.
Если Вы знакомы с ООП, C++, STL. То Вам не составит труда научиться писать мобильные (и не только) приложения на этом движке. 
Для управления памятью, в движке реализован пул объектов. Когда объект создается, то он помещается в пул, после того, как объект больше не нужен, он удаляется из кучи.

CCObject

Управляемый класс , общий родитель для всех объектов библиотеки классов – CCObject. Он же является умным указателем (smart pointer). Каждый объект класса, порожденный от CCObject, имеет, защищенную, переменную m_uReference, которая указывает на количество его клиентов. В движке реализован менеджер памяти, который следит за управляемыми объектами и удаляет их из памяти по мере необходимости.
К примеру, вы создаёте спрайт: 

Код
...
CCSprite* pSprite = CCSprite::create(“somename.png”); // после выполнения этой строчки pSprite->m_uReference == 0
...

Если ничего с pSprite не делать, то при выходе из области локальной функции, в которой он был создан, наш pSprite будет удален. 
Тогда , если pSprite глобальная переменная в пределах класса (поле класса), то указывать она будет на мусор, и при попытке обратиться к объекту pSprite программа упадёт. Но, обычно, мы создаем экземпляры, чтобы ими пользоваться. 
Для этого нам надо добавить спрайт на слой
Код
pLayer->addChild(pSprite)
, или в массив
Код
pArray->addObject(pSprite)
, или и на слой и в массив, или еще куда…. При каждом добавлении m_uReference у нашего pSprite будет увеличиваться. Сразу скажу, не обязательно на 1. Например при добавлении спрайта на слой, его m_uReference увеличивается на 2. Это специфика реализации движка, внутри движка свои рабочие списки, массивы и т.д., которые тоже влияют на m_uReference объектов. Когда мы удаляем объект из слоя, или из массива и т.п. то его m_uReference уменьшается. Когда m_uReference объекта ==0 он очищается.
Можно увеличивать и уменьшать m_uReference с помощью вызовов методов объекта: 

Код
pSprite->retain(); // m_uReference +=1 (это сокращенная запись, в C++  применяется везде и всегда, означает m_uReferebnce = m_uReference+1)
pStrite->release(); // m_uReference -=1.

Осторожно пользуйтесь данными методами, каждому retain(), должен соответствовать вызов release(). 
Иначе объект останется в памяти и произойдет - memory leak.
Надо быть очень аккуратными, если вы расширяете функциональность базовых классов COCOS-а, путем наследования. Надо, так сказать, играть по правилам и тогда с памятью будет порядок. (Вернее, утечки то будут :rolleyes: , но их будет меньше)
Мы как раз подошли к пункту:


Правила хорошего тона, или как минимизировать утечки памяти.

При расширении базовых классов, считается хорошим тоном создавать экземпляр объекта в соответствующей статической функции класса. В наших классах, нам надо реализовать статическую функцию, внутри которой будет создаваться экземпляр расширенного класса по правилам движка.
(Вообще, более подробно о сценах, слоях, спрайтах я расскажу позже.)
Посмотрим на примере, уже известного нам, CCSprite. Это класс, который реализует спрайт. Что такое спрайт, надеюсь, Все знают. Хороший такой класс, нам нравится... Но вот по логике игры, нам очень надо расширить его функционал. К примеру, написать класс CBonus — бонус в игре, который должен быть почти как CCSprite, чтобы мы могли добавлять его на сцену, но иметь некоторые свои расширенные функции. 

Не вопрос, пишем вот такой класс:

Мы сейчас говорим об управлении памятью в Cocos2dx, поэтому в определении и реализации класса нам важно рассмотреть статическую функцию createBonus


объявление:
Код
...
class TABonus:public CCSprite
{
    ...
    TABonus(CCLayer* s, b2World* world, float pX, float pY,TABonusType type);
public:
    ~TABonus();
    static TABonus* createBonus(CCLayer* s, b2World* world, float pX, float pY,TABonusType type); // Вот она родная :)
};
...

реализация:
Код
...
TABonus* TABonus::createBonus(CCLayer* s, b2World* world, float pX, float pY,TABonusType type)
{
    TABonus* pRet = new TABonus(s,world,pX,pY,type);
    if (pRet)
    {
        pRet->autorelease();
        return pRet;
    }
    CC_SAFE_DELETE(pRet);
    return NULL;
}
...

Как видите, я конструктор класса объявил в закрытой секции. Т.е. непосредственно создать класс мы не можем. Чтобы создать класс, нам надо вызвать статическую функцию класса CBonus::createBonus
Посмотрите код выше, в этой функции мы создаем объект класса:

Код
TABonus* pRet = new TABonus(s,world,pX,pY,type);

добавляем его в пул движка:
Код
pRet->autorelease();

и если все хорошо, отдаем объект. А если все не очень, то
CC_SAFE_DELETE
безопасно удалит объект из памяти.

Вот пример:

Пусть pLayer — это наш слой, где будет помещен бонус

Код
...
Cbonus* pBonus = TABonus::createBonus(s,world,pX,pY,type);
pLayer->addChild(pBonus); // pBonus->m_uReference +=2 

здесь что-то происходит
… 
// нам больше не нужен бонус
pBonus->removeFromParentAndCleanup(true);// pBonus->m_uReference -=2

Соответственно m_uReference нашего объекта pBonus ==0, менеджер памяти удалит объект


Экземпляры классов движка мы можем создавать с помощью ф-ции new, но тогда вся ответственность 
за удаление объекта из памяти ложится на плечи программиста.

Comments