93 lines
4.7 KiB
C
93 lines
4.7 KiB
C
|
|
#pragma once
|
|||
|
|
// -----------------------------------------------------------------------------
|
|||
|
|
// Приложение Tile2D: окно, кадр и ход времени.
|
|||
|
|
//
|
|||
|
|
// До этого окно, безрамочный режим, фреймбуфер, блиттер и fixed timestep жили
|
|||
|
|
// двумя почти одинаковыми копиями — в игре и в редакторе. Третьему приложению
|
|||
|
|
// пришлось бы написать третью, и она бы уже отличалась: где-то забытый
|
|||
|
|
// `SetExitKey`, где-то другой потолок шагов, где-то апскейл не целым числом.
|
|||
|
|
//
|
|||
|
|
// Здесь НЕ инверсия управления: цикл остаётся у приложения, а App отвечает на
|
|||
|
|
// вопросы «пора ли закрываться», «сколько шагов симуляции прошло» и «куда лёг
|
|||
|
|
// кадр в окне». Колбэки сделали бы порядок «шаг — рендер — интерфейс»
|
|||
|
|
// невидимым, а он у каждого приложения свой и в нём вся суть.
|
|||
|
|
//
|
|||
|
|
// Типичный цикл целиком:
|
|||
|
|
//
|
|||
|
|
// App app;
|
|||
|
|
// app.Init({"Моя игра"});
|
|||
|
|
// while (app.NextFrame())
|
|||
|
|
// {
|
|||
|
|
// for (int i = 0, n = app.Advance(false); i < n; ++i) game.Step(FIXED_DT);
|
|||
|
|
// Render(game, app.fb, app.Alpha());
|
|||
|
|
// app.Present(); // кадр на экран
|
|||
|
|
// DrawUi(app.fit); // поверх кадра, примитивами raylib
|
|||
|
|
// app.Finish();
|
|||
|
|
// }
|
|||
|
|
// app.Shutdown();
|
|||
|
|
// -----------------------------------------------------------------------------
|
|||
|
|
#include "engine/config.h"
|
|||
|
|
#include "engine/framebuffer.h"
|
|||
|
|
|
|||
|
|
struct AppConfig
|
|||
|
|
{
|
|||
|
|
const char* title = "Tile2D";
|
|||
|
|
|
|||
|
|
// Безрамочное на весь монитор — обычный режим. Окно нужно для отладки и
|
|||
|
|
// для съёмки картинок: на полном экране их не сравнить между собой.
|
|||
|
|
bool borderless = true;
|
|||
|
|
bool vsync = true;
|
|||
|
|
|
|||
|
|
// Разрешение кадра. По умолчанию — размер из engine/config.h.
|
|||
|
|
int frameW = INTERNAL_W;
|
|||
|
|
int frameH = INTERNAL_H;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
struct App
|
|||
|
|
{
|
|||
|
|
Framebuffer fb;
|
|||
|
|
ScreenBlitter screen;
|
|||
|
|
|
|||
|
|
// --- состояние текущего кадра ---------------------------------------------
|
|||
|
|
int winW = 0;
|
|||
|
|
int winH = 0;
|
|||
|
|
// Куда лёг кадр в окне: на безрамочном экране он центрирован и вокруг него
|
|||
|
|
// чёрные поля. Интерфейс и оверлей обязаны считать координаты ЧЕРЕЗ это.
|
|||
|
|
ScreenRect fit;
|
|||
|
|
float frameDt = 0.0f;
|
|||
|
|
|
|||
|
|
// Приложение может попроситься наружу само (пункт меню «выход», Esc в
|
|||
|
|
// редакторе). Отдельный флаг, а не возврат из NextFrame: решение о выходе
|
|||
|
|
// принимается в середине кадра, а закрыться надо в его конце.
|
|||
|
|
bool quit = false;
|
|||
|
|
|
|||
|
|
bool Init(const AppConfig& cfg);
|
|||
|
|
void Shutdown();
|
|||
|
|
|
|||
|
|
// Начало кадра: закрытие окна, F11, размеры, ScreenFitRect, dt.
|
|||
|
|
// false — пора выходить.
|
|||
|
|
bool NextFrame();
|
|||
|
|
|
|||
|
|
// Сколько раз выполнить шаг симуляции в этом кадре. Шаг постоянный
|
|||
|
|
// (FIXED_DT), лишнее время копится до следующего кадра, потолок
|
|||
|
|
// MAX_STEPS_PER_FRAME спасает от спирали смерти.
|
|||
|
|
//
|
|||
|
|
// halted — пауза: время НЕ копится. Иначе после меню симуляция догоняла бы
|
|||
|
|
// пропущенные секунды одним рывком.
|
|||
|
|
int Advance(bool halted);
|
|||
|
|
|
|||
|
|
// Доля незакрытого шага, [0..1). По ней интерполируется картинка: рендер
|
|||
|
|
// идёт чаще симуляции, и без неё движение дёргалось бы на 60+ Гц.
|
|||
|
|
float Alpha() const;
|
|||
|
|
|
|||
|
|
void Present(); // BeginDrawing + кадр на экран; дальше можно рисовать UI
|
|||
|
|
void Finish(); // EndDrawing
|
|||
|
|
|
|||
|
|
// Снять кадр в файл. Здесь, а не в приложении, потому что снимать надо
|
|||
|
|
// ПОСЛЕ Finish, и об этом легко забыть.
|
|||
|
|
void Screenshot(const char* file) const;
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
double accumulator = 0.0;
|
|||
|
|
};
|