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

Немного практики

Первое приложение

Наконец, мы готовы создать наше первое приложение!



Итак, создайте новый шаблонный проект  cocos2d (или используйте тот, который был создан в прошлом уроке).

В этом уроке мы поиграем с распознаванием касаний.
Откройте файл HelloWorldLayer.m и в методе -(id)init после строчки [self addChild: label];
добавьте следующие строки:


label.tag = 13;
self.isTouchEnabled = YES;

Таким образом, наш метод выглядит так:
-(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];
        
label.tag = 13;
self.isTouchEnabled = YES;
}
return self;
}

Что же делают наши две строки?
Сначала мы присваиваем нашей надписи тэг - уникальный номер, по которому ее можно будет идентифицировать в нашем HelloWorldLayer.
Поскольку мы не храним label как отдельный член класса, а добавляем его в коллекцию, то, чтобы иметь возможность обращаться к нашей label, мы используем этот номер.
Почему именно 13? В данном случае - просто так. Вообще, по-хорошему, в вашем коде не должно быть случайных значений - лучше создать целочисленную константу с самоописывающим именем и присвоить ее. В дальнейшем мы так и будем делать.
Важно: тэг - это положительное число и для каждого объекта он должен быть разный (иначе непонятно, какой объект из двух объектов с одинаковыми тэгами мы получим).

Далее, присваивая YES свойству isTouchEnabled, мы сообщаем, что хотим получать события о касаниях.
Для обработки касаний используются несколько методов, один из них это ccTouchesBegan:withEvent:. Он вызывается каждый раз, когда начинаются касания.

Обработка касаний

Добавим в код нашего класса HelloWorldLayer этот метод:

-(void)ccTouchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
    CCLabelTTF* label = (CCLabelTTF*)[self getChildByTag:13];
    label.scale = CCRANDOM_0_1();
}

Что делает этот код?
Каждый раз, когда пользователь касается экрана, мы:
1-ая строка: получаем объект с тэгом 13 (как мы помним, это надпись) и приводим его к типу CCLabelTTF, сохраняя под идентификатором label
2-ая строка: нашему объекту label присваиваем scale (масштаб) от 0 до 1, используя встроенный в cocos2d макрос CCRANDOM_0_1(), который возвращает случайное число от 0 до 1.

Если теперь запустить наше приложение и несколько раз потыкать по экрану, то надпись будет меняться в размерах :)

Важно обратить ваше внимание на первую строчку. Мы используем явное приведение возвращаемого методом getChildByTag элемента к CCLabelTTF, поскольку знаем, что только что его туда положили.
Но хорошая практика в таких случаях - проверять, что объект действительно предполагаемого типа (иначе нас ждет крах приложения с ошибкой EXC_BAD_ACCESS).

Можно переписать код нашего метода следующим образом:
CCNode* node = [self getChildByTag:13];
NSAssert([node isKindOfClass:[CCLabelTTF class]],
@"node is not a CCLabelTTF!");
CCLabelTTF* label = (CCLabelTTF*)node;
label.scale = CCRANDOM_0_1(); 


Мы вызываем NSAssert и предполагаем, что наш node имеет класс CCLabelTTF. Если это правда - все будет в порядке, если нет - приложение упадет и напишет нам указанную во втором параметре ошибку.
Вы можете спросить - а если я каждый раз буду проверять, не будет ли моя программа тормозить?
Нет! Вызовы NSAssert убираются в Release-версии приложения автоматически, поэтому код по производительности остается тем же самым.

Итоги

Итак, мы создали наше первое приложение :) Не просто создали, но и написали реальный, работающий код!

Дальше нас ждет более подробный разбор различных функций cocos2d, ну а пока хочется отметить несколько важных моментов, которые могут встретиться вам при разработке приложений:
  1. У каждого устройства Apple (iPod, iPhone, iPad) разных поколений есть свои, пусть и незначительные (а иногда, значительные) отличия, про которые не стоит забывать при разработке игр. Например, у iPhone 4 оперативной памяти 512 мегабайт, а у iPod Touch 4 - 256 мегабайт, как и у iPhone 3Gs и iPad 1го поколения. Поэтому, если у вас игра идет хорошо на одном устройстве, это не значит, что она будет хорошо идти на всех. Технические спецификации можно посмотреть на сайте Apple для iPhone, iPod, iPad.
  2. Опять же, помните, что в мире еще много старых устройств (у меня у самого iPhone 3Gs :) ), поэтому, выпуская игру, идущую лишь на самых новых устройствах, вы можете лишить себя большого количества потенциальных покупателей
  3. По поводу оперативной памяти - сейчас используются разное количество оперативной памяти (128 мб, 256 мб, 512 мб, а теперь, кажется, и 1 гб) - но, конечно, не вся эта память доступна приложению - большой кусок использует сама операционная система, а так же работающие в background-режиме процессы. По существующим подсчетам, на устройствах с 128 мб памяти вам доступно (до memory warning) 20-25 мб, максимум 35-40 мб (далее система просто убьет приложение). На устройствах с 256 мб памяти - 80-90 мб до предупреждения, 120-150 максимум. На устройствах с 512 мб памяти - 260-300 мб до прежупреждения, 340-370 максимум.
  4. О memory warning'ах (ошибка, связанных с малым количеством оставшейся памяти). Предупреждение первого уровня обычно можно игнорировать (если не планируется более выделять памяти), а вот предупреждение второго уровня - это, по сути, предупреждение от системы о том, что если приложение сейчас же не освободит память, оно будет убито. Освобождать память нужно в вашем классе AppDelegate, в методе 
    - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application. Например, таким образом:
    - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application 
    {
    [[CCDirector sharedDirector] purgeCachedData];
        [[CCTextureCache sharedTextureCache] removeUnusedTextures];
    }
    Здесь, мы просим cocos2d освободить кэшированные данные и убрать из памяти неиспользуемые текстуры. Конечно, в сложных приложениях может потребоваться более сложная схема очистки памяти. Более того, помните, что приложение может притормаживать при освобождении больших кусков памяти. А если cocos2d удалит текстуру, которую нужно использовать через несколько кадров, то придется снова загружать ее, на что также тратится драгоценное время.
  5. Помните о приложениях, работающих в бэкграунде - можно попробовать протестировать игру при нескольких таких приложениях и посмотреть на ее поведение.
  6. Замерить употребляемую память можно, используя приложение Instruments - более подробно о его использовании можно почитать в интернете :)
  7. Всегда тестируйте игру на устройстве (по крайней мере, каждое важное изменение в игре), а не только на симуляторе, поскольку производительность симулятора и производительность устройства - совершенно разные. Аналогично, потребление памяти (игра в симуляторе может получить гораздо больше памяти, чем на устройстве) + многих функций, таких как акселерометр, GPS, камера и т.д. на симуляторе банально нет :) К тому же, поведение на симуляторе иногда может отличаться от поведения на устройстве.
  8. Для измерения производительности игры используйте Release-сборку, а не Debug-сборку, поскольку в Debug-сборке, например, не отключено логирование и некоторые другие функции, что в целом может повлиять на производительность.
Уфф, советов получилось много. Какие-то из них могут быть непонятны, но, как всегда, не стоит пугаться - когда мы будем сталкиваться с этими вещами на практике, я снова буду упомянать о них и вы сможете понять, в чем дело :)

Как всегда, задавайте вопросы в комментарии - возможно, благодаря вам я смогу улучшать качество своих лекций :)


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

1 комментарий: