воскресенье, 6 января 2013 г.

О структуре базового проекта-2

Структура шаблона проекта Cocos2D. Часть 2

В прошлой части мы начали говорить о структуре шаблона проекта Cocos2D.
В этой части - продолжим.

HelloWorldLayer

Данный класс - это, собственно, тот класс, где случается вся магия cocos2d :)

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

cocos2d использует набор классов CCNode (узел) для того, чтобы отображать объекты на экране.
По сути, CCNode - это базовый класс, который хранит в себе позицию на экране, но не имеет никакого визуального отображения - все необходимые визуальные отображения реализуются в тех классах, которые наследуются от CCNode.
От CCNode наследуются еще два фундаментальных класса - CCScene и CCLayer.

CCScene(сцена) - по сути, класс, управляющий попиксельным расположением объектов на экране.
Фактически, чаще всего, для каждой сцены нам будет требоваться один такой класс, вместе они будут присутствовать на экране только при переходе между сценами.

CCLayer (слой) - класс, используемый для получения данных от пользователя - касаний и показаний акселерометра. Обычно, объект класса CCLayer первым добавляется в сцену.

Взглянем на код нашего HelloWorldLayer.
Опять же, важный момент: возможно, не весь код будет вам понятен. Это нормально! Если не понятны некоторые вызовы, в этом нет ничего страшного. Если вы не понимаете вообще ничего - возможно, стоит сначала более подробно изучить программирование для iOS.
Он наследуется от теперь знакомого нам CCLayer. 
Для создания сцены, в класс Layer'a добавляется статический метод +(id)scene (в языке objective-c + перед началом метода означает, что это метод класса, т.е. статический метод)

+(CCScene *) scene
{
CCScene *scene = [CCScene node];
HelloWorldLayer *layer = [HelloWorldLayer node];
[scene addChild: layer];
return scene;
}

В этом коде мы создаем сцену и слой, вызывая статические методы node, затем добавляем слой в сцену и возвращаем сцену для использования.

Посмотрим на код метода -(id)init
-(id) init
{
if( (self=[super init])) {
CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World" fontName:@"Marker Felt" fontSize:64];

CGSize size = [[CCDirector sharedDirector] winSize];
label.position =  ccp( size.width /2 , size.height/2 );
[self addChild: label];
}
return self;
}

В этом методе происходит создание объекта класса HelloWorldLayer.
Сначала мы обращаемся к классу, от которого наследуемся (super init - в данном случае к классу CCLayer), и если инициализация в CCLayer прошла успешно, мы получаем объект нашего класса и можем продолжить дополнительную инициализацию.
Сначала создадим надпись с TrueType-шрифтом MarkerFelt (шрифт маркера, установлен на iOS).
Затем мы получаем размеры нашего экрана в переменную size, устанавливаем позицию надписи в середину экрана и добавляем ее на наш слой вызовом addChild.

Управление памятью

Я рассчитываю, что вы знаете об управлении памятью в iOS. Если нет - пишите в комментарии, я создам пост на эту тему.
В cocos2d принято использовать в классах статический метод (например, node, как в вызове [CCScene node]), который возвращает autorelease'нутый объект и нам нет необходимости заботиться о вызове release для него.
Сам метод node определяется примерно так:

+(id) node {
return [[[self alloc] init] autorelease]; }

Это очень удобный стиль (хотя некоторые, конечно, могут с этим не согласиться).
Но таким образом, инициализация объекта становится очень простой:

CCNode* myNode = [CCNode node]; 
вместо
CCNode* myNode = [[[CCNode alloc] init] autorelease];
Разумеется, нужно помнить, что, поскольку объект autorelease-нут, то он исчезнет уже на следующем фрейме, поэтому нам надо позаботиться о том, чтобы он не пропал.

Это достигается добавлением в ваш CCNode-объект другого CCNode-объекта в качестве child'a - выше в HelloWorldLayer наш CCLayer(который, как мы знаем, производный от CCNode)
делает вызов [self addChild: label].
При вызове addChild объект добавляется в коллекцию CCArray (аналог NSMutableArray, но более быстрый), соответственно CCArray теперь отвечает за вызовы retain и release.

Некоторые разработчики считают, что autorelease работает медленно и его нужно избегать. Общая рекомендация здесь такая - использовать описанный подход со статической инициализацией при работе с объектами cocos2d, а при использовании своих объектов - использовать тот подход, который удобнее вам.
Стоит всегда помнить, что созданный autorelease-объект исчезнет в следующем фрейме (кадре), поэтому если вы на каждом кадре создаете кучу autorelease-объектов, это может стать причиной тормозов в приложении.

Итоги

Это была сложная статья!
Здесь мы наконец-то дошли до прямого разбора кода и поговорили об управлении памятью.

Если у вас есть какие-то вопросы, обязательно задавайте их - я постараюсь отредактировать текст записи, чтобы дать ответы на все сложные вопросы.

Теперь стоит передохнуть и двигаться дальше - у нас впереди небольшой практикум!


Иллюстрации и примеры кода взяты из книги Learn cocos2d Game Development with iOS 5.
Посетите также блог автора книги ("Learn Cocos2d", Steffen Itterheim)

2 комментария:

  1. Напиши пожалуйста об управлении памятью в iOS.

    ОтветитьУдалить
    Ответы
    1. В одном из последних постов я писал, что теперь мы будем работать с cocos2d 2.0 - там управление памятью автоматическое - благодаря технологии ARC, которая сама определяет, когда нужно вставить необходимые вызовы для очистки памяти.

      Поэтому теперь это не актуально :)
      Про ARC можно почитать тут: http://habrahabr.ru/post/129874/

      Удалить