суботу, 14 жовтня 2017 р.

Розробка фреймворка для 2-Д відеоігор на основі мультимедійної бібліотеки SDL2 Частина 3. Вдосконалюємо роботу з вікном.


Розробка фреймворка для 2-Д відеоігор на основі мультимедійної бібліотеки SDL2

Частина 3. Вдосконалюємо роботу з вікном.

Оптимізація класу

В попередній частині ми створили каркасний клас для фреймворка, який створює вікно засобами SDL2. Проте для комфортної роботи цей клас потрібно вдосконалити, використовуючи можливості бібліотеки SDL2 для роботи з вікнами.
В останньому варіанті коду я допустив помилку: код, який відповідає за ініціалізацію, виніс в конструктор класу, а потрібно було його описати в методі Initialize. В новому варіанті класу ми цю помилку виправимо, а також розширимо можливості створення та керування вікном.
Щоб не заплутуватись в лістингах, надалі я не буду приводити код повністю після кожної зміни, а лише сніпети коду, який додається або змінюється
І ще одна особливість, яку я виявив при виведенні заголовку вікна: для того, щоб коректно відображалась кирилиця, потрібно використовувати тип UTF8String, який приводиться до типу PChar. Але для того, щоб такий метод відпрацьовував коректно, заголовок вікна винесемо окремою властивісю класу:

//заголовок вікна
    property Caption : String;
    

Натискуй Ctrl+Shift+C і Lazarus сам створить весь додатковий код, тобі лише потрібно зробити виправлення в методі Init(): рядок

fSDLWindow:=SDL_CreateWindow(PChar(аCaption),SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,_height,_width,SDL_WINDOW_SHOWN);

потрібно замінити на наступні рядки:

fSDLWindow:=SDL_CreateWindow(PChar(Utf8String(Caption)),
                  SDL_WINDOWPOS_UNDEFINED,
                  SDL_WINDOWPOS_UNDEFINED,
                  Height,Width,
                  SDL_WINDOW_SHOWN);
   

Тепер значення заголовку буде зберігатися у відповідній властивості класу і коректно відображатись на екрані.
Аналогічно винесемо розміри вікна у окремі властивості класу Width і Height:

//ширина і висота вікна
    property Width : Integer;
    property Height : Integer;
 
Теж натискуй Ctrl+Shift+C щоб Lazarus автоматично завершив код і поки що ці методи лишай без змін, до них ми повернемось трохи пізніше.
Ще, на мою думку, зручно було б винести обробку робочого циклу теж окремим методом (аналогічно як в класі Application в LCL). Тож я вирішив додати метод Run:

procedure TGOEngine.Run;
begin
 while IsRunning do begin
    DoEvents;
    Render;
   end;
end;


Відповідно в тестовій програмі весь цикл while потрібно замінити лише одним рядком TestEngine.Run. Можеш спробувати запустити програму і переконатись, що все працює так як і раніше.

Повноекранний режим SDL

Для створення вікна бібліотека SDL використовує певний набір параметрів. Це ширина і висота вікна, початкові координати верхнього лівого кута вікна, а також набір різних додаткових ознак. Якщо звернутись до документації по SDL, то можна використовувати наступні ознаки:
Ознака Призначення
SDL_WINDOW_FULLSCREEN Вікно повинне бути на весь екран
SDL_WINDOW_OPENGL Вікно може використовуватись як контекст OpenGL
SDL_WINDOW_SHOWN Вікно видиме на екрані
SDL_WINDOW_HIDDEN Вікно сховане
SDL_WINDOW_BORDERLESS Вікно не має обрамлення і заголовку
SDL_WINDOW_RESIZABLE Вікно може змінювати розміри
SDL_WINDOW_MINIMIZED Вікно згорнуте
SDL_WINDOW_MAXIMIZED Вікно розгорнуте
SDL_WINDOW_INPUT_GRABBED Вікно захопило фокус вводу
SDL_WINDOW_INPUT_FOCUS Вікно має фокус вводу
SDL_WINDOW_MOUSE_FOCUS Вікно має фокус вказівника мишки
SDL_WINDOW_FOREIGN Вікно створене не бібліотекою SDL
Оскільки більшість відеоігор зручніше грати в повноекранному режимі, нас цікавить сама перша ознака SDL_WINDOW_FULLSCREEN. Спробуй змінити ознаку в функції створення вікна в методі Init() нашого класу і подивись результат. Щоб закрити вікно в повноекранному режимі, натисни Alt+F4, оскільки заголовок зі стандартними кнопками відсутній.
Проте періодично виникають ситуації, коли потрібно швидко переключитись між повноекранним/віконним режимами без виходу з програми. SDL дозволяє таке зробити за допомогою функції SDL_SetWindowFullscreen(), яка в якості параметрів приймає вікно, з яким відбувається дія, та ознаку 0 для віконного режиму або 1 для повноекранного.
Отже, для встановлення ознаки повноекранного режиму я ввів ще одну властивість:

property IsFullScreen : Boolean default true;

після чого натиснув вже добре знайому комбінацію Ctrl+Shift+C для автоматичного завершення коду. Далі, переходимо в згенерований Лазарусом внутрішній метод SetIsFullScreen() і в кінці додаємо наступні рядки коду:

if FIsFullScreen then SDL_SetWindowFullscreen(fSDLWindow,SDL_WINDOW_FULLSCREEN)
    else SDL_SetWindowFullscreen(fSDLWindow,0);
  SDL_UpdateWindowSurface(fSDLWindow);


Цей код реалізує перехід в повноекранний або віконний режим в залежності від поточного значення поля FIsFullScreen. Останній рядок необхідний для того, щоб вікно обновилось після зміни режиму.
Добре, код для реалізації зміни режиму вже є, тепер потрібно назначити коли цей код повинен виконуватись. В більшості програм для переключення між віконним та повноекранним режимами прийнято використовувати комбінацію клавіш Alt+Enter. Я вирішив теж реалізувати переключення між режимами цією комбінацією клавіш, і мене переклинило більше ніж на годину часу. Справа в тому, що потрібно відловити натискання комбінації клавіш, а за один раз вдається відловити лише натискання однієї клавіші. Звісно, є ще можливість перевірити які з клавіш-модифікаторів були в цей час натиснуті, але конкретного прикладу як це правильно зробити я знайти так і не зміг (навіть для C++!), тож довелось практичним шляхом виясняти самому.
Спочатку потрібно відловити подію натискання клавіш. Бібліотека SDL для цього має два типа подій, які виникають перша при натисканні клавіші, а друга при відпусканні. В нашому випадку краще підходить подія відпускання клавіші. Переходь в метод DoEvents() та після рядка

if fSDLEvent^.type_ = SDL_QUITEV then fRunning:=false;
      

додай наступний код:

if (fSDLEvent^.type_ = SDL_KEYUP) then begin
         //відловлюємо Alt+Enter для зміни режиму
         if (fSDLEvent^.key.keysym.sym = SDLK_RETURN) and ((fSDLEvent^.key.keysym._mod and KMOD_ALT) <> 0) then
            IsFullScreen:=not IsFullScreen;
      end

Спробуй запустити та потестити - повинно все працювати саме так, як і задумано. Якщо щось не виходить чи не можеш зрозуміти що куди в коді потрібно прописати, можеш скачати архів з сирцями (лише у властивостях проекту потрібно поправити шляхи до трансльованих заголовочних файлів SDL2, про що я писав ще в першій статті). В наступній частині ми завершимо рутинну роботу з вікном і поступово почнемо переходити до виведення зображень. А на сьогодні поки що все. Всі зауваження та побажання до статті пишіть в коментарях.

Немає коментарів:

Дописати коментар