IrrlichtСтатьи

Перевод Example 004 Movement

Автор:

Ну что я могу здесь вам сказать?
Следующим делом планирую перевести урок Example 012 Terrain Rendering... если звёзды нашлют вдохновение. В этом уроке есть 2 замечательные фишки: как ставить Скибокс и Скидоме и как делать территорию для игры по карте высот(heightmap). Заодно расскажу где брать скибоксы и с помощью чего генерировать свою карту высот для уровня и для последующего встраивания в Иррлихт.

Ну а пока:

/** Example 004 Movement(Пример 004 Движение)

Этот туториал покажет как передвигать и анимировать SceneNodes. 
Покажет основную концепцию SceneNodeAnimators(аниматоров узлов 
сцены) и то как передвигать узлы сцены используя клавиатуру.  
Мы продемонстрируем расчёт частоты кадров в главном цикле Иррлихта
и как с его помощью сделать независимое движение, 
что означает настройку зависимости от того как давно был запущен
основной цикл иррлихта последний раз.(т.е. расчёт фактора deltaTime)
(http://en.wikipedia.org/wiki/Delta_time)

А пример 19.MouseAndJoystick покажет как настроить управление
с помощью этих устройств.

Как всегда, Я извлекаю заголовки, включаю пространство имён irr,
и связывают проект с .lib файлом.
*/
#ifdef _MSC_VER
//Мы так же определяем этот дефайн, чтобы MSVC 
//не жаловался на sprintf().
#define _CRT_SECURE_NO_WARNINGS
#pragma comment(lib, "Irrlicht.lib")
#endif

#include <irrlicht.h>
#include "driverChoice.h"

using namespace irr;

/*
Чтобы получить событие от мыши или клавиатуры, или событие GUI, 
такие как "OK кнопка была нажата", нам необходим объект:
irr::IEventReceiver object. 
Существует только один метод класса, виртуальный и переопределяемый в:
irr::IEventReceiver::OnEvent(). Реагирующий на возникновение событий.
Ведь мы действительно должны знать - является ли кнопка в данный 
момент нажатой, и как мы будем проверять текущее состояние 
всех клавиш.
*/
class MyEventReceiver : public IEventReceiver
{
public:
  //Это тот самый единственный метод, который получает события
  //и который необходимо переопределить
  virtual bool OnEvent(const SEvent& event)
  {
    //Если произошло событие, затрагивающее 
    //клавиатуру, записать по индексу клавиши
    //в массиве true/false = зажата/нет
    if (event.EventType == irr::EET_KEY_INPUT_EVENT)
      KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;

    return false;
  }

  //Эта ф-ция возвращает булёвый массив с текущими
  //значениями всех зажатых или отпущенных клавиш
  virtual bool IsKeyDown(EKEY_CODE keyCode) const
  {
    return KeyIsDown[keyCode];
  }
  
  MyEventReceiver()
  {
    for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
      KeyIsDown[i] = false;
  }

private:
  // Мы используем этот массив для хранения текущего значения каждой клавиши
  bool KeyIsDown[KEY_KEY_CODES_COUNT];
};


/*
Получальщик событий для получения и сохранения нажатых клавиш готов, 
фактически нажатия будут доступны внутри цикла рендеринга, ещё перед 
отрисовкой сцены. Итак, давайте просто создадим:
irr::IrrlichtDevice и узел сцены, который мы хотим передвигать. Мы так же
создадим ряд дополнительных узлов сцены, чтобы показать какие 
существуют возможности для передвижения и анимации узлов сцены.
*/
int main()
{
  //Спросить пользователя про драйвер
  video::E_DRIVER_TYPE driverType=driverChoiceConsole();
  if (driverType==video::EDT_COUNT)
    return 1;

  //Создать устройство
  MyEventReceiver receiver;

  IrrlichtDevice* device = createDevice(driverType,
      core::dimension2d<u32>(640, 480), 16, false, false, false, &receiver);

  if (device == 0)
    return 1; // устройство не удалось создать с таким драйвером.

  video::IVideoDriver* driver = device->getVideoDriver();
  scene::ISceneManager* smgr = device->getSceneManager();

  /*
  Создаём узел, который может быть перемещён нажатием кнопок 
  'W','S','A','D'. Мы создадим узел-сферу, которая является 
  геометрическим примитивом. Мы установим узел в координаты
  (0,0,30) и назначим ему текстуру пусть он смотрится более интересно
  Так как мы не имеем динамического освещения в данной сцене, мы
  отключаем освещение для каждой модели(В противном случае 
  модель будет отображаться чёрной).
  */
  scene::ISceneNode * node = smgr->addSphereSceneNode();
  if (node)
  {
    node->setPosition(core::vector3df(0,0,30));
    node->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
    node->setMaterialFlag(video::EMF_LIGHTING, false);
  }

  /*
  Теперь мы создаём другой узел, движимый с помощью АНИМАТОРА узлов 
  сцены. Аниматоры узлов сцены динамически изменяют узлы сцены и могут
  быть присоединены к любому узлу сцены как, например, к сетке
  узла сцены, биллбоардам(картинкам всегда повёрнутым к пользователю), 
  к источникам свети и даже к узлу камеры сцены.
  Аниматор узла сцены не только в состоянии изменять положение
  узла сцены, они так же могут анимировать, например, текстуры объекта!
  Мы создадим куб-узел сцены и присоединим к нему аниматор по названию:
  'fly circle'(летать по кругу), заставляя этот узел летать вокруг узла нашей 
  сферы.
  */
  scene::ISceneNode* n = smgr->addCubeSceneNode();

  if (n)
  {
    n->setMaterialTexture(0, driver->getTexture("../../media/t351sml.jpg"));
    n->setMaterialFlag(video::EMF_LIGHTING, false);
    scene::ISceneNodeAnimator* anim =
      smgr->createFlyCircleAnimator(core::vector3df(0,0,30), 20.0f);
    if (anim)
    {
      n->addAnimator(anim);
      anim->drop();
    }
  }

  /*
  Последний узел сцены мы добавляем, чтобы посмотреть возможности 
  анимирования b3d модели в узле сцены, которая использует аниматор 
  'полёт прямо' для движения между точками.
  */
  scene::IAnimatedMeshSceneNode* anms =
    smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/ninja.b3d"));

  if (anms)
  {
    scene::ISceneNodeAnimator* anim =
      smgr->createFlyStraightAnimator(core::vector3df(100,0,60),
      core::vector3df(-100,0,60), 3500, true);
    if (anim)
    {
      anms->addAnimator(anim);
      anim->drop();
    }

    /*
    Для того, чтобы модель была видна мы отключаем освещение,
    устанавливаем фрейм(кадр) между которыми анимация должна 
    проигрывать свой цикл, поворачиваем модель на 180 градусов, и 
    настраиваем скорость анимации и текстуры. Чтобы правильно 
    установить анимацию (кадры(фреймы) и скорость), мы просто 
    можем вызывать:
    "anms->setMD2Animation(scene::EMAT_RUN)" для 'запуска'
    анимации, а не "setFrameLoop" и "setAnimationSpeed",
    ведь это работает только с MD2 анимациями. Но теперь вы знаете как
    работать и с другими видами анимации. И хороший совет - НЕ 
    используйте жёсткое задание номеров фреймов(frame-numbers)
    в коде...
    */
    anms->setMaterialFlag(video::EMF_LIGHTING, false);

    anms->setFrameLoop(0, 13);
    anms->setAnimationSpeed(15);
//    anms->setMD2Animation(scene::EMAT_RUN);

    anms->setScale(core::vector3df(2.f,2.f,2.f));
    anms->setRotation(core::vector3df(0,-90,0));
//    anms->setMaterialTexture(0, driver->getTexture("../../media/sydney.bmp"));

  }


  /*
  Чтобы иметь возможность видеть и перемещаться по сцене, мы 
  создаём камеру в стиле FPS(first person shooter) и делаем 
  курсор мыши невидимым.
  */
  smgr->addCameraSceneNodeFPS();
  device->getCursorControl()->setVisible(false);

  /*
  Добавим красочный логотип Иррлихта
  */
  device->getGUIEnvironment()->addImage(
    driver->getTexture("../../media/irrlichtlogoalpha2.tga"),
    core::position2d<s32>(10,20));

  gui::IGUIStaticText* diagnostics = device->getGUIEnvironment()->addStaticText(
    L"", core::rect<s32>(10, 10, 400, 20));
  diagnostics->setOverrideColor(video::SColor(255, 255, 255, 0));

  /*
  Мы сделали всё необходимое для отрисовки. Мы так же напишем
  кол-во кадров в секунду(frames per second,FPS)и имя драйвера
  в заголовке окна.
  */
  int lastFPS = -1;

  //Для того, чтобы сделать частоту кадров независимой от 
  //движения(т.е. чтобы сделать игровой процесс независимым от
  //быстродействия железа), мы должны знать, как давно 
  //сменился последний фрейм(высчитать фактор 
  //дельтатайм = deltaTime, разность во времени между 
  //текущим кадром рисуемым движком и предыдущим)
  u32 then = device->getTimer()->getTime();

  //Это скорость передвижения в секунду.
  const f32 MOVEMENT_SPEED = 5.f;

  while(device->run())
  {
    //Рассчёт DeltaTime.
    const u32 now = device->getTimer()->getTime();
    const f32 frameDeltaTime = (f32)(now - then) / 1000.f; // Время в секундах
    then = now;

    /* Проверить нажата ли W, S, A или D и переместить
    узел сферы сообразно этому. */
    core::vector3df nodePosition = node->getPosition();

    if(receiver.IsKeyDown(irr::KEY_KEY_W))
      nodePosition.Y += MOVEMENT_SPEED * frameDeltaTime;
    else if(receiver.IsKeyDown(irr::KEY_KEY_S))
      nodePosition.Y -= MOVEMENT_SPEED * frameDeltaTime;

    if(receiver.IsKeyDown(irr::KEY_KEY_A))
      nodePosition.X -= MOVEMENT_SPEED * frameDeltaTime;
    else if(receiver.IsKeyDown(irr::KEY_KEY_D))
      nodePosition.X += MOVEMENT_SPEED * frameDeltaTime;

    node->setPosition(nodePosition);

    driver->beginScene(true, true, video::SColor(255,113,113,133));

    smgr->drawAll(); // нарисовать 3d сцену
    device->getGUIEnvironment()->drawAll(); // нарисовать GUI (логотип движка)

    driver->endScene();

    int fps = driver->getFPS();

    if (lastFPS != fps)
    {
      core::stringw tmp(L"Movement Example - Irrlicht Engine [");
      tmp += driver->getName();
      tmp += L"] fps: ";
      tmp += fps;

      device->setWindowCaption(tmp.c_str());
      lastFPS = fps;
    }
  }

  /*
  Под конец, освободить память занятую устройством Иррлихта.
  */
  device->drop();
  
  return 0;
}

/*
Вот и всё. Откомпилируйте и поиграйте с программой.
**/

12 августа 2010