Анімація у Cocos2d-x

8 хв. читання

Виділяють два способи створення анімації:

  • покадрова: художники малюють ряд кадрів, а Cocos2d їх послідовно показує, використовуючи клас Animation. Робота з ним схожа на інші акції, хоча сам клас має трошки інше походження;
  • скелетна: у спеціальних програмах формується модель персонажа, а потім завантажується у Cocos2d-x.

Покадрова анімація

Для прикладу візьмемо анімацію свічки з безкоштовного паку. Там є лише три кадри, їх дуже просто завантажити й використати ось так:

Vector<SpriteFrame*> animFrames;
animFrames.reserve(3);
animFrames.pushBack(SpriteFrame::create("candle/SC300-1.png", Rect(0,0,64,64)));
animFrames.pushBack(SpriteFrame::create("candle/SC300-2.png", Rect(0,0,64,64)));
animFrames.pushBack(SpriteFrame::create("candle/SC300-3.png", Rect(0,0,64,64)));

// create the animation out of the frames
Animation* animation = Animation::createWithSpriteFrames(animFrames, 0.4f);
Animate* animate = Animate::create(animation);

// run it and repeat it forever
Sprite* candleSprite = Sprite::create();
candleSprite->runAction(RepeatForever::create(animate));

Тут цифри у Rect(0,0,64,64) такі, тому що файли розміром 64х64 пікселі.

Проблема в тому, що практика прямого завантаження файлів, яку ми досі використовували у всіх прикладах, насправді є досить поганою. Вона підходить для навчання, але краще використовувати списки файлів (sprite sheet), про роботу з якими добре написано у цій довгій, але дуже корисній статті про анімацію.

Якщо коротко, то є такий клас SpriteFrameCache і він може завантажувати зображення, описані у файлах *.plist. Такі файли можна створювати у різних програмах: непоганим варіантом є TexturePacker, також можна використовувати Shoebox, SpriteSheet Packer та Zwoptex. Результатом пакування є два файли:

  • одне велике зображення, що об'єднує усі передані ресурси. Оскільки це один файл, він займає менше місця та швидше завантажується;
  • файл *.plist, що описує упаковані ресурси.

Наприклад, можна взяти чотири зображення істоти у капюшоні, створити на його основі plist та ресурс і використати ось так:

SpriteFrameCache* sfc = SpriteFrameCache::getInstance();

const string violetMonsterFN = "littleCandleMonster/littleCandleMonster_violet.plist";
sfc->addSpriteFramesWithFile(violetMonsterFN);

Vector<SpriteFrame*> animFrames;
Animation *monsterAnimation = Animation::create();
char tmps[256];
for (int i = 1; i<=4; i++) {
  sprintf(tmps, "lm-1-%i.png", i);
  SpriteFrame * sf = sfc->getSpriteFrameByName(tmps);
  monsterAnimation->addSpriteFrame(sf);
}

monsterAnimation->setDelayPerUnit(0.1);

Animate* animate = Animate::create(monsterAnimation);

Можна позбавитись від переліку імен файлів у коді, якщо використати метод addAnimationsWithFile класу AnimationCache. Тоді завантаження анімацій відбуватиметься так:

const string plistFilename = "skeleton/skeleton_images.plist";
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(plistFilename);

const string animationsPlistFN = "skeleton/skeleton_animations.plist";
AnimationCache::getInstance()->addAnimationsWithFile(animationsPlistFN);

А використання так:

Animation* animation = AnimationCache::getInstance()->getAnimation(idleAnimationName);
Animate* animate = Animate::create(animation);
skeletonSprite->runAction(animate);

Недоліком такого способу є те, що він вимагає якогось особливого plist-файлу. Цей файл відрізняється від тих, що створює Texture Packer (хоча розширення однакові) і його доводиться виписувати вручну. Для нашого прикладу цей файл матиме такий вигляд.

У кожному разі отримана в результаті акція animate може використовуватись так само, як і будь-яка інша акція. Ось, наприклад, фіолетове створіння пересувається подібно до зеленого НЛО з попередньої статті.

Анімація у Cocos2d-x

Скелетна анімація з Dragon Bones

Існує багато різних засобів для створення скелетної анімації, але більшість з них платні, а тому недоступні для вивчення. У цій статті мова піде про Dragon Bones — доступний, але трохи своєрідний варіант.

Основною перевагою Dragon Bones є те, що ця програма має достатню функціональність і при цьому є безкоштовною. Точніше, редактор є безкоштовним із закритим кодом, а код для інтеграції з Cocos2d розповсюджується за ліцензією MIT.

Основний недолік — китайське походження, і в цьому випадку йдеться не просто про географічні характеристики. Скажімо, зараз вам навряд вдасться завантажити редактор з офіційної сторінки, там виникли якісь проблеми з нововведеннями в сучасних браузерах. Код інтеграції, розташований в офіційному репозиторії, вже трохи застарів, тому я б радив брати за основу для своєї розробки ось цей репозиторій, там внесені усі зміни, необхідні для Cocos2d V4.

Хай там як, після подолання початкових проблем Dragon Bones працює стабільно і з ним можна робити досить круті штуки. Наприклад, такі:

Анімація у Cocos2d-x
Приклад CoreElementGame з офіційного репозиторію

Зрозуміло, що це просто малюнок, тут цього робота можна побачити розібраним на запчастини.

Для прикладу в цій статті ми візьмемо персонажів з безкоштовного набору і дамо їм в руки мечі та вогнепальну зброю.

Процес створення моделі та анімації я не буду описувати, тому що це скоріше справа художників, що має свої нюанси. Можна порадити такі навчальні матеріали: перший, другий. Натомість краще сфокусуватись на особливостях, важливих для програмування.

Результатом експорту з Dragon Bones до Cocos2d є три файли: два json, що описують модель, та одне зображення-ресурс.

Імпорт моделі у програму схожий на завантаження plist-файлів:

#include "dragonBones/cocos2dx/CCDragonBonesHeaders.h"

dragonBones::CCFactory* factory = dragonBones::CCFactory::getFactory();
factory->loadDragonBonesData("db_export/BlueWizard_ske.json", "BlueWizard");
factory->loadTextureAtlasData("db_export/BlueWizard_tex.json", "BlueWizard");

dragonBones::CCArmatureDisplay* wizard = factory->buildArmatureDisplay("WizardArmature", "BlueWizard");

wizard->setAnchorPoint(Vec2(0.5,0.5));
wizard->setScale(0.33);
wizard->setPosition(160,140);
addChild(wizard, 10);

CCArmatureDisplay — клас для об'єктів, з якими ви працюєте на сцені. Він є нащадком Node, тому з ним можна робити всі ті самі речі, що й з іншими об'єктами Cocos2d.

«BlueWizard» — це щось типу тегу, який відрізняє завантажену модель від усіх інших. У документації цей параметр згадується як displayName.

«WizardArmature» — назва скелета (armatureName), яка вказується у редакторі, ось тут:

Анімація у Cocos2d-x

Програвання анімації викликається ось так:

wizard->getAnimation()->play("idle01", 1);//1 to play animation once

Це матиме такий вигляд:

Анімація у Cocos2d-x

Частини скелета можна змінювати одразу під час виконання програми, досягти цього можна двома способами.

По-перше, редактор дозволяє в одному слоті зберегти кілька варіантів зображень. Наприклад, тут у слоті «Gun» їх три:

Анімація у Cocos2d-x

У такому випадку перемикання з одного зображення на інше виконується простим вказуванням індексу:

int currentWeaponIndex;
<....>
wizard->getArmature()->getSlot("Gun")->setDisplayIndex(currentWeaponIndex);

По-друге, можна створити окрему модель, у якій будуть лише змінювані зображення. Тоді завантаження і використання будуть складнішими:

dragonBones::CCFactory* factory = dragonBones::CCFactory::getFactory();
factory->loadDragonBonesData("db_export/MagentaKnight_ske.json", "MagentaKnight");
factory->loadTextureAtlasData("db_export/MagentaKnight_tex.json", "MagentaKnight");

factory->loadDragonBonesData("db_export/WeaponPack_ske.json", "WeaponPackMK");
factory->loadTextureAtlasData("db_export/WeaponPack_tex.json", "WeaponPackMK");

knight = factory->buildArmatureDisplay("KnightArmature", "MagentaKnight");

<..пізніше..>
factory->replaceSlotDisplay("WeaponPackMK", "WeaponPackArmature", "OneHandWeaponSlot",
                            "morning_star", knight->getArmature()->getSlot("Weapon"));

Тут «WeaponPackArmature» та «OneHandWeaponSlot» — ідентифікатори скелета і слоту, вказані в редакторі:

Анімація у Cocos2d-x

В обох випадках результат буде однаковим:

Анімація у Cocos2d-x

Перехід між сценами

На завершення хотілося б розглянути одну тему, яка не пов'язана з анімаціями, але її легко продемонструвати саме на анімованих сценах. Як ви пам'ятаєте, розробники Cocos2d хотіли, щоб гра зсередини нагадувала театральну виставу: ряд сцен, що змінюються за командою режисера (Director).

Власне, для зміни сцен у Director є такі методи:

  • replaceScene() замінює поточну сцену на нову;
  • popScene()/pushScene() працюють зі стеком сцен. push ставить поточну сцену на паузу та зберігає на стеку, потім запускає нову. Відповідно, pop видаляє поточну сцену та забирає зі стека старішу;
  • popToRootScene() та popToSceneStackLevel(int level) реалізують особливі випадки.

У кожному разі нова сцена повністю закриває те, що було показано до її запуску. Зазвичай така поведінка є нормальною, але можливі винятки. Припустимо, ви хочете показати якесь повідомлення або просто поставити гру на паузу. Щоб затримка була більш очевидною для гравця, буде краще, якщо події нової сцени відбуватимуться на фоні старої.

Наприклад, ось такий перехід:

Анімація у Cocos2d-x

Спочатку тут була якась сцена з анімацією-свічкою та об'єктом, що рухається. Потім це змінюється на сцену з повідомленням та меню, але ця нова сцена має містити старі елементи.

У Cocos2d немає прямого засобу, який би дозволяв досягти такого ефекту. Проте можна зробити два кроки: зберегти зображення, згенероване для старої сцени, і використати його як фон у новій.

Приблизно так:

void StaticInterruptScene::switchToInstantScene() {
    const Size visibleSize = Director::getInstance()->getVisibleSize();
    RenderTexture* rt      = RenderTexture::create(visibleSize.width,
                                                   visibleSize.height);

    rt->begin();
    this->visit();
    rt->end();
    rt->getSprite()->setAnchorPoint(Vec2(0, 0));

    Scene* ims = InstantMenuScene::create(rt);

    Director::getInstance()->pushScene(ims);
  }
}

Тут об'єкт RenderTexture зберігає зображення, згенероване для старої сцени. InstantMenuScene — це нова сцена, використання переданого зображення там майже не відрізняється від будь-якого іншого, тому я тут його не згадую.

На цьому все, дякую за увагу.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.6K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація