Cocos2d-x RU‎ > ‎

Статья 3

Статья 3. Базовые понятия движка. 
Давайте добьёмся получения целостной картины... Чтобы тем, кто только начинеает знакомиться с cocos2d-x было более менее понятно как всё работает.
В этой статье я расскажу про ноды, сцены, слои
К моменту чтения и понимания этой статьи Вам необходимо развернуть проект HelloWorld. Как это сделать зависит от Вашей ОС. Под Mac OS все делается легко и не принуждённо, под Windows не пробовал. Для Mac-оводов, надо сходить на официальный сайт cocos2d-x.org, скачать движок, развернуть архив в нужном месте на диске. От имени администратора запустить скрипт (install-templates-xcode.sh) по установке шаблонов в XCode. У меня заняло времени, в первый раз, минут 30. Так что разберётесь. 
Далее создаем новый проект, выбираем шаблон cocos2dx, вводим имя проекта и указываем место на диске, куда развернуть проект. Жмём «Create». С этим проблем не должно возникнуть, там работает специальный «мастер». Будем считать, что создан.
Для тех, кто на винде, погуглите как создать проект cocos2dx. Если создать не получается, то хотя-бы запустите тестовый проект. Если запустить удалось, то скопируйте его в новую папку, назовите папку HelloWorld, к примеру. Создайте 2-а файла 
HelloWorld.h и HelloWorld.cpp. В первом файле будет объявление слоя, а во втором его реализация. 
Как уже говорилось в первой статье, все объекты являются наследниками от CCObject. Класс CCNode не исключение - он порожден от CCObject, а от него порождены такие объекты, как сцена (CCScene), слой (CCLayer), спрайт (CCSprite), и т.д. 
Ноды могут включать в себя другие ноды, те - другие ноды могут включать в себя еще ноды и т. д.
Все что видит пользователь - это спрайты, элементы меню, частиицы и т. д., всё это добавлено на слои, а слои, находятся на сцене. Сам по себе слой — CCLayer прозрачен, но есть класс CCLayerColor — этот слой может быть произвольного цвета. Слои могут быть произвольного размера, к примеру в моих играх слои 30000x2000 пикс. Вот, соответственно, в такой прямоугольник мы и добавляем нужные ноды. На слоях могут находиться другие слои и т. д., вложенность может быть произвольной и зависеть от логики приложения. То есть получается древовидная структура. Как-то так... Надеюсь это понятно.
Я набросал схему включения нодов. Посмотрите.


 Уменьшено на 67%
Прикрепленное изображение
1200 x 805 (89.07 КБ)
 

(CCScene) Сцена — главное действующее «лицо» во время игрового процесса. Сцена показывает слои и обрабатывает тапы на дисплее. 
Как я и говорил раньше, в классе AppDelegate в методе applicationDidFinishLaunching() создается начальная сцена нашего приложения. Например заставка в игре, с нее всё начинается. 
Но мы сейчас рассматриваем проект Hello World, посмотрите в код метода applicationDidFinishLaunching()

Код
// create a scene. it's an autorelease object
    CCScene *pScene = HelloWorld::scene();
    pDirector->runWithScene(pScene);


Вот эта строчка создает сцену 
Код
CCScene *pScene = HelloWorld::scene();


А дальше отдаем созданную сцену директору для запуска
Код
pDirector->runWithScene(pScene);


Самое время посмотреть на код HelloWorld.h
Код
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"

class HelloWorld : public cocos2d::CCLayer
{
public:
    // Method 'init' in cocos2d-x returns bool, instead of 'id' in cocos2d-iphone (an object pointer)
    virtual bool init();
    // there's no 'id' in cpp, so we recommend to return the class instance pointer
    static cocos2d::CCScene* scene();
    // a selector callback
    void menuCloseCallback(CCObject* pSender);
    // preprocessor macro for "static create()" constructor ( node() deprecated )
    CREATE_FUNC(HelloWorld);
};
#endif // __HELLOWORLD_SCENE_H__


Что мы здесь видим ? Видит кто нибудь ? )))
Класс HelloWorld — наследник CCLayer, это слой, на который будут добавлены, к примеру спрайты, бувкы, и т. д. 
У этого класса есть статический метод scene(), я писал о таких методах в первой статье. В этом статическом методе будет создана сцена, слой, а затем созданный слой добавлен на сцену.
Смотрим код:

Код
CCScene* HelloWorld::scene()
{
    CCScene *scene = CCScene::create();
    HelloWorld *layer = HelloWorld::create();
    scene->addChild(layer);
    return scene;
}

Надеюсь код Вам понятен. Для добавления нода к нодам есть метод addChild, пример:
Код
pFatherNode->addChild(pChildNode);

также существует метод addChild(CCNode*,int z), о нем я расскажу позже. А пока посмотрим код реализации класса:
Код
#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
using namespace cocos2d;
using namespace CocosDenshion;
CCScene* HelloWorld::scene()
{
    // 'сцена' управляемы объект
    CCScene *scene = CCScene::create();
    // 'layer' управляемый объект
    HelloWorld *layer = HelloWorld::create();
    // добавим слой, как дочерний элемент сцены 
    scene->addChild(layer);
    // вернем сцену
    return scene;
}

// в методе  "init" вам необходимо инициализировать ваши данные
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !CCLayer::init() )
    {
        return false;
    }
    
    /////////////////////////////
    // 2. создадим пункт меню для выхода из приложения
    // добавим  кнопу close "CloseNormal.png" — это изображение кнопки в обычном сосотянии
    // "CloseSelected.png" — это изображение кнопки в нажатом сосотянии
    CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
                                        "CloseNormal.png",
                                        "CloseSelected.png",
                                        this,
                                        menu_selector(HelloWorld::menuCloseCallback) 
   );
// установим позицию элемента
    pCloseItem->setPosition( ccp(CCDirector::sharedDirector()->getWinSize().width - 20, 20) );

    // создадим объект меню и добавим в него наш пункт
    CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);
// зададим позицию элемента меню
    pMenu->setPosition( CCPointZero );
// добавим меню на слой ( теперь пункт меню виден и может принимать событие нажатия)
    this->addChild(pMenu, 1);

    // добавим  "HelloWorld" сплэш скрин"
    CCSprite* pSprite = CCSprite::create("HelloWorld.png");

    // расположен посредине
    pSprite->setPosition( ccp(size.width/2, size.height/2) );

    // добавляем на слой под пунктом меню
    this->addChild(pSprite, 0);
    
    return true;
}
// функция обратного вызова, когда сработало нажатие на пункт меню
void HelloWorld::menuCloseCallback(CCObject* pSender)
{
    CCDirector::sharedDirector()->end();

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif
}

В коде есть комментарии, должно быть все понятно. Скажу еще пару слов о координатах, в кокосе координаты объекта — это его середина. А начало координат на слое находится в левом нижнем углу, как нас учили в школе. Т.е. точка с координатами (0,0) находится в левом нижнем углу. Если мы хотим расположить объект посредине экрана, то его координаты будут:
X = ШИРИНА_ЭКРАНА / 2;
Y = ВЫСОТА_ЭКРАНА / 2; 

На первый взгляд странно, правда ? Обычно начало координат в левом верхнем углу и у экрана и у спрайта... Но потом понимаешь, что в кокосе это очень логично сделано и с такими координатами удобнее работать.
Хотя эту точку отсчета можно легко менять для каждого нода, есть метод setAnchorPoint. Но это уже сами почитаете, ничего сложного там нет.
Вернемся к addChild(CCNode*,int z):
z — это Z order, Т.е. с помощью этой переменно мы можем один нод добавить, на слой, поверх другого, указывая меньший Z, или наоборот один нод скрыть под другим нодом указывая больший Z. 
Чтобы было понятно представьте себе слой, на котором будет фон в виде неба и горы, лес, и забор, а на заборе слово... Какое слово... ? 
Правильно, слово == «cocos2d-x», я в Вас не сомневался. Но обойдемся без слова. 
Просто 3 спрайта. Смотрите на рисунок.


 Уменьшено на 78%
Прикрепленное изображение
1742 x 724 (217.07 КБ)
 

Давайте модифицируем код в HeloWorld.h и в секцию public нашего класса, добавим такие строки:
CCSprite* pBack;
CCSprite* pWood;
CCSprite* pFence;
void displayPicture();
Должно быть так:

Код
...
class HelloWorld : public cocos2d::CCLayer
{
public:
    // Method 'init' in cocos2d-x returns bool, instead of 'id' in cocos2d-iphone (an object pointer)
    virtual bool init();

    // there's no 'id' in cpp, so we recommend to return the class instance pointer
    static cocos2d::CCScene* scene();
    
    // a selector callback
    void menuCloseCallback(CCObject* pSender);
    
    CCSprite* pBack;
    CCSprite* pWood;
    CCSprite* pFence;
    
    void displayPicture();

    // preprocessor macro for "static create()" constructor ( node() deprecated )
    CREATE_FUNC(HelloWorld);
};

А теперь в классе реализации до метода bool HelloWorld::init()
добавим реализацию метода displayPicture()

Код
void HelloWorld::displayPicture()
{
    CCSize winSize = CCDirector::sharedDirector()->getWinSize();
    
        pBack = CCSprite::create("back.png");
    pWood = CCSprite::create("wood.png");
    pFence = CCSprite::create("fence.png");
    
        pBack->setPosition(ccp(winSize.width/2,winSize.height/2));
    pWood->setPosition(ccp(winSize.width/2,pWood->getContentSize().height/2));
    pFence->setPosition(ccp(winSize.width/2,pFence->getContentSize().height/2));

    addChild(pBack,0);
    addChild(pWood,1);
    addChild(pFence,2);
}

Теперь в методе init закомментим все строки, начиная с // 3. add your codes below...
кроме последней return true;



И после 
Код
if ( !CCLayer::init() )
{
  return false;
}

добавим вызов displayPicture()
Должно получиться так:

Код
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !CCLayer::init() )
    {
        return false;
    }
    displayPicture();
    
    /////////////////////////////
    // 2. add a menu item with "X" image, which is clicked to quit the program
    //    you may modify it.

    // add a "close" icon to exit the progress. it's an autorelease object
    CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
                                        "CloseNormal.png",
                                        "CloseSelected.png",
                                        this,
                                        menu_selector(HelloWorld::menuCloseCallback) );
    pCloseItem->setPosition( ccp(CCDirector::sharedDirector()->getWinSize().width - 20, 20) );

    // create menu, it's an autorelease object
    CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);
    pMenu->setPosition( CCPointZero );
    this->addChild(pMenu, 1);

//    /////////////////////////////
//    // 3. add your codes below...
//
//    // add a label shows "Hello World"
//    // create and initialize a label
//    CCLabelTTF* pLabel = CCLabelTTF::create("Hello World", "Thonburi", 34);
//
//    // ask director the window size
//    CCSize size = CCDirector::sharedDirector()->getWinSize();
//
//    // position the label on the center of the screen
//    pLabel->setPosition( ccp(size.width / 2, size.height - 20) );
//
//    // add the label as a child to this layer
//    this->addChild(pLabel, 1);
//
//    // add "HelloWorld" splash screen"
//    CCSprite* pSprite = CCSprite::create("HelloWorld.png");
//
//    // position the sprite on the center of the screen
//    pSprite->setPosition( ccp(size.width/2, size.height/2) );
//
//    // add the sprite as a child to this layer
//    this->addChild(pSprite, 0);
    
    return true;
}

Запускаем и видим:

Прикрепленное изображение

Посмотрите код метода displayPicture() и Рисунок 2 и результат на скриншоте. 
Думаю с Z ордером вопросов не возникнет.
На этом пока все, работы много. Если хотите выложу файлы с картинками, но думаю и без них все понятно.
Comments