Блог dream2d

Регистрация

dream2d

Dream2D: Двумерная мечта

<<< О выборе платформы (немного истории)
Графы и локации>>>

Framework игрового приложения

Этот блог может быть интересен в основном таким же, как я, разработчикам инди-игр, а наиболее ценная информация — это чужие ошибки (с тем, чтобы их не повторять). Сейчас я хочу немного поговорить об общих принципах программирования и о каркасе игрового приложения, к которому я на данный момент пришёл для проекта Dream, пройдя довольно тернистый путь.

Конечно, при написании небольшой простой игры, которую вы не планируете изменять или расширять, строгая организация кода не очень-то и нужна. Во всех остальных случаях это жизненно необходимо. Прежде всего следует правильно выделить сущности, т.е. воспользоваться принципами объектно-ориентированного программирования. Очень полезно отделить друг от друга собственно игровой мир (некоторую модель), его отображение (вывод) и управление им (ввод). В игровом мире целесообразно выделить сущности различных игровых объектов, близких по своему поведению. Здесь можно использовать наследование, но не стоит слишком увлекаться: это запутывает код. В некоторых случаях проще скопировать код объекта и внести туда необходимые изменения. На Python, на мой взгляд, не стоит выделять низкоуровневые объекты, такие как вектор. Во-первых, это невероятно замедляет код, а во-вторых — передача таких объектов по ссылке часто неудобна (в Python все объекты — это ссылки, т.е. присваивание a=b ведёт к тому, что a и b ссылаются на один и тот же объект). Вектор проще всего представлять в виде двух отдельных вещественных чисел.

У меня в процессе игры игровой мир (объект game) переходит из одного состояния в другое, получая на каждом шаге управление для каждого игрока. Текущее состояние игрового мира отображается на экране, например, в зависимости от положения камеры. Камера — это отдельная сущность: она может быть привязана к игроку или перемещаться по каким-то своим законам. Опрос клавиатуры и мышки осуществляет главное приложение (server). Он создаёт объект game и запускает основной цикл, в котором выполняется следующее:

  • происходит обработка событий мыши и клавиатуры, проверяется состояние клавиш
  • формируется объект control (управление) и вызывается метод game.update(delta_t,control,eventlist), который переводит игровой мир в следующее состояние с шагом времени delta_t, управлением control, списком событий eventlist (о событиях позже)
  • вызывается метод camera.update, который обновляет положение камеры (оно тоже может управляться игроком)
  • происходит отрисовка в буфер (сейчас это выполняет объект camera, но думаю выделить в отдельную сущность)
  • происходит смена буферов OpenGL, при которой новое изображение попадает на экран
  • происходит задержка, доводящая общее время такта до 1/60 секунды (типичная частота обновления на ЖК-мониторах)

При многопользовательской игре сервер также должен получать информацию об управлении всеми остальными игроками, и рассылать информацию о текущем состоянии игрового мира клиентам. Но это отдельная большая тема, которую я сейчас не рассматриваю.

Объект game содержит всю начинку игры. Как правило он состоит из набора объектов игрового мира и должен обрабатывать все взаимодействия между ними. Он вызывает метод update каждого объекта, обрабатывает столкновения, создаёт новые объекты и удаляет отработавшие. Однако, не каждую из этих операций можно выполнять где угодно. Должна быть строго определна последовательность переключения игрового мира в новое корректное состояние (некорректное состояние — это, например, объект, застрявший в стене). Здесь на помощь приходит событийно-ориентированное программирование. Когда в игре что-то происходит (а это что-то может обнаружиться при выполнении метода game.update или метода update одного из игровых объектов), мы просто добавляем событие в очередь. Это событие будет обработано во время текущего или следующего такта именно в том месте, где это необходимо. Схема метода game.update может быть примерно такой:

  • обработка событий, в ходе которой могут быть добавлены новые объекты или удалены отработавшие, изменено состояние объектов
  • обновление состояния объектов (update)
  • обработка столкновений, корректировка состояний

В библиотеку PyGame очередь событий уже встроена. Туда поступают события от мыши и клавиатуры, туда же можно добавлять и пользовательские события. Кстати, недавно я решил написать простенький оконный интерфейс для Dream (когда-нибудь он всё равно понадобится). Это надстройка над всей вышеописанной системой — приложение server превращается из приложения уже в окно, которое может создавать свои дочерние окна. GUI передаёт события мышки и клавиатуры в активное окно, осуществляет переключение между окнами и т.п. GUI вызывает метод update каждого окна; окно, если в нём игра — методы update игры и камеры, игра — методы update своих дочерних объектов. После того, как все объекты обновлены, GUI производит отрисовку по тому же иерархическому принципу.

События — это своего рода канал связи между отдельными объектами всей системы. Если один объект хочет что-то передать другому, он создаёт событие. Каждый объект обрабатывает только те события, которые предназначены для него. Какие-то он может обрабатывать сам, какие-то — направлять дочерним объектам. Главное — чтобы всё было согласовано. :)

Теги: framework|python


  • olga-n25
    21 декабря 2009|13:52|ссылка
    Хм...