Как лучше разделить представление в памяти и на экране?
Модераторы: Sanja, Максим Кич
Как лучше разделить представление в памяти и на экране?
Есть сильное желание в игре выделить подсистему вывода на экран в отдельные классы. Пока в голову пришло 2 варианта:
Вариант 1.
Есть классы для внутреннего представления игровых сущностей (ячейки карты, предметы, монстры и т.д.), и есть класс для отображения всего на экране. При необходимости нарисовать клетку карты объект-клетка вызывает соответствующий метод класса отображения, передавая себя в качестве параметра.
Минусы:
- класс отображения сильно разрастается
- каждый объект сам отслеживает необходимость своей отрисовки
Плюсы:
- единая схема вывода на экран
Вариант 2.
Есть класс отображения, есть классы объектов игры. Каждый игровой объект параллельно сопровождается классом для отображения (типа Map - MapPainter, Monster - MonsterPainter). Общий класс отображения вызывает методы специфических рисователей, общая перерисовка инициируется главным циклом обработки событий.
Минусы:
- сопровождение параллельной иерархии объектов
Плюсы:
- каждый класс отображения является небольшим и связанным только с одним игровым классом
Оба этих варианта устраивают мало. Какие еще есть идеи?
Вывод на экран хочется сделать независимым от того, буквами или тайлами.
Вариант 1.
Есть классы для внутреннего представления игровых сущностей (ячейки карты, предметы, монстры и т.д.), и есть класс для отображения всего на экране. При необходимости нарисовать клетку карты объект-клетка вызывает соответствующий метод класса отображения, передавая себя в качестве параметра.
Минусы:
- класс отображения сильно разрастается
- каждый объект сам отслеживает необходимость своей отрисовки
Плюсы:
- единая схема вывода на экран
Вариант 2.
Есть класс отображения, есть классы объектов игры. Каждый игровой объект параллельно сопровождается классом для отображения (типа Map - MapPainter, Monster - MonsterPainter). Общий класс отображения вызывает методы специфических рисователей, общая перерисовка инициируется главным циклом обработки событий.
Минусы:
- сопровождение параллельной иерархии объектов
Плюсы:
- каждый класс отображения является небольшим и связанным только с одним игровым классом
Оба этих варианта устраивают мало. Какие еще есть идеи?
Вывод на экран хочется сделать независимым от того, буквами или тайлами.
Первая заповедь фотолюбителя: Проявил себя - закрепи!
Re: Как лучше разделить представление в памяти и на экране?
А идея есть, такая.
Четкое разделение всего кода на непересекающиеся "блоки". Блоки взаимодействуют между собой, но не перекрываются. Например, блок UI и блок игрового мира (последний тоже может быть разделен). Б блоке Мира нет логики, ответственной за UI, максимум что там есть - свойства объектов, относящиеся к отображению (тайлы, символы, и тп.).
В блоке UI мы используем данные, какие можем собрать из других блоков, чтобы отобразить состояние игры.
Плюс - четкая локализация задач. Если чтото надо сделать с уи, то не придется шерстить все мегабайты кода, относящегося к миру. Ну и (относительная) независимость блоков друг от друга позволяет перестраивать их по отдельности.
Конечно, тут есть вопрос о принципе ООП, что каждый объект сам себя рисует, но это не обязательно.
Во втором вашем варианте, это имхо излишество. Если логика вывода объектов отличается, то добавить пару методов получения свойств того же монстра (GetImage, GetColor, GetBlabla,), или завести структуру Image, с необходимыми полями, и возвращать ее.
Image image = monster.GetImage();
setColor(image.color); print(image.symbol);
Хотя если объектов не очень много, порядка 10, то XxxPainter тоже выход. Если только для базовых объектов использовать (монстрПАинтер но не гоблинПаинтер).
В первом варианте, "Минусы: - класс отображения сильно разрастается". Он будет разрастатья при добавлении новых типов объектов. Что в принципе терпимо. ДОбавили новую вещь, монстра, скилл, - мало что изменилось в UI. Добавили эффекты (молнии, вспышки и тп), понятно, что изменится.
Зато, как бы это мысль выразить, от UI в коде НИЧЕГО больше не будет зависеть, на нем ничего не висит, так что его структура может быть и не совсем идеальной. А на структуру движка мира будет "навешано" все многообразие объектов и явлений, и добавлять новое вы будете скорей всего именно в часть, отвечающую за мир, а Ui - как дополнение, аппедникс, не то чтобы не важный (для конечного пользователя он должен быть на уровне), но к его структуре нет особо жестких требований.
Имхо.
Четкое разделение всего кода на непересекающиеся "блоки". Блоки взаимодействуют между собой, но не перекрываются. Например, блок UI и блок игрового мира (последний тоже может быть разделен). Б блоке Мира нет логики, ответственной за UI, максимум что там есть - свойства объектов, относящиеся к отображению (тайлы, символы, и тп.).
В блоке UI мы используем данные, какие можем собрать из других блоков, чтобы отобразить состояние игры.
Плюс - четкая локализация задач. Если чтото надо сделать с уи, то не придется шерстить все мегабайты кода, относящегося к миру. Ну и (относительная) независимость блоков друг от друга позволяет перестраивать их по отдельности.
Конечно, тут есть вопрос о принципе ООП, что каждый объект сам себя рисует, но это не обязательно.
Во втором вашем варианте, это имхо излишество. Если логика вывода объектов отличается, то добавить пару методов получения свойств того же монстра (GetImage, GetColor, GetBlabla,), или завести структуру Image, с необходимыми полями, и возвращать ее.
Image image = monster.GetImage();
setColor(image.color); print(image.symbol);
Хотя если объектов не очень много, порядка 10, то XxxPainter тоже выход. Если только для базовых объектов использовать (монстрПАинтер но не гоблинПаинтер).
В первом варианте, "Минусы: - класс отображения сильно разрастается". Он будет разрастатья при добавлении новых типов объектов. Что в принципе терпимо. ДОбавили новую вещь, монстра, скилл, - мало что изменилось в UI. Добавили эффекты (молнии, вспышки и тп), понятно, что изменится.
Зато, как бы это мысль выразить, от UI в коде НИЧЕГО больше не будет зависеть, на нем ничего не висит, так что его структура может быть и не совсем идеальной. А на структуру движка мира будет "навешано" все многообразие объектов и явлений, и добавлять новое вы будете скорей всего именно в часть, отвечающую за мир, а Ui - как дополнение, аппедникс, не то чтобы не важный (для конечного пользователя он должен быть на уровне), но к его структуре нет особо жестких требований.
Имхо.
Re: Как лучше разделить представление в памяти и на экране?
Вот, мне именно интересно познакомиться с разделением на блоки и организацией взаимодействия блоков друг с другом. В моем проекте применялся первый вариант, в результате чего блоки мира и UI оказались очень переплетены.
Я хочу избежать ситуации "каждый объект сам себя рисует" - тогда можно будет выбирать способ отображения - буквы или тайлы, менять размер тайлов и т.д.
Я застрял на такой структуре паинтера:
Painter:
- DrawMapWindow
- DrawStatWindow
- DrawMessageWindow
MapWindow:
- DrawMapCell
- DrawItem
- DrawHero
С одной ошибкой долго мучался, когда карта выводилась дважды - сначала карта, затем герой, затем опять карта.
Хочу теперь этого избежать, вообще убрать зависимость блока мира от UI - т.е. игрок нажимает клавиши, изменяется состояние объектов игрового мира, а блок UI/рисования перерисовывает картинку на экране в соответствии с изменившимся состоянием, при необходимости извлекая все нужные данные из блока мира.
Я хочу избежать ситуации "каждый объект сам себя рисует" - тогда можно будет выбирать способ отображения - буквы или тайлы, менять размер тайлов и т.д.
Я застрял на такой структуре паинтера:
Painter:
- DrawMapWindow
- DrawStatWindow
- DrawMessageWindow
MapWindow:
- DrawMapCell
- DrawItem
- DrawHero
С одной ошибкой долго мучался, когда карта выводилась дважды - сначала карта, затем герой, затем опять карта.
Хочу теперь этого избежать, вообще убрать зависимость блока мира от UI - т.е. игрок нажимает клавиши, изменяется состояние объектов игрового мира, а блок UI/рисования перерисовывает картинку на экране в соответствии с изменившимся состоянием, при необходимости извлекая все нужные данные из блока мира.
Первая заповедь фотолюбителя: Проявил себя - закрепи!
- Aerton
- Сообщения: 503
- Зарегистрирован: 11 авг 2007, 02:58
- Откуда: Новосибирск
- Контактная информация:
Re: Как лучше разделить представление в памяти и на экране?
Пусть каждый объект, который может быть отрисован на экране, содержит в себе всю информацию, необходимую для отрисовки в любом режиме. Можно её сгруппировать в структуру DrawInfo.
Когда надо что-то наирисовать, мы проходим по всем объектам, попавших в экран, и по данным из их DrawInfo рисуем буквы или спрайты в зависимости от режима.
Монстры и прочие объекты ничего не знают ни про систему рисования, ни когда она выполняется, а только поддерживают свой DrawInfo в актуальном состоянии.
DrawInfo едино для всех типов объектов. Если какие-то данные у данного объекта не нужны, они просто ставятся в NULL. DrawInfo не является базовым классом и в вообще наследовании не участвует, а аггрегируется (включается как переменная) в класс монстра, предмета, дерева, и т.п. Это надо, чтобы для перерисоваки можно было передавать данные только DrawInfo и не тащить всё остальное. В зависимости режима отображения система рисования интерпретирует данные по-своему.
Это лучше, чем вызывать отрисовку из классов монстров тем, что перерисовать экран может понадобиться не только во вресмя хода, а вообще в любой момент, как игровых (игрок залез в инвентарь), так и не зависящих от игры (пользователь развернул окошко и ОС требует его перерисовать).
В DrawInfo может хранится подобная информация:
Когда надо что-то наирисовать, мы проходим по всем объектам, попавших в экран, и по данным из их DrawInfo рисуем буквы или спрайты в зависимости от режима.
Монстры и прочие объекты ничего не знают ни про систему рисования, ни когда она выполняется, а только поддерживают свой DrawInfo в актуальном состоянии.
DrawInfo едино для всех типов объектов. Если какие-то данные у данного объекта не нужны, они просто ставятся в NULL. DrawInfo не является базовым классом и в вообще наследовании не участвует, а аггрегируется (включается как переменная) в класс монстра, предмета, дерева, и т.п. Это надо, чтобы для перерисоваки можно было передавать данные только DrawInfo и не тащить всё остальное. В зависимости режима отображения система рисования интерпретирует данные по-своему.
Это лучше, чем вызывать отрисовку из классов монстров тем, что перерисовать экран может понадобиться не только во вресмя хода, а вообще в любой момент, как игровых (игрок залез в инвентарь), так и не зависящих от игры (пользователь развернул окошко и ОС требует его перерисовать).
В DrawInfo может хранится подобная информация:
- * тип предмета - по нему определяется буква и цвет в ASCII, номер спрайта в графическом
* номер визуальной вариации (случайное число) - если в графике имеется несколько вариации спрайта кирпичной стены, то это по этому значению мы и выбираем номер варианта для отрисовки x / WALL_VARIATIONS, а в текстовом режиме просто игнорируем это поле. Сама стена ничего не знает про диапазон значений, отрисовщик должен сам его проинтерпретировать как надо в данном режиме.
* номер состояния - если орк ранен, буква может быть цветом темнее, а на спрайте кровь.
* время перехода в это состояние - облако газов со временем становится более разреженным
Re: Как лучше разделить представление в памяти и на экране?
Отличная идея!
Хотя с моей точки зрения лучше будет перенести DrawInfo из игровых объектов в Painter или даже отдельный класс. Тогда игровые объекты будут содержать информацию только о состоянии игры. Во время рисования Painter считывает состояние объектов игры, обращается к перечню объектов DrawInfo (например, по типу клетки карты из соответствующего массива выбирается DrawInfo этой клетки), и выполняет рисование на экране.
Спасибо за замечание про перерисовку не только во время хода - я это упустил.
Хотя насчет определения, какие объекты попадают в экран, нужно отдельно подумать.
Хотя с моей точки зрения лучше будет перенести DrawInfo из игровых объектов в Painter или даже отдельный класс. Тогда игровые объекты будут содержать информацию только о состоянии игры. Во время рисования Painter считывает состояние объектов игры, обращается к перечню объектов DrawInfo (например, по типу клетки карты из соответствующего массива выбирается DrawInfo этой клетки), и выполняет рисование на экране.
Спасибо за замечание про перерисовку не только во время хода - я это упустил.
Хотя насчет определения, какие объекты попадают в экран, нужно отдельно подумать.
Первая заповедь фотолюбителя: Проявил себя - закрепи!
- Чёрствый Рогалик
- Сообщения: 48
- Зарегистрирован: 13 фев 2009, 14:35
- Откуда: Санкт-Петербург
Re: Как лучше разделить представление в памяти и на экране?
Дмитрий, а вы продолжаете печь свой рогалик? Почему же позволили в Lost Dreams попасть?
Анимэшницы и Велосипеды
Re: Как лучше разделить представление в памяти и на экране?
Печь продолжаю, только ме-е-е-едленно .
Как только будет что-нибудь играбельное, вы об этом узнаете.
Как только будет что-нибудь играбельное, вы об этом узнаете.
Первая заповедь фотолюбителя: Проявил себя - закрепи!
Re: Как лучше разделить представление в памяти и на экране?
Обработку клавиатуры/мыши/джойстика/педалей и т.п. я бы тоже в UI засунул.. к интерфейсу это имеет больше отношения, чем к игровому миру. а игровой мир получает от UI, скажем, команды. Не "оппа, клавиша 6 нажата!", а "а ну шасть на восток!"
Кто сейчас на конференции
Сейчас этот форум просматривают: нет зарегистрированных пользователей и 38 гостей