Як додаток до відповіді Journeyman Geek (через те, що мою редакцію відхилено) для людей, які цікавляться кодуючою частиною / перспективою розробника:
З точки зору програмістів, для тих, хто цікавиться, часи DOS були періодами, коли кожна галочка процесора була важливою, тому програмісти зберігали код якомога швидше.
Типовий сценарій, коли будь-яка програма буде працювати з максимальною швидкістю процесора, такий простий (псевдо С):
int main()
{
while(true)
{
}
}
це буде працювати назавжди, тепер давайте перетворимо цей фрагмент коду в псевдо-DOS-гру:
int main()
{
bool GameRunning = true;
while(GameRunning)
{
ProcessUserMouseAndKeyboardInput();
ProcessGamePhysics();
DrawGameOnScreen();
//close game
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
}
}
якщо DrawGameOnScreen
функції не використовують подвійну буферизацію / V-синхронізацію (що було досить дорогим у дні, коли були зроблені ігри DOS), гра буде працювати з максимальною швидкістю ЦП. У сучасному мобільному i7 це може працювати приблизно від 1 000 000 до 5 000 000 разів за секунду (залежно від конфігурації ноутбука та поточного використання процесора).
Це означає, що якби я міг отримати будь-яку гру DOS, що працює над моїм сучасним процесором, у моїх 64-бітових вікнах я міг би отримати більше тисячі (1000!) FPS, що занадто швидко для будь-якої людини, якщо фізична обробка "передбачає", що вона працює між 50-60 кадрів в секунду.
Що можуть зробити розробники поточного дня:
- Увімкнути V-синхронізацію в грі (* недоступно для віконних програм ** [він також доступний лише у повноекранних програмах])
- Виміряйте різницю у часі між останнім оновленням та оновленням фізики відповідно до різниці у часі, що ефективно змушує гру / програму працювати з однаковою швидкістю незалежно від швидкості FPS
- Програмуйте обмеження частоти кадрів
*** В залежності від конфігурації графічної карти / драйвера / OS це може бути можливо.
Для пункту 1 немає прикладу, який я покажу, оскільки це насправді не якесь "програмування". Це просто використання графічних функцій.
Щодо пунктів 2 і 3, я покажу відповідні фрагменти коду та пояснення:
2:
int main()
{
bool GameRunning = true;
long long LastTick = GetCurrentTime();
long long TimeDifference;
while(GameRunning)
{
TimeDifference = GetCurrentTime()-LastTick;
LastTick = GetCurrentTime();
//process movement based on how many time passed and which keys are pressed
ProcessUserMouseAndKeyboardInput(TimeDifference);
//pass the time difference to the physics engine so it can calculate anything time-based
ProcessGamePhysics(TimeDifference);
DrawGameOnScreen();
//close game if escape is pressed
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
}
}
Тут ви можете побачити введення даних користувачів та фізику врахувати різницю в часі, але ви все одно можете отримати 1000+ FPS на екрані, оскільки цикл працює як можна швидше. Оскільки двигун фізики знає, скільки часу пройшло, це не повинно залежати від "жодних припущень" чи "певного фреймрейду", тому гра буде працювати з однаковою швидкістю на будь-якому процесорі.
3:
Що розробники можуть зробити, щоб обмежити частоту кадрів, наприклад, 30 FPS - це насправді нічого складніше, просто подивіться:
int main()
{
bool GameRunning = true;
long long LastTick = GetCurrentTime();
long long TimeDifference;
double FPS_WE_WANT = 30;
//how many milliseconds need to pass before we need to draw again so we get the framerate we want?
double TimeToPassBeforeNextDraw = 1000.0/FPS_WE_WANT;
//For the geek programmers: note, this is pseudo code so I don't care for variable types and return types..
double LastDraw = GetCurrentTime();
while(GameRunning)
{
TimeDifference = GetCurrentTime()-LastTick;
LastTick = GetCurrentTime();
//process movement based on how many time passed and which keys are pressed
ProcessUserMouseAndKeyboardInput(TimeDifference);
//pass the time difference to the physics engine so it can calculate anything time-based
ProcessGamePhysics(TimeDifference);
//if certain amount of milliseconds pass...
if(LastTick-LastDraw >= TimeToPassBeforeNextDraw)
{
//draw our game
DrawGameOnScreen();
//and save when we last drawn the game
LastDraw = LastTick;
}
//close game if escape is pressed
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
}
}
Тут відбувається те, що програма рахує скільки мілісекунд пройшло, якщо досягнуто певної кількості (33 мс), то вона перемальовує ігровий екран, ефективно застосовуючи частоту кадрів біля ~ 30.
Також, залежно від розробника, він / вона може вибрати обмеження ВСІХ обробкою до 30 кадрів в секунду, при цьому вищезгаданий код трохи змінений на це:
int main()
{
bool GameRunning = true;
long long LastTick = GetCurrentTime();
long long TimeDifference;
double FPS_WE_WANT = 30;
//how many miliseconds need to pass before we need to draw again so we get the framerate we want?
double TimeToPassBeforeNextDraw = 1000.0/FPS_WE_WANT;
//For the geek programmers: note, this is pseudo code so I don't care for variable types and return types..
double LastDraw = GetCurrentTime();
while(GameRunning)
{
LastTick = GetCurrentTime();
TimeDifference = LastTick-LastDraw;
//if certain amount of miliseconds pass...
if(TimeDifference >= TimeToPassBeforeNextDraw)
{
//process movement based on how many time passed and which keys are pressed
ProcessUserMouseAndKeyboardInput(TimeDifference);
//pass the time difference to the physics engine so it can calculate anything time-based
ProcessGamePhysics(TimeDifference);
//draw our game
DrawGameOnScreen();
//and save when we last drawn the game
LastDraw = LastTick;
//close game if escape is pressed
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
}
}
}
Є кілька інших методів, і деякі з них я справді ненавиджу.
Наприклад, використовуючи sleep(<amount of milliseconds>)
.
Я знаю, що це один метод обмеження частоти кадрів, але що відбувається, коли обробка ігор займає 3 мілісекунди і більше? І тоді ви виконуєте сон ...
це призведе до меншої частоти кадрів, ніж тієї, яка лише sleep()
повинна бути причиною.
Для прикладу візьмемо час сну 16 мс. це призвело б до запуску програми на 60 Гц. тепер обробка даних, введення, малювання та всього іншого займає 5 мілісекунд. зараз ми знаходимося в 21 мілісекундах за один цикл, що призводить до трохи менше 50 Гц, тоді як ви все ще можете бути при 60 Гц, але через сон це неможливо.
Одним з рішень було б зробити адаптивний сон у вигляді вимірювання часу обробки та вирахування часу обробки з потрібного сну, що призведе до виправлення нашої "помилки":
int main()
{
bool GameRunning = true;
long long LastTick = GetCurrentTime();
long long TimeDifference;
long long NeededSleep;
while(GameRunning)
{
TimeDifference = GetCurrentTime()-LastTick;
LastTick = GetCurrentTime();
//process movement based on how many time passed and which keys are pressed
ProcessUserMouseAndKeyboardInput(TimeDifference);
//pass the time difference to the physics engine so it can calculate anything time-based
ProcessGamePhysics(TimeDifference);
//draw our game
DrawGameOnScreen();
//close game if escape is pressed
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
NeededSleep = 33 - (GetCurrentTime()-LastTick);
if(NeededSleep > 0)
{
Sleep(NeededSleep);
}
}
}