Skip to content

Универсальные Mac iOS Retina меню с Cocos2D

psineur edited this page Apr 15, 2011 · 1 revision

Универсальные Mac/iOS/Retina меню с Cocos2D

Привет! В этой статье я хотел бы поделиться с вами своим опытом разработки меню с использованием Cocos2D для iPhone(SD+Retina), iPad & Mac.
Сначала я разработал iTraceur только для iPhone, потом перенес его на iPad, потом iPad версию перенес на Mac и наконец добавил все эти изменения и поддержку ретины в iTraceur 1.3 для iOS.

iTraceur - моя первая игра для iOS и первое приложение с использованием Cocos2D. Так что довольно часто разработка шла не оптимально, и я наступал на различные грабли.
Да кстати, тогда я использовал Cocos2D версии 0.7.2 и в процессе работы добавлял довольно много костылей в движок. В последствии я смог избавиться от большинства, хотя некоторые до сих пор висят - можете посмотреть мою ветку Cocos2D (Кстати там может быть что-нибудь полезное, что я еще не выложил в виде отдельного репозитория с примерами)

В iTraceur с версии 1.0 до 1.2.2 у меня под меню был отведен один файл MenuScreens.h/m, объемом чуть больше 7000 строк кода.
Положения элементов были жёстко зашиты в код (hardcoded), а все элементы были спрайтами, сгруппированными по принадлежности к различным экранам в атласы. Стоит отметить, что текст также был сделан картинками, а шрифты не использовались.
В процессе добавления новых пунктов меню, атласы приходилось пересобирать, а жестко закодированные позиции элементов - изменять и проверять каждый раз перекомпиляцией.
Это было довольно не удобно. ;)

В процессе портирования на iPad пришлось заменить все графические ресурсы меню. Слава богу мне хватило ума рисовать силуеты, кнопки и прочую сложную графику в векторных форматах или в большем разрешении, поэтому прямой перерисовки не было - только сохранение и перекомпоновка ресурсов, но это тоже довольно хлопотная работа, которая занимает много времени.
Для iPad версии также пришлось изменить код меню. Не сильно, но #ifdef'ов это в и без того неуклюжий файл добавило.

В процессе работы над iTraceur & iTraceur HD я не использовал никакой SCM, а необходимые мне фиксы и фичи кокоса добавлял в свой проект вручную.
Это еще добавило костылей и уродства в код =)

Для портирования на Мак мне пришлось обновить Cocos2D, потому что в 0.7.2 поддержки Mac OS X еще не было. Вот с начала работы над Мак версией я и начал выкидывать костыли. Я выкинул MenuScreens и написал все меню с нуля, используя предыдущий файл лишь как документацию к именам файлов ресурсов и возможностям, которые необходимо реализовать. Это позволило сделать меню совместимыми как с Mac, так и с iOS.
Так как разрешение iPad'a довольно близко к разрешению 13 дюймовых макбуков, я решил использовать те же самые ресурсы для новых меню, используя лишь грамотное масштабирование и расположение для получения хорошей картинки при любом разрешении экрана. В итоге эти же самые меню (с теми же самыми ресурсами) я использовал и для 1.3, которое является Universal app. Если в будущем Apple сделает возможным универсальные приложения для Mac & iOS - мне не придется переделывать меню, они и так уже универсальные.

Также в 1.3 я решил добавить поддержку ретины, что было довольно просто благодаря возможностям Cocos2D. Некоторые ресурсы даже не пришлось дублировать для hd качества, потому что они были созданы для экрана iPad, у которого разрешение больше чем у экрана Retina.

Для создания универсальных меню я использовал:

  1. CCMenuAdvanced - SubClass CCMenu, это меню размер которого расчитывается на основе содержимого, а не устанавливается равным размеру экрана, что позволяет свободно манипулировать размерами и положением меню. (Также там еще есть управление клавиатурой для мака, прокрутка, свойство приоритета и может еще чего вкусное… )
  2. Gimp в качестве редактора. (Кстати! В Windows при перетаскивании png файла в окно гимпа создается новый слой с именем файла - очень удобно, жаль что на Маке этого нет)
  3. xc2sprites (модификация xcftools) для парсинга xcf файла в plist
  4. CCMenuEditor в качестве загрузчика полученного plist.
Warning:  
xcf2sprites + CCMenuEditor не являются тем, чем я мог бы гордиться, а в этой статье я использую их лишь для иллюстрации идеи.  
Из xcf файла можно получить лишь имя слоя, его положение и размеры, при этом насколько слой был растянут или повернут - узнать невозможно, так что это довольно
ограниченное средство - не стоит на него рассчитывать.

Прежде чем перейти непосредственно к коду, я хотел бы дать несколько общих рекомендаций, которые помогут быстро и удобно разрабатывать меню, независящее от разрешения экрана и подходящее для Mac, iPhone, iPhone Retina & iPad:

  1. Никаких hardcoded положений/размеров в пикселях/поинтах в коде.
  2. Если нужно что-то жестко зафиксировать - выделите это в отдельную CCNode и создавайте ее содержимое с помощью редактора.
  3. Не используйте картинки для текста - используйте шрифты. Для кастомных шрифтов используйте CCLabelBMFont (Кстати текст легче будет локализовать, чем спрайты)
  4. У одной и той же текстуры может быть разный размер в point'ах на разных устройствах. Учитывайте это при масштабировании (например как это сделано в DynamicTiledLevelNode или Бэкграунд картинках в примерах с CCMenuAdvanced )
  5. При планировании игровых экранов и меню думайте не об идеальном(фиксированном) размере экрана, а об минимальном и максимальном.
  6. Создавайте и тестируйте меню на Маке в режиме kCCDirectorResize_NoScale, изменяя размеры окна.
  7. Пусть ваши ресурсы будут готовы к различным разрешениям (лучше всего иметь их в векторе, или сильно превосходящем разрешающую способность экрана разрешении)

При создании меню я придерживался следующего подхода, может быть это даже можно назвать паттерном:

  1. [[CCDirector sharedDirector] winSize] != const, так что запоминать его нигде не стоит.
  2. Элементы создаются и добавляются в ноду как дети в init методе.
  3. В конце init метода вызывается [self updateForScreenReshape]
  4. updateForScreenReshape каждый раз запрашивает размеры окна у директора заново (смотри пункт 1) и на основе этих и других данных переставляет и масштабирует элементы
  5. updateForScreenReshape вызывается каждый раз, когда изменяется размер winSize.

Пример (главное меню iTraceur 1.3):

- (void) updateForScreenReshape
{
	// size of window
	CGSize winSize = [[CCDirector sharedDirector] winSize];
	
	// background to fit the window
	[_backgroundLayer setContentSize: winSize];

	// caption must be not more that 1/5 window height, and must not be overscaled
	_nameLogo.scale = (winSize.height / 5.0f) / ([_nameLogo contentSize].height);
	_nameLogo.scale = MIN(_nameLogo.scale, 1.0f);
	
	// position caption at top center
	_nameLogo.anchorPoint = ccp(0.5f, 1.0f );
	_nameLogo.position = ccp(winSize.width / 2.0f, winSize.height);	
	
	// BG silhouette must fit on screen in height and 1/2 of screen in width
	_cornerSil.scale = (winSize.width / 2.0f) / [_cornerSil contentSize].width;
	_cornerSil.scale = MIN(_cornerSil.scale, winSize.height /  [_cornerSil contentSize].height);
	_cornerSil.scale = MIN(_cornerSil.scale, 1.0f);	
	
	// position BG Silhouette
	_cornerSil.anchorPoint = ccp(1,0);
	_cornerSil.position = ccp(winSize.width, 0); 	
	
	// active Size is space that left for menu after caption
	CGSize activeSize = CGSizeMake( winSize.width, winSize.height - _nameLogo.scale * [_nameLogo contentSize].height);
	
	//scale and position menu widget
	_widget.scale = (winSize.width / 2.0f) / [_widget contentSize].width;
	_widget.scale = MIN(_widget.scale, activeSize.height / [_widget contentSize].height);
	_widget.scale = MIN(_widget.scale, 1.0f);
	_widget.anchorPoint = ccp(0, 0.5);
	_widget.position = ccp(0, activeSize.height / 2.0f);
	
	//position social widget
	_socialWidget.anchorPoint = ccp(1,0);
	_socialWidget.position = ccp(winSize.width, 0);
}

Репозиторий CCMenuAdvanced находится здесь В истории коммитов начиная с 4го и на протяжении 31 коммита идет разработка демо универсального приложения, сначала на Mac, потом готовый код собирается под iOS и в конце добавляется поддержка ретины.

Вот и все, пожалуй. Любые комментарии, вопросы и дополнения приветствуются, как всегда ;) Этот блог я завел с целью делиться подобными штуками, которые я использовал при разработке. Я думаю что хороший код зачастую гораздо выразительнее постов в блоге, поэтому о FileDownloader и DynamicTiledLevelNode я скорее всего не буду писать в блог, а просто выложу их в виде отдельных репозиториев на GitHub'e

Если вас заинтересуют другие детали разработки iTraceur - спрашивайте, буду рад помочь.

P.S. На момент написания этой статьи 1.3 ожидает рассмотрения в AppStore. Текущая версия - 1.2.2 не является Universal app и не имеет поддержки ретины.