Cocos2D-X і щоб легко на всіх пристроях

Кілька років робив замовні игрульки під iOS. В умовах, коли ніколи точити, а потрібно пиляти, йдеш в гугл і питаєш. Гуглокодінг. Ось і звела доля мене з Cocos2D for iPhone і теплим ламповим www.raywenderlich.com
 
Мені Objective-C сподобався, як і сам cocos2D. М'який як пластилін. Після пристойних років писанини на C + + все якось спростилося. На жаль, тільки iOS. Безумовно, з'явилися всякі Apportable , однак я не хотів чомусь дивитися в ту сторону. До того ж відчувалася втома від однієї і тієї ж платформи і хотілося свого проекту, при тому, щоб гралося на кожній мікрохвильовці. Unity начебто хороший, але закритий, а для мене дуже важливо знати, як воно працює зсередини: оцінити потенційні боттлнекі, щось оптимізувати (доводилося часто за практику), та навіть просто баги пофиксить. Плюс, хотілося почати щось робити прям зараз. А оскільки з моделлю айфоновского кокоса я був дуже добре знайомий, було прийнято рішення поглянути на cocos2D-X . Той, що на C + +.
 
Те ж саме. Просто на С + +. Ті ж release / retain (в останній 3.x версії деякі моменти вже змінилися), та ж модель з нод. Поганяв тестовий стенд (завжди, завжди дивіться приклади роботи движка) — все спритно працює. Згадав я, правда, про одну штуку — про Android з його безліччю дозволів екранів.
 
Коли я робив гри під айфони, все було просто. Якщо потрібна була підтримка ретини, то достатньо було встановити лише один прапор:
 
 
[director enableRetinaDisplay: YES]

і додати набір HD-графіки. Під айпеди все було злегка інакше: був потрібний деякий перерахунок координат. З появою п'ятого айфонів все трохи ускладнилося, але трошки. Однак, коли ти Андроїд і в тебе майже необмежену кількість всяких екранів, потрібно трохи включити мозок. Ось, що ми зробили.
 
Ми додали конфіги. Вся магія твого проекту починає відбуватися в сгенерированном кокосовскім скриптом класі AppDelegate . В одному з його методів запускається і активується сцена:
 
 
bool AppDelegate::applicationDidFinishLaunching() {
	Director *director = Director::getInstance();
    EGLView *eglView = EGLView::getInstance();

    director->setOpenGLView(eglView);
    
	...
	
	Scene *scene = GameScene::scene();

    director->runWithScene(scene);
    return true;
 }

У сцені ти вантажиш свої спрайт / інші ноди, якось їх позиціюєш, коротше, якщо буде потрібно, і т.д. Механізм створення, наприклад, спрайту тісно пов'язаний з класом FileUtils . Всякий раз, коли ти твориш щось на кшталт:
 
 
Sprite *object = Sprite::create("object.png")

внутрішні методи (добре мати код під рукою) класу Sprite просять FileUtils побігати за списком встановлених шляхів пошуку і подивитися, чи є там такий-то файл. Шляхи пошуку? Та це просто. Можна створювати спрайт так:
 
 
Sprite *enemy = Sprite::create("enemies/enemy0.png")

а можна сказати кокосу, що у нас є деяка директорія або навіть список, в яких слід шукати файли. Це скорочує писанину, відповідно, зменшує кількість помилок і надає гнучкості. Захотів згрупувати файли іншим чином — будь ласка, в коді не доведеться міняти всі «enemies /» на, приміром, «objects /». Дійсно удобноі легко:
 
 
FileUtils::getInstance()->addSearchPath("fonts");
FileUtils::getInstance()->addSearchPath("objects");
FileUtils::getInstance()->addSearchPath("backgrounds");

...

Sprite *back = Sprite::create("back0.png");
LabelBMFont *label = LabelBMFont::create("an incredible label", "font0.fnt");

Щось я, здається, говорив про всякі дозволи екранів. У cocos2D-X вже немає всяких
 
 
[director enableRetinaDisplay: YES]

замість цього з'явилася пара нових речей. Хочеш дізнатися фізичний розмір екрану? Зроби так:
 
 
Size frameSize = Director::getInstance()->getOpenGLView()->getFrameSize();

Ще я щось говорив про конфіг. Це простий json-файл (Парс rapidson ), в якому ми прописали набори шляхів ресурсів (ох, цей знахідний відмінок). Кожен набір виглядає так:
 
 
{
	"width": 960,
	"height": 640,
	"designWidth": 480,
	"designHeight": 320,
	"paths":
	[
	    "Res/960x640/fonts/",
	    "Res/960x640/ui/",
		"Res/960x640/maps/"
		...
	]
}

Тут width і height представляють фізичні розміри, для якого цей набір ресурсів міг би підійти. Вже вловлюєш, до чого я хилю? При старті додатки в AppDelegate :: applicationDidFinishLaunching я завантажую конфіг, бігаю по всіх наборів шляхів і звіряю фізичний розмір, отриманий з Director :: getInstance () -> getOpenGLView () -> getFrameSize () , з тими width і height і завантажую потрібних розмірів картинки. Цього, однак недостатньо.
 
Для більш глибокого розуміння піди сюди:
 www.cocos2d-x.org/wiki/Multi_resolution_support
 
Для практичного ж використання досить знати наступне. Є Director :: getInstance () -> getOpenGLView () -> setDesignResolutionSize () , який встановлює відносні розміри. Твій айфон, наприклад, має розмір 960x640 і центр екрану може бути представлений координатою {480, 320}. Однак, можна передати в setDesignResolutionSize які-небудь {96, 64}, і тоді центр можна буде задати за допомогою {48, 32}. Відносні координати дуже важливі, а ті два параметри designWidth і designHeight з конфіга їх і виставляють. Отже, ми бігаємо по конфігу, звіряємо розміри, вантажимо потрібні ресурси і встановлюємо правильні відносні координати. Ми майже на фінішній прямій.
 
Уяви, що ти запускаєш гру на пристрої з величезним дозволом екрану, але набору ресурсів, щоб прям pixel perfec t, немає. Нічого страшного — ми просто скажемо кокосу, щоб він розтягнув картинку. Для цього є Director :: getInstance () -> setContentScaleFactor () , що приймає деякий float. Просто поділи (у разі, якщо гра портретна) width з конфіга на designWidth і буде тобі щастя на всіх платформах.
 
Є ще один параметр, останній — ResolutionPolicy . Якщо чесно, то корисних там два штуки: ResolutionPolicy :: FIXED_WIDTH і ResolutionPolicy :: FIXED_HEIGHT . Хочеш грати в портретній орієнтації — став ResolutionPolicy :: FIXED_WIDTH . При цьому, картинка розтягнеться по ширині, наприклад, як у моїй грі solve Me :
 
 
 
При такому підході може з'явитися додатково вертикальний простір. У прикладі вище я просто розтягував фон так, щоб замостити весь екран, а елементи інтерфейсу розташовував щодо його країв. Однак, це працює не завжди. Так, в іншій моїй грі reTales з ландшафтної орієнтацією (використовувався ResolutionPolicy :: FIXED_HEIGHT ) я просто малював два спрайту по краях екрану:
 
 
 
Бігаємо по конфігу, зчитуємо набори шляхів, порівнювати з фізичними розмірами, вантажимо потрібні ресурси, застосовуємо вірну політику і розтягуємося. Але я пішов ще трохи далі — прикрутив локалізацію. На рівні движка. Особливість FileUtils :: getInstance () -> addSearchPath () в тому, ресурси будуть шукатися строго по тим шляхам, які були встановлені, і в тому ж порядку. Не знайшов в «fonts /», йде в «ui /», не знайшов там — йде в «maps /» і далі шукає в корені у випадку невдачі. Прекрасно. Адже ми можемо отримати поточний мову за допомогою Application :: getInstance () -> getCurrentLanguage () і в момент додавання шляху додати спочатку шлях, характерний для конкретної мови, а потім і сам цей шлях. Краще один раз побачити. Буде відбуватися приблизно наступне:
 
 
FileUtils::getInstance()->addSearchPath("fonts/en");
FileUtils::getInstance()->addSearchPath("fonts");

FileUtils::getInstance()->addSearchPath("objects/en");
FileUtils::getInstance()->addSearchPath("objects");

FileUtils::getInstance()->addSearchPath("backgrounds/en");
FileUtils::getInstance()->addSearchPath("backgrounds");

У підсумку, при спробі створити спрайт кокос спочатку подивиться в локалізовану папку, потім в папку загальну. Потрібно, щоб шрифти вантажилися в залежності від мови за замовчуванням? Просто створи папку. Потрібно, щоб конкретні спрайт (прапор на нашивці солдата) вантажилися відповідно до мови гравця? Створи папку і кинь туди потрібний файл. Сам текст просто лежить в таких же json-файлах як і конфіг. Замість
 
 
Label::create("This is label", "font.fnt") 

просто використовуй
 
 
Label::create(Localized::getString("mainMenuCaptionLabel"), "font.fnt")

Клас Localized і багато іншого ти зможеш завантажити за посиланням нижче. Мені не шкода :)
 
Спасибі, щоб був зі мною, читач. Не забувай годувати свого кота — він дуже тебе потребує.
 
Приклад робочого конфіга — pastebin.com/5idCpjYh
Приклад файлу з рядками — pastebin.com/LfBxs6dA
Localized.h — pastebin.com / LwNaKrFK
Localized.cpp — pastebin.com / GHVmPvCc
Завантаження шляхів — pastebin.com/CX8Xma25
Як запустити все це справа в AppDelegate — pastebin.com/dMVtY2Rb

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.