Я нахожу, что большая часть разработки игр требует основного игрового цикла, но я не знаю, почему это необходимо. Разве мы не могли реализовать слушателя события и ответить на каждое пользовательское действие? Анимации (и т.д.). мог затем играться, когда событие имеет место.
Какова цель основного игрового цикла?
Аргумент, что вам «нужен цикл, потому что в противном случае то, что вызывает прослушиватель событий», не выдерживает критики. По общему признанию, в любой основной ОС такой цикл действительно есть, и слушатели событий работают таким образом, но вполне возможно создать систему, управляемую прерываниями, которая работает без каких-либо циклов.
Но вы все равно не хотели бы строить игру таким образом.
Что делает цикл наиболее привлекательным решением, так это то, что ваш цикл становится тем, что в программировании в реальном времени называется «циклическим исполнителем». Идея состоит в том, что вы можете сделать относительную скорость выполнения различных действий системы детерминированной по отношению друг к другу. Общая скорость цикла может контролироваться таймером, и этот таймер в конечном итоге может быть прерыванием, но в современных ОС вы, вероятно, увидите свидетельство этого прерывания в виде кода, который ожидает семафор (или какой-либо другой механизм синхронизации) как часть вашего «основного цикла».
Так зачем вам детерминированное поведение? Учитывайте относительную скорость обработки входных данных вашего пользователя и злодеев ИИ. Если вы поместите все в систему, основанную исключительно на событиях, нет гарантии, что ИИ не получат больше процессорного времени, чем ваш пользователь, или наоборот, если только у вас нет некоторого контроля над приоритетами потоков, и даже тогда вы склонны к трудностям в соблюдении сроков.
Поместите все в цикл, и вы гарантируете, что ваши временные рамки ИИ будут продолжаться в фиксированной зависимости от времени вашего пользователя.Это достигается путем вызова из вашего цикла, чтобы дать ИИ временной отрезок, в котором он может решить, что делать, вызова ваших подпрограмм пользовательского ввода, чтобы опросить устройства ввода, чтобы узнать, как ваш пользователь хочет себя вести, и позвать, чтобы сделать рендеринг.
С таким циклом вы должны следить за тем, чтобы на обработку каждого прохода у вас не уходило больше времени, чем на самом деле происходит в реальном времени. Если вы пытаетесь зациклить свой цикл на частоте 100 Гц, всю обработку вашего цикла лучше завершить менее чем за 10 мсек, иначе ваша система будет рывками. В программировании в реальном времени это называется превышением временных рамок. Хорошая система позволит вам контролировать, насколько вы близки к перерасходу, и затем вы сможете снизить нагрузку на обработку, как вы сочтете нужным.
Две причины -
Даже системам, управляемым событиями, обычно нужен какой-то цикл, который считывает события из какой-либо очереди и отправляет их в обработчик, так что вы все равно получите цикл событий в окнах, например, в любом случае, и его можно было бы расширить.
Для анимации вам нужно будет обрабатывать какой-то вид даже для каждого кадра анимации. Вы, конечно, могли бы сделать это с помощью таймера или какого-либо события бездействия, но вы, вероятно, в конечном итоге создадите их в каком-то цикле, так что просто проще использовать цикл напрямую.
Я видел системы, которые обрабатывают все это с помощью событий, у них есть приемник кадров, который прослушивает событие, отправляемое в начале каждого кадра. У них все еще есть крошечный игровой цикл внутри, но он делает немного больше, чем обрабатывает события оконной системы и создает события кадра,
Природа игр такова, что они, как правило, моделируют и реагируют не только на внешние события, но и на внутренние процессы. Вы можете представить эти внутренние процессы повторяющимися событиями вместо опроса, но они практически эквивалентны:
schedule(updateEvent, 33ms)
function updateEvent:
for monster in game:
monster.update()
render()
vs:
while 1:
for monster in game:
monster.update()
wait(33ms)
render()
Интересно, что pyglet реализует метод, основанный на событиях, вместо более традиционного цикла. И хотя в большинстве случаев это работает хорошо, иногда это вызывает проблемы с производительностью или непредсказуемое поведение, вызванное разрешением часов, vsync и т. Д. Цикл более предсказуем и проще для понимания (если вы не имеете опыта исключительно веб-программирования, возможно ).
Прослушиватель событий также зависит от некоторого цикла вызова, независимо от того, видите вы его или нет. Кто еще будет вызывать слушателя?
Создание явного игрового цикла дает вам полный контроль над тем, что происходит, поэтому вы не будете зависеть от того, что какой-нибудь инструментарий / библиотека обработки событий делает в своем цикле событий.
Это неправда, что все игры требуют выделенного главного цикла игры.
Экшн-игры нуждаются в таком цикле из-за частого обновления объектов и точности игрового ввода.
С другой стороны, я реализовал игру minesweeper и использовал window
сообщения для уведомлений.
Это потому, что текущие операционные системы не полностью основаны на событиях. Несмотря на то, что вещи часто представляются как события, вам все равно придется создать цикл, в котором вы ждете следующего события и обрабатываете его бесконечно (например, цикл событий Windows). Сигналы Unix, вероятно, наиболее близки к событиям на уровне ОС, но они недостаточно эффективны для подобных вещей.
На практике, как указывали другие люди, цикл необходим.
Однако ваша идея теоретически верна. Вам не нужен цикл. Вам нужны операции на основе событий.
На простом уровне можно представить ЦП, имеющий несколько таймеров;
Конечно, эти таймеры можно настроить.
Фактически, то, что могло бы происходить, было бы (несколько опущено) следующим образом:
void int_handler1();
//...
int main()
{
//install interrupt handlers
//configure settings
while(1);
}
Главный цикл вызывает слушателя событий. Если вам повезло, и у вас есть операционная система или оконный менеджер, управляемый событиями, то цикл событий находится там. В противном случае, вы пишете главный цикл для посредничества в "несоответствии импеданса" между интерфейсами системных вызовов, которые основаны на вводе/выводе, poll
или select
, и традиционным приложением, управляемым событиями.
P.S. Поскольку вы пометили свой вопрос функциональным программированием, вам стоит ознакомиться с Функционально-реактивным программированием, которое делает большую работу по соединению высокоуровневых абстракций с низкоуровневыми, основанными на событиях реализациями.
Любая программа, которая может просто сидеть там бесконечно долго и реагировать на ввод пользователя, нуждается в каком-то цикле. В противном случае программа просто дойдет до конца и выйдет.
Игра должна работать в режиме реального времени , поэтому лучше всего работает, если она постоянно работает на одном процессоре / ядре. Управляемое событиями приложение обычно передает ЦП другому потоку, если в очереди нет события. Может потребоваться значительное ожидание, прежде чем ЦП вернется к вашему процессу. В игре это означало бы короткие срывы и нервную анимацию.
Цикл игры (очень упрощенно выглядит следующим образом)
initialise do input update render loop clean up
Это будет происходить каждый кадр, в котором проводится игра. Таким образом, для игр со скоростью 60 кадров в секунду вышеуказанное выполняется шестьдесят раз в секунду.
Это означает, что игра работает плавно, игра остается синхронизированной и обновления / розыгрыши за цикл происходят достаточно часто. Анимация - это просто уловка глаза: объекты перемещаются между локациями, но при достаточно быстром воспроизведении кажется, что они перемещаются между этими локациями.
Если бы вы обновляли только ввод данных пользователем, игра реагировала бы только тогда, когда пользователь вводил данные. Другие игровые компоненты, такие как игровые объекты с искусственным интеллектом, не будут реагировать сами по себе. Таким образом, цикл - это самый простой и лучший способ обновления игры.