Only One Man can DonСтатьи

Урок 1: Ядро движка Nutemg::Core::Engine

Автор:

Здравствуйте :)

В первом уроке я хотел бы познакомить вас c функциями, обеспечивающими
базовую функциональность движка.

Ядро (Core)
Основой любого приложения на движке Nutmeg является класс Engine, реализующий
инициализацию  окна приложения, обработку ввода а так же предоставляет пользователю
переопределять  системные события  движка, для чего необходимо наследовать класс,
в котором будет вызываться игровая логика от класса Engine и переопределить
необходимые виртуальные функции событий:

class Game : public Engine {

public:
  
  //--------------------------------------------------------------------------
  // Событие инициализации приложения
  void onStartup() { }
  
  //--------------------------------------------------------------------------
  // Событие завершения приложения
  void onShutdown() { }

  //--------------------------------------------------------------------------
  // Событие отрисовки окна приложения
  void onRender() { }
  
  //--------------------------------------------------------------------------
  // Событие, в котором необходимо исполнять логику приложения
  // dt -  время, прошедшее с момента предыдущего вызова функции onUpdate
  //     может быть равно времени предыдущего кадра, либо фиксированной 
  //    заданной величине, в зависимости от заданных параметров.
  void onUpdate(float dt) { }
  
  //--------------------------------------------------------------------------
  // Событие изменения размеров окна приложения
  // width -  новая ширина окна приложения в пикселах
  // height -  новая высота окна приложения в пикселах
  void onResize(int width, int height) { }

  //--------------------------------------------------------------------------
  // Событие сворачивания/разворачивания окна приложения
  // active -  флаг активации приложения 
  //       true - приложение получает фокус
  //      false - приложение теряет фокус
  void onActivate(bool active) { }

  //--------------------------------------------------------------------------
  // Событие нажатия на клавишу
  // keу - сканкод нажатой клавиши
  void onKeyDown(int key) { }
  
  //--------------------------------------------------------------------------
  // Событие повторения клавиши. Срабатывает с периодом, равным периоду 
  // повторения вводимого символа, пока нажата клавиша.
  // keу - сканкод клавиши
  void onKeyRepeat(int key) { }
  
  //--------------------------------------------------------------------------
  // Событие отпускания клавиши
  // keу - сканкод отпущенной клавиши
  void onKeyUp(int) { }   
  
  //--------------------------------------------------------------------------
  // Событие генерации символа. Срабатывает с периодом, равным периоду 
  // повторения вводимого символа, пока нажата клавиша.
  // c - ASCII код символа
  void onKeyChar(char) { }

  //--------------------------------------------------------------------------
  // Событие нажатия кнопки мыши.
  // x -  координата курсора мыши по горизонатльной оси относительно 
  //    левой границы окна приложения
  // y -  координата курсора мыши по вертикальной оси относительно 
  //    верхней границы окна приложения
  // b -  код нажатой кнопки.
  void onMouseDown(int x, int y, int b) { }
  
  //--------------------------------------------------------------------------
  // Событие отпускания кнопки мыши.
  // x -  координата курсора мыши по горизонатльной оси относительно 
  //    левой границы окна приложения
  // y -  координата курсора мыши по вертикальной оси относительно 
  //    верхней границы окна приложения
  // b -  код отпущенной кнопки.
  void onMouseUp(int x, int y, int b) { }
  
  //--------------------------------------------------------------------------
  // Событие перемещения курсора мыши. Возникает каждый раз, когда координаты 
  // курсора мыши изменяются.
  // x -  координата курсора мыши по горизонатльной оси относительно 
  //    левой границы окна приложения
  // y -  координата курсора мыши по вертикальной оси относительно 
  //    верхней границы окна приложения
  void onMouseMove(int x, int y) { }

};

Переопределение событий необязательно, можно переопределить только необходимые события.
Далее идет краткое описание всех функций класса Engine:

class Engine {

public:

  //--------------------------------------------------------------------------
  // главная функция
  //--------------------------------------------------------------------------
  
  virtual void main();

  //--------------------------------------------------------------------------
  // функции управления временем и информация о производительности
  //--------------------------------------------------------------------------

  // получить значение счетчика колличества отработанных кадров приложения 
  // за секунду (Frames per Second) (усредненное)
  virtual float getFPS() const;

  // очистить таймер значения dt, которое передается 
  // в событие onUpdate (это необохдимо после единичного
  // вызова какой-либо тяжелой функции во избежание
  // передачи в событие слишком большого значнения dt
  // например, после вызова функции загрузки нового уровня)
  virtual void clearTimer() const;

  // получить время, затраченное на работу события onRender (усредненное)
  virtual float getRenderTime() const; 
  
  // получить время, затраченное на работу события onUpdate (усредненное)
  virtual float getUpdateTime() const;
  
  // получить время, затраченное на отрисовку кадра (усредненное)
  virtual float getFrameTime() const;

  // получить частоту вызова события onUpdate 
  // возвращаемое значение:
  // = 0.0f -  каждый кадр, 
  // > 0.0f -  колличество вызовов события onUpdate в секунду
  virtual float getUpdateFPS();
  
  //--------------------------------------------------------------------------
  // установить частоту вызова события onUpdate 
  // это значение необходимо устанавливать, 
  // если теребуется изменить способ синхронизации
  // времени в приложении. Синхронизацию нужно производить
  // для того, чтобы приложение работало с одинаковой скоростью
  // вне зависимости от FPS. Существует два режима синхронизации
  // *  переменный шаг (updteFPS = 0.0f). 
  //    При данном методе синхронизации для каждой изменяющейся во 
  //    времени величины необходимо производить ее интегрирование. 
  //    Например, если нужно, чтобы тело двигалось со скоростью 5 м/с, 
  //    нужно в каждом вызове события onUpdate прибавлять к координате
  //    тела значение 5.0f * dt, т.е. производить интегрирование.
  //    этот метод обеспечивает хорошую плавность, но минусом является
  //    то, что функция интегрирования не всегда тривиальна и одиночные
  //    события происходящие в определенный момент времени для разных
  //    значений счетчика fps могут выполниться в разных по счету вызовах
  //    события onUpdate от начала симуляции, т.е. будет небольшое различие, 
  //    в большенстве случаев оно допустимо :)
  //    
  // *  фиксированный шаг (updateFPS > 0.0f)
  //    Этот метод обеспечивает точное повторение результата для разных 
  //    симуляций, вне зависимости от значения счетчика fps.
  //    Минусом является фиксированная плавность (оптимальное значение >=50fps)
  //    Т.е. событие onUpdate будет вызваться 50 раз в секунуду, даже если
  //    реальный фпс больше этого значения.
  //
  virtual void setUpdateFPS(float updateFPS);


  //--------------------------------------------------------------------------
  // функции управления видеорежимами
  //--------------------------------------------------------------------------

  // получить коллличество актуальных видеорежимов, которые поддержвиются
  // монитором, видеоадаптером и движком.
  int getVideoModesCount() const;
  
  // получить информацию о видеорежиме по его индексу
  VideoMode getVideoMode(int) const;

  // получить информацию о текущем уставноленном видеорежиме
  VideoMode getCurrentVideoMode() const;
  
  // определить режим работы приложения
  // возвращаемое значение:
  // true -  полноэкранный
  // false -  оконный
  virtual bool isFullscreen() const;

  // установить текущий видеорежим
  // videomode -  видеорежим для установки
  // fullscreen -  полноэкранный/оконный
  virtual bool setVideoMode(const VideoMode &videomode, bool fullsceen);

  // получить ширину буффера кадра приложения в пикселах
  virtual int getWidth() const;
  
  // получить высоту буффера кадра приложения в пикселах
  virtual int getHeight() const;
  
  // получить соотношение сторон монитора
  virtual float getAspectRatio() const;

  //--------------------------------------------------------------------------
  // функции по работе с устройствами ввода
  //--------------------------------------------------------------------------

  // получить название клавиши по сканкоду
  virtual const char *getKeyName(int) const;
  
  // получить сканкод клавиши по названию
  virtual int getKeyByName(const char *name) const;
  
  // получить состояние (нажата/отпущена) клавиши 
  // key - сканкод клавиши
  virtual bool keyHold(int key) const;

  // захватить/освободить положение указателя мыши. В захваченном состоянии 
  // работают функции получения смещения мыши (getMouseDeltaX/getMouseDeltaY)
  virtual void setGrab(bool);
  virtual bool isGrab() const;

  // показать/спрятать курсор мыши
  virtual void setShowCursor(bool show);
  virtual bool getShowCursor() const;

  // получить оконные координаты мыши
  virtual int getMouseX() const;
  virtual int getMouseY() const;
  virtual int getMouseDeltaX() const;
  virtual int getMouseDeltaY() const;
  virtual int getMouseDeltaZ() const;
  
  // получить состояние (нажата/отпущена) кнопки мыши
  // button -  код кнопки мыши
  virtual bool buttonHold(int button) const;

  //--------------------------------------------------------------------------
  // окно приложения
  //--------------------------------------------------------------------------

  // установить заголовок окна приложения
  virtual const char *getTitle() const;
  virtual void setTitle(const char *);

  // вывести сообщение (MessageBox)
  virtual void message(const char *, const char *) const;

  //--------------------------------------------------------------------------
  // буфер обмена
  //--------------------------------------------------------------------------

  // получить/установить строку буффера обмена
  virtual const char *getClipboard() const;
  virtual void setClipboard(const char *) const;

  //--------------------------------------------------------------------------
  // общее
  //--------------------------------------------------------------------------

  // завершить работу приложения
  virtual void halt();
  
  // проверить активно ли приложение движка
  virtual bool isActive();
  
  // поменять буферы местами (двойная буфферизация). 
  // Класс Engine сам делает это после вызова события
  // onRender, но эту функцию нужно вызывать после вызова события onRender из 
  // события изменения размера окна (onResize) чтобы обеспечить отображение
  // корректного изображения во время изменения размера окна приложения.
  void swap();

  //--------------------------------------------------------------------------

}

В этом уроке хотелось бы рассказать еще об одной полезной функции - функции вывода
двумерного текста на экран:

// Вывод текста на экран. Функция должна вызываться после вызова renderer.set2d();
// Использование произвольного числа параметров аналогично функции printf().
// x, y - координаты левого верхнего угла текста относительно левого верхнего угла
// экрана.
void Renderer::text(float x, float y, const char *fmt, ...);

// Пример:

//--------------------------------------------------------------------------
// Событие отрисовки окна приложения
void onRender() {
  
  renderer.begin();
  renderer.clear();
  renderer.set2d();
  renderer.text(10, 10, "framerate: %.2f", getFPS());
  renderer.set3d();
  renderer.end();

}

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

nutmeg_lesson01 | Урок 1: Ядро движка Nutemg::Core::Engine

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

Пример к уроку:
http://disk.tom.ru/6k4u1v7

C Уважением, Алексей Егоров aka EvilSpirit

#engine, #nutmeg

29 августа 2009

Комментарии [8]