среда, 3 августа 2011 г.

Размышления по поводу игровых движков

Давным давно, в далекой галактике Млечный путь, на планете Земля, порождения эволюции или неких Божеств звездного скопления в созвездии Персея , создали технологию имитации реальности и назвали её "игровым движком".

Так исторически сложилось, что первое мое знакомство было отнюдь не на предшествовавшем изобретении велосипеда по ходу написания курсовой, но летом, сейчас то бишь. Движек этот называется и по ныне Irrlicht и считается относительно легким.

Познать дзен пока не удалось. И потому я решил применить метод прокруста обучения путем обучения других. И тут попробую запудрить вам мозг.



Итак. Имеется сколько-то там пространств имен. Нужны они затем чтобы наверное помочь упорядочить классы.


Все что можно найти в Irrlicht движке
Основа - вектора, списки, массивы, плоскости (?) и прочие основы геометрического построения и позиционирования в пространстве
Для графического интерфейса. Кнопочки, менюшки и прочие панельки.
Input-Output. Ввод и вывод в файлы, парсеры XML и прочего.
Тут классы того что можно накидать на сцену. Т.н. Mesh, то есть сетка, если в переводе, короче говоря -  трехмерные модели для вашей игры, которые можно накидать в игру, как декорации на сцену.
Тут всякие фиговины для того чтобы быстро написать копию Кваки на этом движке.
Доступ к драйверу видеокарты, разные способы отображения (по "умному" - рендеринг) трех и двухмерных объектов.



На примере "Хайль Мир" покажу как работает эта штука:



#include < irrlicht.h > - А вы как думали!

using namespace irr; - в принципе и этого и того что пониже можно не писать, так даже в чем-то удобнее - так вы запомните 6 названий, а то и 4-5 и освободите себя от надобности запоминать всякие классы. Написали irr::video:: и оно в Визуал Студии вам выдаст список. 
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif // Без этой фигни будет ошибки выдавать - так что скопируйте и всё. 
Директива #pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup") говорит компоновщику, что приложение оконное. Если бы точкой входа являлась WinMain или wWinMain, то оно бы было оконным по умолчанию.
Второй ключ указывает точку входа. В данном случае это int main().

int main()
{
 IrrlichtDevice *device =
  createDevice( video::EDT_SOFTWARE, dimension2d<u32>(640, 480), 16,
   false, false, false, 0);

// Создали устройство (девайс), которой занимается отображением ваших моделек и прочего. С этого момента можете забыть про низкоуровневую отладку как в OpenGL. Теперь вы условно говоря нажимаете на кнопки. 

 if (!device)
  return 1;

// Если устройство создать не удалось - то и смысла продолжать нет - ведь как можно что-то рисовать без кисти!
 
 guienv->addStaticText(L"Hello World! This is the Irrlicht Software renderer!",
  rect<s32>(10,10,260,22), true);

// Тут создали небольшой элемент интерфейса - текст в левом верхнем углу (почему? А кто его знает - я не интересовался! ). Размеры как видно в скобках.

 IVideoDriver* driver = device->getVideoDriver();
 ISceneManager* smgr = device->getSceneManager();
 IGUIEnvironment* guienv = device->getGUIEnvironment();

// Драйвер управляет отображением, менеджер сцены расставляет ваше все, а GUI делает интерфейс. Вообщем представьте себя в роли режиссера - у вас есть рупор (device), кинопленка (driver), менеджер, который все расставляет на свои места и провдит съемку (scene manager), и можно при просмотре фильма выбрать параметры (guienvironment)

 IAnimatedMesh* mesh = smgr->getMesh("../../media/sydney.md2");

// IAnimatedMesh - класс для хранения меши - вашей модели, уровня с текстурами или без. Походу дела так как анимейтед меш - то можно привязать к этой меши аниматор - штуку, которая будет управлять положением модели или включать анимацию, которую вы написали для модели в редакторе. Так вот: в этой строке загрузили модель Сидни - чувихи из Кваки. У неё, кстати, анимация встроенная. Да и Иррлихт любит кваку, как было видно из того что есть даже пространство имен

 if (!mesh)
 {
  device->drop();
  return 1;
 }
 IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );

// Нет модели - ну так и нечего будет поставить на сцену! Убираем за собой мусор в виде устройства и выходим из игры.
IAnimatedMeshSceneNode - Анимированная "нить" из меша - звучит отвратительно и бредово. Но так получается что меш - это декорация, а нода - элемент сцены, который видно, который может двигаться. 
  
 if (node)
 {
  node->setMaterialFlag(EMF_LIGHTING, false);
  node->setMD2Animation(scene::EMAT_STAND);
  node->setMaterialTexture( 0, driver->getTexture("../../media/sydney.bmp") );
 }

// Без ноды тоже нет резона смотреть что-либо дальше. Если менеджер не смог поставить модель на сцену, то и сцена окажется пустой. А как можно управлять пустотой? Правильно, никак!
Устанавливаем тип отображения для нашей модельки (если честно, констант там целая тонна - нужно экспериментировать чтобы узнать). Дальше говорим, что хотим анимироавать кваковскую моедь - опять таки там констант много. Ну и грузим текстуру для модели, ведь бесцветная модель - это некрасиво.
 
 smgr->addCameraSceneNode(0, vector3df(0,30,-40), vector3df(0,5,0));

// Добавим на сцену камеру, то есть с какой точки будем снимать фильм. Указываем точку и направление.
  
 while(device->run())
 {
  driver->beginScene(true, true, SColor(255,100,101,140));

// начнем съемку

  smgr->drawAll();
//Нарисуем все что было сказано выше. 

  guienv->drawAll();

// Нарисуем интерфейс.

  driver->endScene();
// окончим съемку. Снимаем по одному кадру. 
 }

 
 device->drop();

// Уберем за собой.

 return 0;
}

Пример рассмотрен. Итак - мы режиссеры фильма - мы приказываем компьютеру делать все что захотим на высоком уровне - с помощью функций и объектов.

Такой же пождход я заметил и в других движках. Но в этом преимущество заключается в том, что в него всунули все самое необходимое - минимальную реакцию на столкновения с окружающим миром, анимацию, свет и прочее. Таким образом создать что-то простое несложно.

Я лично привык делать все "через одно место", так как начинал с алгоритмов и написания функций, по-этому логика во многом мне была непонятна. Так же непонятна она мне и сейчас, так как я дико злюсь и многое не понимаю как оно работает. Так что инкапсуляция не всегда идет на пользу. Еще бы документацию получше. На этом пожалуй обзорчик закончу. 

2 комментария:

  1. Директива #pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup") говорит компоновщику, что приложение оконное. Если бы точкой входа являлась WinMain или wWinMain, то оно бы было оконным по умолчанию.
    Второй ключ указывает точку входа. В данном случае это int main().

    ОтветитьУдалить