Урок 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( ); }
Далее с использованием полученной информации о функциях движка можем
создать следующий демонстрационный пример, который будет рисовать много
движущихся надписей с названием функций, которые были сегодня изучены.
А так же для разнообразия создадим одну большую надпись, которую можно
будет двигать, нажимая клавиши.