Как лучше разделить представление в памяти и на экране?

Темы, связанные с проектированием и программированием roguelike-игр

Модераторы: Sanja, Максим Кич

Ответить
Dmiry
Сообщения: 168
Зарегистрирован: 14 июн 2007, 10:32

Как лучше разделить представление в памяти и на экране?

Сообщение Dmiry » 28 фев 2009, 07:42

Есть сильное желание в игре выделить подсистему вывода на экран в отдельные классы. Пока в голову пришло 2 варианта:

Вариант 1.
Есть классы для внутреннего представления игровых сущностей (ячейки карты, предметы, монстры и т.д.), и есть класс для отображения всего на экране. При необходимости нарисовать клетку карты объект-клетка вызывает соответствующий метод класса отображения, передавая себя в качестве параметра.
Минусы:
- класс отображения сильно разрастается
- каждый объект сам отслеживает необходимость своей отрисовки
Плюсы:
- единая схема вывода на экран

Вариант 2.
Есть класс отображения, есть классы объектов игры. Каждый игровой объект параллельно сопровождается классом для отображения (типа Map - MapPainter, Monster - MonsterPainter). Общий класс отображения вызывает методы специфических рисователей, общая перерисовка инициируется главным циклом обработки событий.
Минусы:
- сопровождение параллельной иерархии объектов
Плюсы:
- каждый класс отображения является небольшим и связанным только с одним игровым классом

Оба этих варианта устраивают мало. Какие еще есть идеи?
Вывод на экран хочется сделать независимым от того, буквами или тайлами.
Первая заповедь фотолюбителя: Проявил себя - закрепи!

Аватара пользователя
Anfeir
Сообщения: 876
Зарегистрирован: 14 дек 2007, 09:29
Контактная информация:

Re: Как лучше разделить представление в памяти и на экране?

Сообщение Anfeir » 28 фев 2009, 08:14

А идея есть, такая.
Четкое разделение всего кода на непересекающиеся "блоки". Блоки взаимодействуют между собой, но не перекрываются. Например, блок UI и блок игрового мира (последний тоже может быть разделен). Б блоке Мира нет логики, ответственной за UI, максимум что там есть - свойства объектов, относящиеся к отображению (тайлы, символы, и тп.).
В блоке UI мы используем данные, какие можем собрать из других блоков, чтобы отобразить состояние игры.
Плюс - четкая локализация задач. Если чтото надо сделать с уи, то не придется шерстить все мегабайты кода, относящегося к миру. Ну и (относительная) независимость блоков друг от друга позволяет перестраивать их по отдельности.

Конечно, тут есть вопрос о принципе ООП, что каждый объект сам себя рисует, но это не обязательно. :)

Во втором вашем варианте, это имхо излишество. Если логика вывода объектов отличается, то добавить пару методов получения свойств того же монстра (GetImage, GetColor, GetBlabla,), или завести структуру Image, с необходимыми полями, и возвращать ее.
Image image = monster.GetImage();
setColor(image.color); print(image.symbol);
Хотя если объектов не очень много, порядка 10, то XxxPainter тоже выход. Если только для базовых объектов использовать (монстрПАинтер но не гоблинПаинтер).

В первом варианте, "Минусы: - класс отображения сильно разрастается". Он будет разрастатья при добавлении новых типов объектов. Что в принципе терпимо. ДОбавили новую вещь, монстра, скилл, - мало что изменилось в UI. Добавили эффекты (молнии, вспышки и тп), понятно, что изменится.
Зато, как бы это мысль выразить, от UI в коде НИЧЕГО больше не будет зависеть, на нем ничего не висит, так что его структура может быть и не совсем идеальной. А на структуру движка мира будет "навешано" все многообразие объектов и явлений, и добавлять новое вы будете скорей всего именно в часть, отвечающую за мир, а Ui - как дополнение, аппедникс, не то чтобы не важный (для конечного пользователя он должен быть на уровне), но к его структуре нет особо жестких требований.
Имхо.

Dmiry
Сообщения: 168
Зарегистрирован: 14 июн 2007, 10:32

Re: Как лучше разделить представление в памяти и на экране?

Сообщение Dmiry » 28 фев 2009, 11:28

Вот, мне именно интересно познакомиться с разделением на блоки и организацией взаимодействия блоков друг с другом. В моем проекте применялся первый вариант, в результате чего блоки мира и UI оказались очень переплетены.

Я хочу избежать ситуации "каждый объект сам себя рисует" - тогда можно будет выбирать способ отображения - буквы или тайлы, менять размер тайлов и т.д.

Я застрял на такой структуре паинтера:
Painter:
- DrawMapWindow
- DrawStatWindow
- DrawMessageWindow

MapWindow:
- DrawMapCell
- DrawItem
- DrawHero

С одной ошибкой долго мучался, когда карта выводилась дважды - сначала карта, затем герой, затем опять карта.
Хочу теперь этого избежать, вообще убрать зависимость блока мира от UI - т.е. игрок нажимает клавиши, изменяется состояние объектов игрового мира, а блок UI/рисования перерисовывает картинку на экране в соответствии с изменившимся состоянием, при необходимости извлекая все нужные данные из блока мира.
Первая заповедь фотолюбителя: Проявил себя - закрепи!

Аватара пользователя
Aerton
Сообщения: 503
Зарегистрирован: 11 авг 2007, 02:58
Откуда: Новосибирск
Контактная информация:

Re: Как лучше разделить представление в памяти и на экране?

Сообщение Aerton » 28 фев 2009, 11:57

Пусть каждый объект, который может быть отрисован на экране, содержит в себе всю информацию, необходимую для отрисовки в любом режиме. Можно её сгруппировать в структуру DrawInfo.

Когда надо что-то наирисовать, мы проходим по всем объектам, попавших в экран, и по данным из их DrawInfo рисуем буквы или спрайты в зависимости от режима.

Монстры и прочие объекты ничего не знают ни про систему рисования, ни когда она выполняется, а только поддерживают свой DrawInfo в актуальном состоянии.

DrawInfo едино для всех типов объектов. Если какие-то данные у данного объекта не нужны, они просто ставятся в NULL. DrawInfo не является базовым классом и в вообще наследовании не участвует, а аггрегируется (включается как переменная) в класс монстра, предмета, дерева, и т.п. Это надо, чтобы для перерисоваки можно было передавать данные только DrawInfo и не тащить всё остальное. В зависимости режима отображения система рисования интерпретирует данные по-своему.

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

В DrawInfo может хранится подобная информация:
  • * тип предмета - по нему определяется буква и цвет в ASCII, номер спрайта в графическом
    * номер визуальной вариации (случайное число) - если в графике имеется несколько вариации спрайта кирпичной стены, то это по этому значению мы и выбираем номер варианта для отрисовки x / WALL_VARIATIONS, а в текстовом режиме просто игнорируем это поле. Сама стена ничего не знает про диапазон значений, отрисовщик должен сам его проинтерпретировать как надо в данном режиме.
    * номер состояния - если орк ранен, буква может быть цветом темнее, а на спрайте кровь.
    * время перехода в это состояние - облако газов со временем становится более разреженным
ну и по потребностям конкретной игры

Dmiry
Сообщения: 168
Зарегистрирован: 14 июн 2007, 10:32

Re: Как лучше разделить представление в памяти и на экране?

Сообщение Dmiry » 28 фев 2009, 15:41

Отличная идея!
Хотя с моей точки зрения лучше будет перенести DrawInfo из игровых объектов в Painter или даже отдельный класс. Тогда игровые объекты будут содержать информацию только о состоянии игры. Во время рисования Painter считывает состояние объектов игры, обращается к перечню объектов DrawInfo (например, по типу клетки карты из соответствующего массива выбирается DrawInfo этой клетки), и выполняет рисование на экране.

Спасибо за замечание про перерисовку не только во время хода - я это упустил.

Хотя насчет определения, какие объекты попадают в экран, нужно отдельно подумать.
Первая заповедь фотолюбителя: Проявил себя - закрепи!

Аватара пользователя
Чёрствый Рогалик
Сообщения: 48
Зарегистрирован: 13 фев 2009, 14:35
Откуда: Санкт-Петербург

Re: Как лучше разделить представление в памяти и на экране?

Сообщение Чёрствый Рогалик » 02 мар 2009, 09:12

Дмитрий, а вы продолжаете печь свой рогалик? Почему же позволили в Lost Dreams попасть?
Анимэшницы и Велосипеды

Dmiry
Сообщения: 168
Зарегистрирован: 14 июн 2007, 10:32

Re: Как лучше разделить представление в памяти и на экране?

Сообщение Dmiry » 02 мар 2009, 15:26

Печь продолжаю, только ме-е-е-едленно :-).
Как только будет что-нибудь играбельное, вы об этом узнаете.
Первая заповедь фотолюбителя: Проявил себя - закрепи!

Аватара пользователя
Anfeir
Сообщения: 876
Зарегистрирован: 14 дек 2007, 09:29
Контактная информация:

Re: Как лучше разделить представление в памяти и на экране?

Сообщение Anfeir » 04 мар 2009, 10:43

Обработку клавиатуры/мыши/джойстика/педалей и т.п. я бы тоже в UI засунул.. к интерфейсу это имеет больше отношения, чем к игровому миру. а игровой мир получает от UI, скажем, команды. Не "оппа, клавиша 6 нажата!", а "а ну шасть на восток!"

Ответить

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 38 гостей