Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Проектирование текстового квеста (http://www.flasher.ru/forum/showthread.php?t=214641)

Appleman 22.10.2017 16:19

Проектирование текстового квеста
 
Друзья! Ещё раз спасибо за терпеливые объяснения. Делал по заветам мэтров, на сегодня имею:
1. кнопки - экземпляры класса MenuButton, которые умеют нажиматься и посылать по клику событие, содержащее свой номер (id:uint задаётся в конструкторе как входной аргумент);
2. меню - экземпляры класса Menu, которое строится с заданным кол-вом кнопок в колонку, создаёт и добавляет в себя сами кнопки и слушает события от них (даже успешно их принимает). Само пока правда ничего никому не посылает.
3. класс MainView, назначенный корневым DOC всего приложения (сей факт был отмечен банкетом), который пока что ничего особо не умеет, лишь помещает в себя это самое меню в порядке теста.

Одно пока не получается: как заставить всё это работать на практике!

Делал всё, ориентируясь на вот эти установки:
Цитата:

Сообщение от undefined (Сообщение 1201976)
бардак - это когда на кто-угодно добавляет дисплей обжекты на сцену.Правильно:есть один DO-контейнер, который создает дочерние DO-компоненты, каждый компонент рисует свои внутренности.Если надо выввести что-то специфичное(хинт или диалог поверх всего что есть на экране) - шлем контейнеру ивент с описанием что хотим.

Цитата:

Сообщение от Wolsh (Сообщение 1201991)
Когда кнопку нажмут, Меню(!) должно послать событие что юзер сделал выбор. На события наведение и увода мыши, нажатия и отпускания Кнопки должны уметь реагировать самостоятельно — это их ответственность. И, наконец, тот кто создал экземпляр Меню, подписывается на событие от Меню "юзер сделал выбор", и получив это событие, разбирается что делать дальше. Надо четко понимать где чья ответственность. Каждый элемент ниже по иерархии является самостоятельным в выполнении своей задачи ИНСТРУМЕНТОМ для того, кто выше: Кнопка должна уметь "нажиматься", Меню должно уметь предоставить выбор, тот кто создал меню — знать что делать с выбором. Это называется иерархия. Тот, кто ниже, обычно извещает того кто выше — своего создателя — о происходящих в нем изменениях, но РЕШЕНИЯ принимает только в своей области ответственности. То есть внутри Кнопки не содержится кода, запускающего игру. И в Меню его не содержится. Меню не запускает игру, это не его ответственность.

Итак, с кнопки - спрос маленький: появляется, нажимается, докладывает наверх "меня нажали", окей. Меню "знает" сколько надо кнопок, какой на каждой должен быть идентификатор лэйбла, активной или нет будет кнопка. Таким образом, через класс Menu нужно "протащить" как минимум по 3 аргумента для каждой из кнопок. По нажатии на кнопку оно докладывает наверх "нажата кнопка id такой-то". А вот что дальше (выше), я сообразить не могу.

У нас по ходу выполнения какого-то отвлечённого кода возникает момент, когда юзера надо спросить, да или нет. Получается, соблюдая цитированные выше принципы, мы должны "попросить" корневой DOC сделать и поместить в себя меню. Я ещё понимаю, что всё это "мясо" с инфой, необходимой для создания кнопок, можно "протащить" и через класс MainView... Понимаю, что корневой DOC может вызвать метод по созданию меню и помещению его в себя. Но как он поймёт, о чём именно мы спросили пользователя, и как практически обрабатывать события, получаемые от меню? Или это меню нужно "готовить" в одном месте, а через DOC -
только выводить на экран? Поясните плиз или киньте ссылку с примером.

И ещё одна тривиальная вещь у меня в голову не может уложиться. Какой смысл строить взаимодействие с визуальными компонентами посредством механизма событий? Понятно, что если мы говорим об обработке каких-то действий от пользователя, то без событий не обойтись. Но если, например, мы строим то же меню, то почему обязательно нужно слать ивент контейнеру? Почему нельзя в контейнере сделать публичным сам метод и вызвать его из любого места программы напрямую?

Wolsh 23.10.2017 20:21

Я даже не очень понял, о чем ты спрашиваешь((
Цитата:

возникает момент, когда юзера надо спросить, да или нет.
Это называется ConfirmWindow, то есть всплывающее (popup) окошко с текстом вопроса и двумя кнопками "да" и "нет". Можно и без событий:
Код AS3:

PopupManager.confirm(Language.getText("127493"), deleteFileConfirmHandler);
//...
private function deleteFileConfirmHandler(result:Boolean):void {}

PopupManager создаст стандартное окно класса ConfirmWindow с предоставленным текстом, разместит его в выделенном для попапов "слое"-спрайте в мэйн вью и либо подпишется на событие от него, либо передаст ему колбек-ссылку на функцию deleteFileConfirmHandler, это уж кому какая архитектура нравится.
То есть ты перепоручаешь все строительные работы профессионалам; твое дело задать вопрос и суметь получить ответ. На всякий случай намекну: надо еще спросить так, чтобы твоему герою никто голову не отрубил, пока игрок думает, действительно ли он хочет выбросить из инвентаря эти "поношенные обмотки", то есть надо/не надо остановить происходящее на экране на время диалога с пользователем.

Цитата:

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

Appleman 23.10.2017 22:09

Цитата:

Сообщение от Wolsh (Сообщение 1202541)
Я даже не очень понял, о чем ты спрашиваешь((

О-о-ох! Предлагаю спуститься на уровень ниже плинтуса, чтобы осознать мой вопрос. Я наверное зря привёл пример про "да и нет". Пусть будет обычное игровое меню со стандартными опциями: "Новая игра", "Загрузить", "Опции" и "Выход".

Я понял, как делать иерархическую систему для создания компонентов и вывода их на экран: MainView создаёт экземпляр класса Menu с некими входными параметрами и добавляет его в себя. Экземпляр Menu, используя эти параметры, создаёт и добавляет в себя экземпляры Button. Button-ы честно моргают при наведении и нажимаются при клике, даже посылают событие ButtonEvent, которое улавливается экземпляром Menu, о чём делается сообщение в консоль.

А вот, блин, как связать всё это удовольствие с нужными действиями и "повесить" на нужные мне кнопки функции запуска, загрузки, выхода и т.п., вот до этого мне чего-то не допереть, уж простите. То есть меню корректно создаётся как объект на экране, но вот как использовать его "в боевом режиме" я не понимаю. :(

Партизан 23.10.2017 23:06

Цитата:

Сообщение от Appleman (Сообщение 1202544)
А вот, блин, как связать всё это удовольствие с нужными действиями и "повесить" на нужные мне кнопки функции запуска, загрузки, выхода и т.п., вот до этого мне чего-то не допереть, уж простите. То есть меню корректно создаётся как объект на экране, но вот как использовать его "в боевом режиме" я не понимаю. :(

MainView запуская допустим Menu "спрашивает" что делать дальше:
Код AS3:

var menu:Menu = new Menu();
menu.addEventListener(MenuEvent.Select, menuSelected)
addChild(menu);
 
function menuSelected(e:MenuEvent):void{
if(e.type == MenuEvent.RUN_GAME){// тогда грузим собственно игровой процесс}
if(e.type == MenuEvent.OPTIONS){// грузим опции }
if(e.type == MenuEvent.EXIT){//выход}
}

грубо, но примерно так. Запуск игры(Run_game) и опции(options) делают примерно то же что и Menu, просто возвращают данные(которые запрашивает MainView) в контроллер(MainView) a Exit просто завершает программу.

Appleman 24.10.2017 00:28

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

Попробую ещё ближе к моему реальному проекту. Это - текстовый квест. Основная механика такова, что выбрав очередное действие, пользователь видит некий арт на бэкграунде, получает порцию текста в окно вывода текста, а также меню с вариантами дальнейших действий. Кликает на вариант и всё повторяется. Поэтому подобные меню появляются часто, но в первом случае там будет условно "пойти налево", "пойти прямо" и "пойти направо", а в следующую итерацию - "дать в лоб", "тихо обойти" и "попытаться охмурить" (причём последний вариант доступен только в случае, если герой - противоположного с NPC пола). Таким образом, каждый раз меняется количество опций, а также функции, которые будут выполняться при клике на тот или иной пункт.

И аналогично, ни хрена я не понимаю, как тест выдавать. Пока сделал объект-область вывода, куда воткнул дочерний TextField, создал экземпляр в MainView. А как туда реальный текст посылать, да ещё с вариативным форматированием - опять туплю. Ведь текст создаётся в другом месте. Вернее сказать так: в процессе выполнения кода, обрабатывающего результат очередной итерации, появляется строковый id, по которому из XML с фразами на заданном языке вытаскивается нужный кусок текста. В консоль его прекрасно вывожу, а так отправить в окно вывода - не пойму.

Такое ощущение, что упускаю какую-то очень простую и важную вещь во всём этом хозяйстве, без которой пазл не складывается и каменный цветок не выходит. Просьба пояснить, если я ещё неутомил своим ламерством :rolleyes: Спасибо.

Wolsh 24.10.2017 00:42

Партизан — Запутаешь человека) Подписываешься на MenuEvent.Select, а потом спрашиваешь какого же оно типа))

Appleman — Ну вобщем, подписываешься либо на каждый тип события, либо передаешь в Событии строку, что же именно выбрал юзер. Можно не городить свой кастомный MenuEvent, взять DataEvent из коробки, у него есть свойство для транспортировки data типа String.

Добавлено через 13 минут
Ох же шь... Ну, тут тебе надо хорошенько продумать саму механику квестов, с таким то ветвлением..
То есть, на каждую стадию у тебя должен быть текст, описывающий ситуацию и к нему набор вариантов действий с какими-то ID (здесь может быть просто a, b, c), и к каждому варианту — адрес следующей стадии, то есть куда перейти в случае выбора этого варианта.
Тогда получится запрограммировать механику абстрактно, то есть универсально для всех стадий, а не расписывать В КОДЕ каждый поворот сюжета))

Партизан 24.10.2017 01:08

Цитата:

Сообщение от Wolsh (Сообщение 1202548)
Партизан — Запутаешь человека) Подписываешься на MenuEvent.Select, а потом спрашиваешь какого же оно типа))

Нда, не хорошо получилось. Надо бы добавить пару подписок. или троеточие воткнуть в код :)

Appleman 24.10.2017 11:31

Цитата:

Сообщение от Wolsh (Сообщение 1202548)
То есть, на каждую стадию у тебя должен быть текст, описывающий ситуацию и к нему набор вариантов действий с какими-то ID (здесь может быть просто a, b, c), и к каждому варианту — адрес следующей стадии, то есть куда перейти в случае выбора этого варианта.
Тогда получится запрограммировать механику абстрактно, то есть универсально для всех стадий, а не расписывать В КОДЕ каждый поворот сюжета))

Да, именно. Это наверное обратно в тему с вопросами по проектированию :), но я кратно обрисую, что придумал. Планирую сделать класс CurrentState, хранящий инфо о текущей фазе игры: локация, участвующие персонажи и т.п. На основании свойств экземпляра Current State выводится нужный исходный арт и текст. Далее запускается метод отбора вариантов действий для игрока. Действие - это либо экземпляры класса, либо данные статической таблицы (помятуя о твоих советах в соседней ветке), но в любом случае они должны быть описаны некоторым набором свойств, корреспондирующих аналогичным свойствам других игровых объектов. Все они помещаются в массив, который перебирается поэлементно. Значения свойств каждого потенциального действия сравниваются с соответствующими значениями из currentState или экземпляров других классов, которые содержит CurrentState. Если все соответствуют, значит, действие может быть выполнено, и оно должно попасть в меню выбора для игрока. Таким образом, по итогу из общего перечня остаётся несколько доступных действий (вот их и надо как-то отправить в меню, и главное, получить обратную связь). Когда получена инфо, какое действие выбрал игрок, запускаем метод-обработчик, который получает на входе тот самый экземпляр (если действие - это класс) или идентификатор выбранного пользователем действия, и дальше уже "распихивает" задачи по специализированным классам: рассчитать результаты, обновить арт и текст, обновить значения свойств персонажей по результату, обновить CurrentState. Собственно, потом всё повторяется. Как-то так вчерне.

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

Wolsh 24.10.2017 14:32

Как мне кажется, тебе нужен класс - наследник спрайта - представляющий собственно слайд (или как там это называется в скроллерах), кадр, шаг игры. Не данные, про которые ты говоришь — это в Модели, а именно визуалку. То есть то, что отображает твой CurrentState в виде фоновой картинки и "диалога". Скажем, StateView. Он, естественно, может включать в себя отдельные компоненты, то же "меню" (хотя мне в этом контексте название "меню" не нравится, сбивает, даже если технически оно правильное; я бы назвал "диалог" — и у тебя наверняка будут именно диалоги персонажа с неписями, и они будут точно также "оформлены"). Вот его тебе и надо учить показывать кликабельные варианты. Не надо пихать всю ответственность в мейнВью.
Что конкретно то не получается с выводом текста? Текстфилд, конечно, штука капризная и нужен некоторый опыт чтобы его настраивать как хочется.. но не настолько же, чтобы вообще текст вывести не получалось?

Appleman 24.10.2017 15:36

Цитата:

Сообщение от Wolsh (Сообщение 1202573)
Как мне кажется, тебе нужен класс - наследник спрайта - представляющий собственно слайд (или как там это называется в скроллерах), кадр, шаг игры. Не данные, про которые ты говоришь — это в Модели, а именно визуалку. То есть то, что отображает твой CurrentState в виде фоновой картинки и "диалога". Скажем, StateView.

Да, я это и подразумевал, когда писал про класс CurrentState, что он создаёт наполнение для вывода на экран, а вот отделить непосредственно вывод в класс типа StateView не додумался. Согласен, как раз получается тандем "модель-вью".

Цитата:

Он, естественно, может включать в себя отдельные компоненты, то же "меню" (хотя мне в этом контексте название "меню" не нравится, сбивает, даже если технически оно правильное; я бы назвал "диалог" — и у тебя наверняка будут именно диалоги персонажа с неписями, и они будут точно также "оформлены"). Вот его тебе и надо учить показывать кликабельные варианты. Не надо пихать всю ответственность в мейнВью.
Окей, определились с термином "диалог".

Цитата:

Что конкретно то не получается с выводом текста? Текстфилд, конечно, штука капризная и нужен некоторый опыт чтобы его настраивать как хочется.. но не настолько же, чтобы вообще текст вывести не получалось?
Я похоже начинаю понимать, чего я упустил и из-за чего у меня ни хрена не получается. Я возможно слишком буквально и в лоб воспринял рекомендацию о том, что весь вывод на экран и взаимодействие с пользователем должны быть делегированы корневому DOC. С тем же текстом я рассуждал так. Получили идентификатор текста фразы, вытащили из XML на нужном языке. Теперь нужно создать какое-то событие, которое заставит MainView добавить этот текст в соответствующее поле. На этом - полный ступор.

Можно мне в режиме дурака для примера кусочек кода, показывающего взаимодействие корневого DOC и условного StateView? Я верно понимаю, что в мы передаём в экземпляр StateView ссылку на MainView, первым делом записываем её в приватную константу и "добавляемся" в неё:
Код AS3:

private const _gameview:MainView = mainView;
_gameview.addChild(this);

И теперь уже прямо в StateView, не отходя от кассы, мы можем создавать объект TextField и, написав несложный метод, выводить в него тексты?

Wolsh 24.10.2017 18:26

Нет.
Хотя поначалу тебе всегда будет хотеться сделать все задом наперед, аж на уровне подсознания.
Но "иерархия" подразумевает, что никакое _gameview.addChild(this); невозможно в принципе. Потому что это приказ родителям от ребенка. В иерархии приказывает тот, кто стоит выше. Он создает себе инструмент и управляет им. Если инструмент считает, что он знает больше, он посылает событие родителям "вас вызывают в школу". Но даже в этом случае родители сами решают, что всвязи с этим предпринять.
Нормальный код выглядит примерно так:
Код AS3:

//// MainView.as
_model.addEventListener(ModelEvent.STATE_CHANGE, handlerStateChange);
//...
private function handlerStateChange(event:ModelEvent):void {
_stateView.show(_model.currentState);

То есть Вью получает событие от Модели "стейт изменился", забирает из модели новый стейт и отдает его СтейтВью.

Добавлено через 23 минуты
"И теперь уже прямо в StateView, не отходя от кассы, мы можем создавать объект TextField и, написав несложный метод, выводить в него тексты"

Добавлено через 36 минут
Цитата:

Я верно понимаю, что в мы передаём в экземпляр StateView ссылку на MainView, первым делом записываем её в приватную константу и "добавляемся" в неё
Давай вот сам подумай. Откуда взялся этот "экземпляр StateView"? Кто его создал? MainView? Так зачем городить огород, передавая ссылку в экземпляр, если тот кто его создал, тут же может его и добавить? У тебя вообще звучит так, словно и не MainView его создал, а кто-то где-то, и сказал куда добавиться. А ничо, что там наверно уже сотня таких добавлена, потому что про удаление таких предыдущих молодцов ты не говоришь. Это то, почему экземпляр не должен САМ себя куда-то добавлять и сам себя удалять (тут обычно холивар начинается любителями аутодафе) — тупо потому, что он не знает общей картины и даже цели своего существования, это знает только Создатель. Создатель знает, что если надо добавить новый экран, то старый надо удалить. И у него ЕСТЬ ссылка на этот старый, а у нового нет. Это только одна из миллиарда возможных ситуаций класса Конфуз.as ))
В моем примере _стейтВью один и всегда добавлен на сцену; но даже если бы это было не так, МейнВью ЗНАЛ бы, что перед созданием нового стейтВью надо удалить старый — они в его руках, он их создает и разрушает.

Добавлено через 43 минуты
Новый экземпляр нужно создавать тогда, когда необходимо сохранить старый.
Если старый не нужен, он очищается и строится заново.
"Используется повторно".

Appleman 24.10.2017 22:58

Цитата:

Сообщение от Wolsh (Сообщение 1202578)
Нормальный код выглядит примерно так:
Код AS3:

//// MainView.as
_model.addEventListener(ModelEvent.STATE_CHANGE, handlerStateChange);
//...
private function handlerStateChange(event:ModelEvent):void {
_stateView.show(_model.currentState);

То есть Вью получает событие от Модели "стейт изменился", забирает из модели новый стейт и отдает его СтейтВью.

Ы-ы-ы-ы... У меня башка лопается :)
Хорошо, а если у нас ситуация, когда стейт не меняется? Я уже спрашивал на счёт отправки содержимого в TextField. Все компоненты - на своих местах, без изменений. Но в TextField нужно поочерёдно вывести 4 куска текста, причём некоторые - со своим TextFormat-ом. Как это делается, если id текстов определяются в model? Как их "переправить" в МэйнВью вместе с форматами?

Цитата:

А ничо, что там наверно уже сотня таких добавлена, потому что про удаление таких предыдущих молодцов ты не говоришь.
Грешно смеяться... Я создать-то толком не могу, а ты - удалять! ;)

Wolsh 24.10.2017 23:28

2) Ну, форматирование текста можно задавать с помощью html, какой-никакой а в AS3 он есть. То есть уже в xml можно хранить текст отформатированным. Либо, как вариант, утвердить несколько конкретных форматов и размечать текст своим способом (заключать в теги со "своими" застолбленными именами, типа <boldred/>), а потом парсить и назначать этим кускам нужный, заранее известный ТекстФормат.

1) Ну не меняется стейт и не меняется, другое событие значит пошлешь)

0) У всех лопается, это ж не банер "Осенние скидки купи сейчас по летним ценам". Спрашивай что непонятно, а то широкими то мазками я горазд размахивать, мне ж отсюда не видно, в чем у тебя затык.
Я вот уже не могу без MVC представить архитектуру, а ты ведь по-настоящему не углублялся в эту тему? К тому же, никто кроме тебя не знает, что за фантазии тебя обуревают. "Стейт не меняется а текст меняется". Так что, картинка на фоне не меняется чтоли? Ну, СтейтВью как-то должен с этим разобраться, для него не должно быть проблемы понять, что картинка та же) Но если все тоньше и это именно НЕ новый стейт, то все-равно это какое-то изменение в модели и она должна об этом сообщить заинтересованным лицам (Вью).
У тебя щас проблема наверное в том, что идей много а окончательной формы пока не видно. Задача твоя как проектировщика определить некие стандарты: что выводиться на экран будет то-то и то-то, таким вот образом раз, и вот таким образом два. Есть вот такой стиль, и вот такой, и нужен еще третий. Найти в куче идей какие-то общие моменты, чтобы разные интересные штуки можно было оформить каким-то общим способом, чтобы этих способов было не 100500 а 5-7. Это важно и для игрока, чтобы его не ошарашивал интерфейс, хаотически меняющийся на каждом слайде. Важно сформулировать, какие именно возможности тебе нужны от движка, иначе на любое решение ты будешь восклицать "а если мне захочется еще и вот так!", и тогда конца-края вообще не дождешься.

Appleman 25.10.2017 00:56

Цитата:

Спрашивай что непонятно, а то широкими то мазками я горазд размахивать, мне ж отсюда не видно, в чем у тебя затык.
Спасибо, но чтобы спрашивать, нужно сформулировать. Боюсь, вопросы в стиле "как мне сделать, чтобы стало всё красиво" не найдёт сочувствия и поддержки. А у меня пока действительно затык, когда я и дальше продвинуться не могу, и спросить не понимаю, чего конкретно :( До сего момента я азартно писал всякие сервисные функции типа подбора текста и подстановки туда слов в нужных падежах. А как до самого геймплея дошёл, тут и алис!

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

Цитата:

Я вот уже не могу без MVC представить архитектуру, а ты ведь по-настоящему не углублялся в эту тему?
Значит, недостаточно углублялся, раз застрял. На самом деле пытался. Хотя бы на концептуальном уровне. Мысли пока следующие. Я всерьёз размышлял, нужна ли мне вообще MODEL для выбранной механики. По итогу решил, что всё-таки да, нужна. Ибо не важно, каким образом и как часто требуется обновлять визуальный ряд: по милисекундам для реал-тайм игры или раз в минуту по клику. Всё равно принцип тот же. А вот необходимость в CONTROL для меня кажется излишней, т.к. никакого прямого управления процессом со стороны игрока нет, только выбор предлагаемых вариантов на каждой стадии. Из этого я сделал вывод, что "С" мы можем интегрировать в "М". Таким образом, общая архитектура получилась примерно такой, какую советовал ещё коллега Nooob в самом начале недавно закрытой темы по проектированию: data + model + view. Это я и взял за основу. Правда, признаюсь честно, местами слабо чувствую разницу между data и model :)

Цитата:

К тому же, никто кроме тебя не знает, что за фантазии тебя обуревают.
Теперь про механику конкретнее. Я решил пока отодвинуть в сторону "большую" игру и попрактиковаться на отдельном её элементе, а именно на схватках. Они по задумке могут проходить в режиме автобоя, а можно запустить мини-игру. Её механика повторяет общеигровую, просто вариативность меньше. Вот такую мини-игру я сейчас и разрабатываю. На старте мы имеем нашего героя с полным набором RPG-шных свойств: постоянных характеристик, неизменных на протяжении игры, набором изменяемых параметров, навыков и т.п. На него "надеваются" предметы, дающие определённые свойства. Также имеем врага - NPC, обладающего примерно аналогичным набором свойств. Здесь есть одна из "фишек" геймплея - в зависимости от степени изученности данного врага, для игрока данные о нём выводятся с разной степенью детализации, это оказывает влияние на игровые решения. Собственно и всё. Локация - зафиксирована, пока это будет чисто поле, чтобы ещё больше не усложнять.

Итак, на старте рассчитываем начальные параметры обеих сторон, выводим игроку полную инфо о своём герое, а также инфо о противнике, но "отфильтрованную" через степень знания о нём. Рисуем картинку, всякие полоски здоровья и т.п.

Каждая итерация состоит из следующего. Игроку предлагается выбрать одно из действий. Общий список - достаточно большой, но доступность каждого определяется рядом условий и в конечном счёте зависит от свойств персонажа и противника, экипированных предметов и т.п. Причём я различал понятие "возможности" и "доступности". Если не выполняются критерии возможности, то действие не выводится вовсе, а если действие возможно, но недоступно в данным момент, то оно выводится в диалог, но в неактивном виде. Чтобы избежать излишнего "мяса", меню действий я планировал двухуровневое, т.е. для каждого действия есть признак принадлежности к определённой группе.

Когда игрок выбрал желаемое действие, игра должна рассчитать непосредственный результат в виде изменения свойств обоих участников (да, схватки только один-на-один), вывести соответствующие текста, обновить картинку. Далее следует реакция соперника, варианты которой выбираются в ответ на действие игрока из некого списка доступных вариантов, в зависимости от свойств NPC. Здесь игрок уже ничего активно не противопоставляет, только получает инфо. Дальше опять обновляем свойства, картинки, двигаем вперёд игровое время. Затем всё повторяется.

Как-то так, если отбросить все усложнения, которые разумеется, также планируются: всякие бафы/дебафы, проверки на специфические свойства и т.п. Наверное, в текущей версии только возможность смены инвентаря нужно добавить.

ZergMaster 25.10.2017 12:29

рекомендую замечательную статью (MVC, часть 1: про дубовый стол и сиськи), которая должна ответить на некоторые вопросы.
И, да, не стесняйтесь зарисовывать архитектуру снова и снова.

Appleman 25.10.2017 14:18

Цитата:

Сообщение от ZergMaster (Сообщение 1202596)
рекомендую замечательную статью (MVC, часть 1: про дубовый стол и сиськи), которая должна ответить на некоторые вопросы.

А кстати, да, точно. Спасибо большое! Читал этот опус давно, когда совсем ничего ещё не соображал. Тогда подумал, что автору можно какую-нибудь премию за сам стиль изложения присудить. :) Теперь можно с большим знанием дела ещё раз проштудировать.

P.S. и да, кто мой предыдущий пространный пост прочитал, просьба не воспринимать всерьёз мои опусы про ненадобность контроллера. Я его почему-то воспринимал, как реализацию управления со стороны игрока, а перечитав любезно порекомендованную статью об MVC, понял, что сморозил сущую глупость.

Добавлено через 2 часа 46 минут
Начал, пыхтя, продумывать архитектуру в стиле MVC... Возник первый содержательный вопрос.

Планируя Вью, нужно ли различать такие понятия как условно "разметка экрана" и "обновление данных из модели"? У нас есть ряд компонентов, которые, с одной стороны, непосредственно связаны с обновляющимися данными модели (тот же TextField для вывода основного текста), а с другой - сам объект (но не текст внутри) - находится на одном и том же месте от начала и до конца игры. Что с таким делать?

Добавлено через 2 часа 54 минуты
...и ещё вопрос. Я выше жаловался на плохое понимание разницы между "data" и "model". Прокомментируйте, плиз, мои мысли.

Сейчас у меня в пакете data есть класс Character, который содержит все свойства персонажа. Причём они, эти свойства, могут изменяться в процессе игры, что в свою очередь, находит отражение в отображаемом игроку содержании. Правильно я понимаю, что в таком случае, мой Character - уже по этому признаку никакой на фиг не data, а model в чистом виде? А если так, то и создавать его должен главный контроллер в начале работы с добавлением всяких событий на обновление и т.п. так? В этом случае становится вообще непонятно, нужен ли мне класс Character в том виде, в котором он у меня есть...

ZergMaster 26.10.2017 17:28

Я обычно стараюсь не усложнять и проектирую в основном так - сначала создаю сущностную диаграмму - это когда ты программу расписываешь на сущности, объекты и их взаимодействия. Ну. например, в твоем случае, это, насколько я понял, --
персонаж(я), вещи, враг, поле. 4 сущности. Они зарисовываются в виде кружочков. Далее обозначаем отношения - вещи носятся на персонаже и влияют на характеристики. Поле содержит в себе персонажа и врага. Персонаж сражается с врагом. Далее рядом с кружочками выписываем свойства - у персонажа это характеристики (ну там сила, скорость передвижения, вещи(массив)), у врага, видимо, тоже (скорее всего персонаж и враг это два экземпляра одного класса герой, или как минимум наследуются от одного класса, так как имеют много общих свойств). Выглядеть это должно примерно так
https://4.downloader.disk.yandex.ru/...ize=XXL&crop=0
и на всякий случай ссылкой

не стоит сильно заморачиваться и усложнять - наоборот, схема должна быть простой и наглядной, чтобы впоследствии при одном взгляде на нее было примерно понятно. Если интеренсо, почитай про ER-диаграммы.

Далее уже рисуем UML на этой основе. Разделяем сущности на классы, разделяем каждую сущность на модель - описание состояния объекта, его свойства, вьювер - отображение состояния объекта и контроллер - изменение состояния объекта. Важно помнить, что изменяем мы именно состояние, а отображаем уже по факту изменения состояния. Так будет проще потом изменять и меньше вероятности потерять связь с реальностью проектом.

Подитожим. Разделяем программу на объекты, а каждый объект отдельно по модели MVC. Профит.
По крайней мере, примерно так делаю обычно я.

Appleman 26.10.2017 23:54

ZergMaster, спасибо. Общая концепция понятна. Я из того, что ты написал, одну вещь уточнить хотел бы. Если для каждого обособленного объекта делать свою полную триаду MVC, то нужно налаживать какую-то иерархию и взаимодействие между ними. И если с Вью более-менее понятно, то как обрабатывать взаимодействие контроллеров? Вообще, насколько это жёсткое требование? Мне пока придумалось иметь один главный контроллер, свои модели на каждый объект и вьюшки, связанные с ними...

И ещё один вопрос для общего понимания. Если у нас есть несколько сильно отличающихся по механике и интерфейсу режимов игры, например, сам игровой процесс и окно инвентаря, где шмотки перетаскивать, то правильно я думаю, что с т.з. разработки - это фактически 2 независимые "игры" со своим MVC?

ZergMaster 27.10.2017 00:04

Appleman добавил картинку в прошлый пост. Куда-то она делась.
Ну да, иерархия. Но всегда есть главный вьювер, главная модель и главный контроллер. У меня как правило они вложены друг в друга по принципу деревьев - в основном для читабельности.
Ты начни с того, чтобы сделать одну главную модель, одну главную вьюху этой модели и один контроллер. А дальше уже будешь добавлять по мере необходимости.

Добавлено через 10 минут
На самом деле, конечно, совсем не обязательно делать для каждого объекта свою триаду. Я бы сказал -
не надо. Можно для группы схожих объектов. Или вообще по одной на всю программу (что бывает тоже редко, так как получается слишком много кода в одном месте.. удобно его разложить по полочкам все-таки). А может модель MVC реализовываться вообще только для части программы - где это удобно. Но это все придет в будущем, а пока лучше делать по одной главной. Тут нет каких-то особых правил. Главное - изучить общую концепцию на собственном опыте и в будущем применять его для удобства своего и своих близких (что тоже важно, чтобы читающий за тобой программист не проклинал тебя слишком сильно)

Wolsh 27.10.2017 01:20

Цитата:

...и ещё вопрос. Я выше жаловался на плохое понимание разницы между "data" и "model".
В MVC нет такой категории "data". Данные бегают туда-сюда постоянно. Вью забирает и отображает данные из Модели, но она же выступает иногда поставщиком, как ни крути. Модель отличается от тупого хранилища данных тем, что выполняет так же и их обработку, для чего и обладает Логикой. В некоторых интерпретациях MVC, которые насочиняли бывшие РНР-шники, Модель является аналогом Базы Данных на сервере, а вся логика сосредоточена в Контроллере (аналог РНР-скрипта; соответственно клиент — браузер пользователя — это Вью). Для ActionScript такое деление внутри одного файла(!) бессмысленно. Модель хранит данные и она же с ними работает, это логично. Контроллеры обеспечивают поставку новых данных от Вью (если нужно, то и с сервера), включая управление (действия игрока).
В разных режимах приложения одни и те же действия пользователя должны интерпретироваться по-разному. Яркий пример — нажатия клавиш. Во время игры клавиши со стрелками вверх/вниз могут передвигать игрока по карте, а когда на экране Меню — обеспечивают переход между вариантами; в окне настроек могут менять громкость звука и т.п. Удобно пихать эту интерпретацию в контроллеры: имеется ввиду, что для каждого состояния программы существует свой контроллер, который просто не слушает то что не надо и вызывает правильные методы в Модели в ответ на то, что слушает. А поскольку "состояния" так или иначе завязаны на то, что показывает сейчас Вью — Меню, Инвентарь, Диалог, Настройки, Видеозаставка, Поединок — удобно все их делать отдельными Вью со своими контроллерами. А вот насчет множества Моделей это перебор. Ну, или они могут быть как части одной Модели: ты можешь считать что твой Character это CharacterModel, смысл от этого не очень поменяется, разве что возникнет вопрос "а как быть с CharacterController? Что он должен делать?" )) При разделениях Моделей на множество важно осознавать, насколько они зависимы друг от друга всмысле обмена данными. В режиме поединка тебе постоянно надо иметь данные о персонаже и оппоненте "вместе", да еще и об их аммуниции; а вот глобальное меню игры здесь вроде как совсем не при делах — оно может быть отдельной триадой, так как его данные абсолютно обособлены.

Appleman 27.10.2017 16:15

Спасибо, очень доходчиво.

Цитата:

А вот насчет множества Моделей это перебор. Ну, или они могут быть как части одной Модели: ты можешь считать что твой Character это CharacterModel, смысл от этого не очень поменяется, разве что возникнет вопрос "а как быть с CharacterController? Что он должен делать?" )) При разделениях Моделей на множество важно осознавать, насколько они зависимы друг от друга всмысле обмена данными. В режиме поединка тебе постоянно надо иметь данные о персонаже и оппоненте "вместе", да еще и об их аммуниции; а вот глобальное меню игры здесь вроде как совсем не при делах — оно может быть отдельной триадой, так как его данные абсолютно обособлены.
Вот я как раз поэтому и спрашивал выше, насколько жёстким является требование создавать полную триаду MVC на каждый из элементов общей конструкции. Я как рассуждал. Добавляем класс GameModel - такой "стол", на котором считаем всю игровую логику. Но при этом если в классе Character "живут" и обновляются по некоторым установленным правилам значимые для механики и одновременно отображаемые для игрока свойства, то зачем мне их тянуть в GameModel, если я могу посчитать их прямо в классе Character и там же создать событие, которое подхватит CharacterView, чтобы отразить изменения, произошедшие именно с этим персонажем. Получается, игрок, взаимодействуя с GameView, выбрал, чего делать. Контроллер это принял и послал команду в GameModel, а Game Model обновилась сама, плюс обновила экземпляры Character для героя и врага. Все трое отправили события, чего показать. Вроде, нормальная схема, как мне кажется. Один главный контроллер, плюс по отдельной Model и View для "стола" и персонажей.

Цитата:

В MVC нет такой категории "data". Данные бегают туда-сюда постоянно.
Да, тут всё понятно, закрыли тему :) Дату отправляем храниться в XML или "статические" классы, откуда будем по необходимости вытягивать.

Ещё с позволения несколько вопросов. Я пока единственное, что реально написал из будущей MVC - это все три класса со ссылками друг на друга "по классике", плюс в Model создал метод setup, который рассчитывает стартовые значения свойств и т.п. Хотел сделать примерно то же самое с GameView, но возник вопрос. Опять на счёт иерархии отображения :). В методе View.setup я хотел сразу создать основные экранные объекты, начиная с моей любимой области для вывода текста. Для этого изготовил новый класс MainTextArea, расширяющий Sprite - контейнер. Внутрь воткнул простенький прямоугольник (в дальнейшем будет окно с "красивой" рамкой), а поверх него в том же контейнере - объект TetxField для вывода собственно текста. GameView добавил его в список отображения, на экране окно появилось, слава аллаху. Решил сразу попрактиковаться и написать метод, который по получении соответствующего события от Модели будет выводить текст. Вот тут началась фигня. Ибо записать напрямую MainTextArea.text я не могу, т.к. это Sprite. Делать область вывода как "голый" TextField совсем без контейнера как-то тоже не хочется - мало ли чего в ней ещё потребуется наворотить... Прикинул вариант написать собственный метод для GameView, получающий по событию текст, чтобы потом "отправить" его в MainTextArea...
И тут мне пришло в голову красивое в своей простоте решение. А может быть пусть сам экземпляр MainTextArea слушает события от модели и выводит текст, который его попросят? Плюс в том, что во-первых, мы сокращаем длину канала взаимодействия, т.к. данные от модели не нужно будет "протаскивать" через главный Вью, а во-вторых, планируемые функции подпадают под ответственность класса MainTextArea - выводить тексты. При этом, замечу, поскольку экземпляр MainTextArea - это креатура GameView, значит последний при необходимости может изменить или удалить его, т.е. иерархия не нарушается. Корректное это решение или всё-таки иерархия нарушается? Прокомментируйте, пожалуйста.

И второе. Во всех примерах MVC, которые я видел, весь код модели был помещён непосредственно в класс Model. В результате даже для простеньких игрушек в нём набиралось прилично разных функций. А если проект будет покрупнее, то там будет просто "мясо"... Верно я понимаю, что "главная" модель должна выглядеть как такой распределитель обязанностей, который получает команды от контроллера и "раздаёт" их разным специализированным классам для выполнения конкретных вычислений?

ZergMaster 27.10.2017 17:41

Да, все верно, главная содержит в себе глобальные параметры и приватные экземпляры дочерних моделей, доступ к которым осуществляется по геттерам.

Wolsh 27.10.2017 18:29

Цитата:

Вот тут началась фигня. Ибо записать напрямую MainTextArea.text я не могу, т.к. это Sprite.
Ну, не спрайт, а MainTextArea)) Так что забабахать ему сеттер .text вполне в твоих силах. Давать какому-то приодевшемуся текстфилду личный телефон топ-модели я бы поостерегся. Хотя звучит все красиво, но есть здесь такой же нюанс, как с объектами, которые сами себя добавляют и сами себя удаляют — а этот сам себя обновляет. Может быть и нет никакой опасности, но ведет он себя в стане Вью как шпион, подчиняясь только модели напрямую. Дело в том, что у вью могут быть и другие реакции на обновление текста, это всетаки не часики в углу экрана... В результате, пытаясь сэкономить, ты придешь к тому что Модели придется отправлять 16 событий что у нее поменялось то, поменялось сё; а на деле поменялся стейт и Вью могла забрать его одним объектом и распихать по своим детям кому текст кому иконку.

Appleman 31.10.2017 01:15

Цитата:

Сообщение от Wolsh (Сообщение 1202612)
Ну, не спрайт, а MainTextArea))

Ну раз ты придрался, я тогда аккуратно оспорю. А разве с т.з. наследования, наш MainTextArea не "is Sprite"?

Цитата:

Так что забабахать ему сеттер .text вполне в твоих силах.
Точняк, спасибо. Туда и формат сразу удалось прикрутить. Теперь получается вообще красиво: модель "знает", к чему относится тот или иной кусок текста и отражает это в строковом id, а вьюшка подхватывает из event.target этот идентификатор и по нему забирает нужный формат, который хранится в третьем месте.

Просьба прокомментировать моё решение на счёт окончательной "разблюдовки" MVC. Поскольку, я напоминаю, у меня текстовый квест, то основных "интерактивных" областей на экране всегда три: область вывода текста, "уголок" героя, где изменяющийся портрет, всякие полоски здоровья, баффы/дебаффы и т.п. и такой же "уголок" соперника. Я рассудил так. Коль они у меня всю дорогу находятся каждый на своём месте, то хрена ли я буду лепить отдельные вьюшки! Это же не RTS, где враги прут пачками и каждый экземпляр нужно отрисовать... И оставил я один контроллер и одну вьюшку.

И небольшой практический вопрос, который меня поставил в тупик. Довольно часто встречается ситуация, когда нужно вывести порцию текста игроку, что-то по мелочам обновить, а затем, когда он осознает увиденное и кликнет, выводить текст и обновлять экран дальше. Естественно, чтобы он сообразил, что игра не зависла, а ждёт от него действие, я сделал класс ClickToContinue, который выводит соответствующее сообщение на выбранном языке, красиво мерцающее на видном месте. Но вот внедрение его в мою MVC вызвало затруднение. Вроде как сам клик ловить в контроллере - не дело, это прерогатива вьюшки. Значит, контроллер "дёргает" вьюшку и говорит "давай тормози до клика". Вьюшка берёт под козырёк и вываливает на сцену мой красивый ClickToContinue. А вот потом что? Что делать с кликом, который вьюшка поймала?

ZergMaster 31.10.2017 16:24

Цитата:

Сообщение от Appleman (Сообщение 1202641)
Значит, контроллер "дёргает" вьюшку и говорит "давай тормози до клика". Вьюшка
берёт под козырёк и вываливает на сцену мой красивый ClickToContinue. А вот потом что? Что делать с кликом, который вьюшка поймала?

По идее, по mvc контроллер дергает не вьюшку, а модели говорит, что сейчас у нас пауза (или изменяет какой-либо параметр сеттером в модели), которая, в свою очередь, диспатчит событие "торможение_до_клика". А вьюшка слушает модель и, в частности, это событие. Вьюшку же, в свою очередь, слушает контроллер, который подписан событие что-то вроде "продолжить_после_паузы". По этому событию контроллер меняет что-то в модели, которая диспатчит событие, которое слушает вьювер и так далее.

Может это и не совсем правильно, но у меня для этих целей обычно служит такой специальный классик-объектик, который называется как-нибудь на вроде "информационное окно", или "окно сообщений". Оно может инициализироваться на главной сценей, принимая в себя верхний слой. А потом, в любом месте кода, я просто, обращаясь к этому классу, дергаю функцию "показать окно", передавая её label - текст, который нужно вывезти, и, если нужно, индентификатор картинки и т.п. А иногда это просто обычный класс-Sprite, и в любом месте кода я создаю новый его экземпляр, передавая в конструктор label и тут же добавляю его на сцену. А все остальное проиходит внутри этого класса и он ничего не знает о том, откуда его вызывают и кто, и зачем. Все, что он умеет - это отобразить графику, встроенную в него - окно с полупрозрачным фоном, отобразить текст, пришедший в конструктору, и удалить сам себя. Да, такой подход часто ругают, но я люблю удаляющие сами себя классы, это очень удобно)) У него просто есть приватная функция remove(), которая вызывается при нажатии на крестик либо ВНЕ окна, и которая выглядит таким образом:
Код AS3:

private function remove():void
{
      this.parent.removeChild(this);
}

Добавлено через 2 минуты
то есть, в моем случае, вся идея сводится к тому, чтобы иметь некий классик, который вообще ни сном не духом о том - куда его добавляют, какая там невероятная космическая архитектура реализована в приложении и тому подобное - он просто существует сам в себе. Просто объектик со своей внутренней логикой. И это позволяет его применять в любом проекте, просто создав его экземпляр, передав ему текст и добавив на сцену. Все остальное же он делает сам.

caseyryan 31.10.2017 19:51

Цитата:

Да, такой подход часто ругают, но я люблю удаляющие сами себя классы, это очень удобно))
Экземпляры классов, если быть точнее. Классы сами себя не удаляют.
И в этом методе есть очень большая проблема. Отсутствует проверка на наличие parent. Если экземпляр не будет никуда добавлен, то при вызове всё сразу отвалится с null pointer исключением.
Лучше уж так:
Код AS3:

public function removeFromParent():void
{
      if (parent) parent.removeChild(this);
}

п.с. Я тоже не вижу в этом подходе ничего плохого)

Godwarlock 31.10.2017 20:33

А мне вот последнее время перестает нравится суть контроллера, который слушает вьюшку, потом вносит изменения в модель, а та в свою очередь возвращает изменения во вью. Как то много лишних действий, нет?

ZergMaster 31.10.2017 20:58

caseyryan да, совершенно верно, забыл указать это. Так как зачастую выходит, что кроме удаления себя со сцены происходит еще некоторое количество действий, то этот метод может выглядеть так
Код AS3:

public function removeFromParent():void
{
      if (!parent)
                return;
 
      //некоторая логика
      parent.removeChild(this);
}

Добавлено через 7 минут
Godwarlock так у нас же может быть множество вьюшек. И таким образом, когда контролер изменяет модель, они ничего не знают о том - сколько вьюх и вообще что там творится снаружи, а модель просто рассылает события. А потом уж кто их поймает - тот и использует. В этом то вся и красота mvc, и если такой системы не будет, то да, это будет уже не mvc. Такая модель особенно актуальна, когда у нас неограниченное и, быть может, даже неизвестное количество вьюшек, которые должны отображать одну и ту же инфу.

Wolsh 31.10.2017 21:18

Цитата:

А разве с т.з. наследования, наш MainTextArea не "is Sprite"?
Не всё так однозначно. Он еще и "is Object" кроме прочего. Но Обжекту ты бы смог добавить .text, а спрайту не можешь, а MainTextArea cнова можешь. Чудеса.

По поводу click to continue — а Модели то есть вообще дело до этого? До того что пауза. В ней что-то происходит? Или она составила кадр, известила вьюху и спит пока не скажут давай следующий кадр?

Appleman 31.10.2017 21:56

Цитата:

Сообщение от Wolsh (Сообщение 1202667)
По поводу click to continue — а Модели то есть вообще дело до этого? До того что пауза. В ней что-то происходит? Или она составила кадр, известила вьюху и спит пока не скажут давай следующий кадр?

Второе. Ничего не происходит.

Вообще вижу тут выше дискуссию на счёт нужности контроллера. Испытываю примерно те же ощущения, правда, пока особо не выпендривась, т.к. нужно хоть что-то рабочее написать, прежде чем делать выводы.
Но вот взять, например, мою типичную ситуацию с выводом диалога выбора дальнейших действий игроком. По задумке модель берёт весь массив действий и, используя свою логику, фильтрует его в зависимости от состояния игровых параметров. Отфильтрованный массив по событию забирает вью, чтобы вывести игроку. В контроллере висит слушатель события выбора действия. Когда игрок делает свой выбор, вью принимает его и отправляет в контроллер. Контроллер же, не приходя в сознание, отправляет идентификатор действия обратно в модель (он ваще не понимает, что это за фигня к нему прилетела!), чтобы там это действие было обработано. Спрашивается, на фига тащить всё через контроллер, если можно сразу из вьюхи принять в модель?

ZergMaster 01.11.2017 00:03

Цитата:

Спрашивается, на фига тащить всё через контроллер, если можно сразу из вьюхи принять в модель?
для порядку!! =)
На самом деле, конечно, можно и напрямую. Да порядка не будет тогда. По сути, это как и с названиями функций и переменных - не обязательно их называть как-то понятно, можно просто "а", "b", "c". Не обязательно соблюдать конвенцию формата кода, не обязательно избегать комментариев, делая сам код читабельным без них. Не обязательно вообще писать в классах - зачем, когда можно писать в нужном кадре нужного символа?))
Все это не обязательно и, в принципе, для того, чтобы написать пару скриптов или пару кнопок - это не нужно.
Но как только проект разрастается, ты бы хотел знать, что моделью у тебя управляет только контроллер и никак иначе. Как только проект становится большим, тебя успокаивает уверенность в том, что вьюха ничего не знает о контроллере и слушает только модель. Ты знаешь, что у тебя не остается рудиментарных вызовов не пойми откуда не пойми чего. Ты уверен, что никакая нечисть не забредет а мир твоего кода. Хотя нет, конечно, неизбежно забредет, но тебе будет гораздо проще её найти, если будет порядок. Ты будешь четко в курсе кто чем управляет и кто что использует. Это удобно. Только лишь для этого, как мне кажется.
Ну и, конечно, полиморфизма ради.=)

Wolsh 01.11.2017 00:05

У Модели нет ссылки на Вью.

Не, я понимаю что у тебя при таком ответе первая мысль будет "ну так давай дадим ей ссылку".
Тут вопрос то в том, MVC или не MVC. Так же как заявление Godwarlock выше. Если ты не понял, зачем ТАК сделано в MVC, то ты просто еще не понял MVC, тут нечего стыдиться)))
Вообще, "Правильно" всегда включает в себя "Чтобы работало", но "Чтобы работало" вовсе не всегда "Правильно". MVC всегда кажется избыточно сложным, кажется что его правила придуманы так, чтобы все усложнить на ровном месте. И ключевое слово здесь — "на ровном месте". Знаешь пословицу — "гладко было на бумаге, да забыли про овраги"? Реальность такова, что место нифига не ровное. Последнее, о чем думает молодой программист, это ошибки, а тем более СИТУАЦИИ, в которых что-то может пойти не так. А еще есть такая мерзкая тема, как защита от взлома. Я вобщем хотел сказать, что пока для тебя ВСЁ приложение умещается в задачу "послать событие вот тут", место кажется ровным и понятным. И начинать с этого вполне нормально, если что. Если MVC непонятно, забей на него до поры до времени. Если амбиции не позволяют, штурмуй дальше. Сразу скажу: вся красота и опрятность MVC проявляется, когда написано полсотни классов хотя бы по 300 строк кода и архитектура типа "всё в кучу" уже тупо не помещается в голове.
У Модели нет ссылки на Вью, потому что Вью может заменяться в любой момент.
У Модели нет ссылки на Контроллер, потому что Контроллеры могут заменяться в любой момент.
Чтобы об этом помнить, надо как минимум представлять себе СИТУАЦИИ, когда Вью и Контроллеры должны заменяться. Если в твоей голове по отношению к этому приложению нет идей таких ситуаций, у тебя не будут возникать подобные мысли, верно? И тогда сама идея MVC становится какой-то муторной и бессмысленной. Это только первое положение MVC само собой моментально укладывается в голову — что есть Модель которая все расчитывает и есть Вью которая всё отображает. Потому что это естественное положение вещей. Но дальше в MVC еще много чего, что так вот сходу на голову не налазит. Это нормально.

Appleman 01.11.2017 18:03

Цитата:

Сообщение от Wolsh (Сообщение 1202674)
Чтобы об этом помнить, надо как минимум представлять себе СИТУАЦИИ, когда Вью и Контроллеры должны заменяться. Если в твоей голове по отношению к этому приложению нет идей таких ситуаций, у тебя не будут возникать подобные мысли, верно? И тогда сама идея MVC становится какой-то муторной и бессмысленной. Это только первое положение MVC само собой моментально укладывается в голову — что есть Модель которая все расчитывает и есть Вью которая всё отображает. Потому что это естественное положение вещей. Но дальше в MVC еще много чего, что так вот сходу на голову не налазит. Это нормально.

Я как обычно с конца отвечать начну :) Мне идея MVC не кажется муторной и бессмысленной. Сама по себе специализация компонентов M, V и C и их относительная независимость друг от друга - уже прекрасный подход, облегчающий планирование программы. Плюс сразу легко рисуются ситуации, когда одна модель "обслуживает" несколько вью. Тут тоже преимущества налицо! Так что отказываться от неё я точно не собираюсь. Просто пока у меня контроллер почти без дела простаивает, вот и подумалась такая крамола.

Цитата:

У Модели нет ссылки на Вью, потому что Вью может заменяться в любой момент.
У Модели нет ссылки на Контроллер, потому что Контроллеры могут заменяться в любой момент.
Проверил, действительно нет. У Контроллера есть ссылки на Вью и Модель, плюс есть ссылка у Вью на Модель, это правильно? И в связи с этим ещё один очень важный вопрос. С т.з. MVC подхода, нормально ли это, когда например, Контроллер напрямую "дёргает" модель, вызывая её публичный метод, или нужно только события использовать для обмена информацией между элементами?

Wolsh 01.11.2017 21:34

Цитата:

Просто пока у меня контроллер почти без дела простаивает
Это нормально. Был даже такой афоризм у борца с "Толстыми Тупыми Контроллерами" — "Лучший контроллер — пустой контроллер". Это старая холиварная тема, о причинах я уже как-то упоминал: БазаДанных - сервер - клиент. Нам такой MVC внутри одной .swf не нужен.

Цитата:

С т.з. MVC подхода, нормально ли это, когда например, Контроллер напрямую "дёргает" модель, вызывая её публичный метод, или нужно только события использовать для обмена информацией между элементами?
У Модели нет ссылки на контроллер. Это значит, что она не может подписаться на события от Контроллера. Модель это игра в себе, она может жить и работать вообще безо всего. Это мозг. Контроллер это нервы, а Вью — мыщцы. Во время сна мозг не получает информации извне, но прекрасно живет и смотрит собственные картинки.
Если тебя смущает доступность методов Модели контроллеру, то ты на верном пути. Здесь обычно всплывает тема Интерфейсов, то есть контроллер может иметь ссылку на Модель не как на тип (класс) Модель, с полным доступом ко всем ее пабликам, а только на Интерфейс, в котором перечислены только те методы, которые можно дергать ДАННОМУ контроллеру.
Точно так же Вью может иметь ссылку на Модель как на Интерфейс, в котором перечислены только те геттеры, которые позволено дергать ДАННОЙ Вью. И она никаким образом не узнает о существовании каких-то еще пабликов у этой Модели. Это, в частности, важный момент защиты данных, ибо Вью у нас душа-на-распашку, заходите люди добрые, берите что хотите — до любого визуального объекта можно добраться через дерево Дисплейлиста, каким бы "приватным" он не был объявлен. Но в Модель никто не имеет права соваться.

Appleman 10.11.2017 15:34

Друзья! Двинулся дальше в разработке и серьёзно застрял (как обычно) :) Need help. Я уже раньше поднимал тему формулировки условий, но только теперь практически дошёл до этого момента. Итак, имеем по-прежнему мини-игру - схватку один-на-один в жанре текстового квеста, хотя для обсуждаемого вопроса это в данный момент не принципиально. В каждую фазу игрок выбирает одно из доступных действий. Мне видится следующая реализация. Планирую создать отдельный класс действия (да, я помню, что в отсутствие изменяемых свойств вполне вероятно, их удобнее будет реализовать статической таблицей, но пока точно удобнее классом), где "живут" значимые для игровой механики свойства. Будучи выбранным, нужный экземпляр отправляется в качестве аргумента в метод-обработчик модели, где будут обрабатываться его результаты. Но до этого этапа мне не дойти. Проблема в том, что условия доступности разных действий сильно варьируются и вообще достаточно разнообразны сами по себе. Например, чтобы действие было доступно для выбора, может одновременно требоваться определённый пол или раса персонажа, значение свойства или навыка, определённый предмет экипировки в наличии. А другое может иметь в числе условий значения каких-то свойств противника и, например, время суток. Естественно, все значения, фигурирующие в числе условий - это свойства тех или иных игровых объектов. Вопрос, как всё это на абстрактном уровне "уложить" в код. Я уверен, что данный вопрос многократно решён. Спасибо.

ZergMaster 10.11.2017 16:29

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

caseyryan 10.11.2017 17:28

Цитата:

Вконтакте api, например, помню, была интересная реализация проверки разрешений через 8ричные числа или как-то так. Может, кто подскажет.
Забавное название) На самом деле это называется "битовая маска"

п.с. Небольшой офтоп, но эта тема сама по себе уже превратилась в текстовый квест) Называется "догадайся о чем спрашивает автор". Слишком много текста. Вопросы надо как-то по-лаконичнее формулировать

ZergMaster 10.11.2017 17:51

Цитата:

Сообщение от caseyryan
тема сама по себе уже превратилась в текстовый квест) Называется "догадайся о чем спрашивает автор"

+1 =)) тоже читаю посты автора, как ребусы)) Ну ничего, сейчас придет Wolsh и все подроообненько расскажет))

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

Wolsh 10.11.2017 19:35

Не, ну я тоже могу только строить предположения)) Мне видится так, что на сцене нарисованы два чувака — Вражина и Герой, написано последнее действие Вражины (или целый лог действий) типа "Серый Волк плюнул в лицо Красной Шапочке" и дальше список вариантов действий Кра.Ша., один из которых должен выбрать Игрок. Мне вот только непонятно, этот список ИЗНАЧАЛЬНЫЙ, неотфильтрованный, он всегда вообще один и тот же? То есть в нем изначально записаны вообще ВСЕ варианты, возможные в игре? Или он индивидуальный для каждого эпизода "большого" сюжета? Если второе, то получается он должен поставляться вместе с квестом в виде скажем XML, типа
Код:

<action id="000001" name="Ударить ножом в глаз">
        <condition owner="self" prop="hasHand" value="true"/>
        <condition owner="self" prop="hasKnife" value="true"/>
        <condition owner="enemy" prop="hasEye" value="true"/>
        <condition owner="weather" prop="isNight" value="false"/>
        <result chanse="0.8" owner="enemy" prop="hasEye" value="false"/>
</action>

, а в первом случае его можно оформить как Класс, но тогда он должен включать в себя неимоверное кол-во действий и необходимых для них условий?

Appleman 10.11.2017 20:13

Цитата:

Сообщение от caseyryan (Сообщение 1202855)
п.с. Небольшой офтоп, но эта тема сама по себе уже превратилась в текстовый квест) Называется "догадайся о чем спрашивает автор". Слишком много текста. Вопросы надо как-то по-лаконичнее формулировать

Сорри, братва, если непонятно изъясняюсь. Хотя всегда наивно полагал, что с русским языком у меня полный порядок, раз взялся за текстовый квест. :) Хотел было пояснить, но действительно пришёл Wolsh и сделал это вместо меня.

Цитата:

Сообщение от ZergMaster (Сообщение 1202852)
самый очевидный вариант - это иметь в каждом действии функцию "доступность", которая будет сверяться с моделью по всем требуемым параметрам перед тем, как предложить себя игроку.

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

Цитата:

Сообщение от Wolsh (Сообщение 1202858)
Не, ну я тоже могу только строить предположения)) Мне видится так, что на сцене нарисованы два чувака — Вражина и Герой, написано последнее действие Вражины (или целый лог действий) типа "Серый Волк плюнул в лицо Красной Шапочке" и дальше список вариантов действий Кра.Ша., один из которых должен выбрать Игрок.

Да, абсолютно верно. Именно так эта механика и выглядит. Одновременно под действие ещё меняется арт на бэкграунде, портреты героев, health-бары и т.п.

Цитата:

Мне вот только непонятно, этот список ИЗНАЧАЛЬНЫЙ, неотфильтрованный, он всегда вообще один и тот же? То есть в нем изначально записаны вообще ВСЕ варианты, возможные в игре? Или он индивидуальный для каждого эпизода "большого" сюжета?
Принимая во внимание, что в качестве эксперимента я решил написать именно мини-игру, которая впоследствии станет частью "большой", то с т.з. глобальных планов, для мини-игры предусмотрен отдельный перечень возможных действий, которые лишь частично совпадают с действиями основной игры. Но если смотреть исключительно в сегодняшних горизонтах, то это будет перечень всех теоретически доступных действий, из которых в каждой фазе будут выбираться те, которые игрок может совершить.

Цитата:

Если второе, то получается он должен поставляться вместе с квестом в виде скажем XML, типа
Код:

<action id="000001" name="Ударить ножом в глаз">
        <condition owner="self" prop="hasHand" value="true"/>
        <condition owner="self" prop="hasKnife" value="true"/>
        <condition owner="enemy" prop="hasEye" value="true"/>
        <condition owner="weather" prop="isNight" value="false"/>
</action>


О! Вот это уже теплее :) А как от таких формулировок переходить непосредственно к проверке? Парсить с такой логикой, что если owner = self, то обращаться _player[hasHand], а если owner = enemy, то проверяем _eneme[hasEye]. Таким образом?

Цитата:

а в первом случае его можно оформить как Класс, но тогда он должен включать в себя неимоверное кол-во действий и необходимых для них условий?
По всей видимости, мы о разных классах говорил. Мне представляется класс Action, где будут храниться свойства, необходимые для игровых расчётов. Я думал там сделать свойство conditions: Array, где будет храниться похожая на приведённую тобой конструкция. А дальше модель перебирает все действия из массива и пробегается по их условиям. Если все выполняются, то экземпляр включается в диалог выбора, а если нет, то например, можно сделать кнопку неактивной и передать туда на всплывающую подсказку, чего конкретно не хватает, чтобы действие стало доступным.


Часовой пояс GMT +4, время: 19:13.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.