2009-07-26 7 views
21

Работа над игрой в Objective-C/Cocoa для OS X, и я закончил прототип так же, как его стоит закончить. Это путаница кода, моя первая игра, но все работает. Я читал лучший способ собрать вещи, и MVC, кажется, имеет наибольший смысл, но я немного смущен.Реализация модели-View-Controller в правильном направлении

С чего начать? С контроллером? Это, кажется, имеет самый лучший смысл для меня, но как это началось? В моем беспорядке прототипа у меня есть все, начиная с init просмотра и оттуда. Я бы просто сделал то же самое для контроллера и поставил то, что нужно в init? Или есть что-то еще, что я могу использовать для этого? Если он запущен с init, то как мне сделать init контроллер?

Как бы настроить игровой мир? В настоящее время я использую два массива, один для мира (Стены, Полы, Двери, Вода, Лава и т. Д.), и один для предметов (я буду добавлять третий для символов). Загружается карта (.plist), а затем объекты создаются и добавляются в массив, к которому она принадлежит. Куда идут массивы? В прототипе они также являются частью представления, поэтому, я думаю, вы могли бы сказать, что я объединил их (View и Controller) вместе. Будет ли создан объект Map для каждой карты? Будет ли объект Карты, содержащий все карты?

Как все это работает вместе? Игрок нажимает клавишу, которая перемещает персонажа в игре. Представление будет обрабатывать вход, правильно? Вы отправите это контроллеру, который будет проверять все (стены, монстры и т. Д.) На карте/других массивах, а затем возвращать результат? Или вы отправите его игроку, который отправится на контроллер, который выполнит все проверки, а затем вернет результат?

Я думал, что у меня было это довольно красиво выложено в моей голове, но чем больше я думаю об этом, тем менее прочными становятся мои идеи и чем больше я смущен. Во что бы то ни стало, не стесняйтесь что-то нарисовать, если считаете, что это будет лучше.

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

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

+0

Я добавляю 300 бонусов. Я хочу проклятый ответ. – Sneakyness

+0

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

+0

Извините, что дал первый ответ, я не имел много времени, но думал, что презентация может помочь :-) – 2009-07-28 21:05:17

ответ

17

Возможно, вас заинтересовала презентация, которую я дал ACCU '09 - «Adopting Model-View-Controller in Cocoa and Objective-C».

С чего начать? С контроллером ? Это, кажется, делает для меня , но как это началось ?

Создайте новый проект приложения Cocoa, и вы увидите, что уже есть класс контроллера, предоставляемый шаблоном - это класс делегата приложения. Теперь посмотрите в MainMenu.xib. Есть экземпляр делегата приложения, и он подключен к выходу delegate объекта «Файл» владельца. В этом случае NSApplication является владельцем файла; это то, что нужно MainMenu для распаковки. Так что это действительно делегат приложения.

Это означает, что у нас есть что-то, что является объектом контроллера, может разговаривать с экземпляром NSApplication и может иметь выходы для всех других объектов в XIB. Это отличное место для настройки начального состояния приложения - то есть для «точки входа» для вашего приложения. Фактически точкой входа должен быть метод -applicationDidFinishLaunching:. Это называется, как только приложение закончило все, что нужно для того, чтобы ваше приложение стало стабильным, работающим состоянием - другими словами, Cocoa рад, что он сделал то, что ему нужно, и все остальное зависит от вас.

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

Если вам нужно отобразить другую информацию в другом представлении - например, если ваш основной вид является основным видом, и вам нужно показать подробный редактор - тогда, когда пользователь скажет вам, что они хотят сделать, вам нужно установить это выше. Они расскажут вам, выполнив какое-то действие, с которым вы могли бы справиться в делетете приложения. Затем вы создаете новый контроллер для поддержки нового представления и рассказываете, где получить требуемую информацию о модели. Вы можете удерживать другие объекты View в отдельном XIB, поэтому они загружаются только тогда, когда они нужны.

Как бы настроить игровой мир? I В настоящее время используются два массива, один для мир (Стены, Этажи, Двери, Вода, Лава и т. Д.), И один для предметов (я добавлю третий для символов). Карта (a .plist) загружена , а затем объекты созданы и добавлены в массив, к которому принадлежит . Куда идут массивы? В прототипе они также являются частью вида, поэтому, я думаю, вы могли бы сказать, что я объединил два (вид и контроллер) вместе. Будет ли объект карты создан для каждой карты? Был бы объект Maps, который содержит все карты ?

Мы можем решить, какие объекты мы моделируем, анализируя ваше утверждение выше - вы можете не осознавать этого, но вы набросали спецификацию :-). Там есть мир, который содержит стены, двери и т. Д., Поэтому мы знаем, что нам нужны объекты для них, и что они должны принадлежать объекту World. Но у нас также есть предметы и персонажи - как они взаимодействуют с миром? Может ли место содержать воду и характер? Если это так, возможно, мир состоит из локаций, и каждое местоположение может иметь стену или дверь или что-то еще, а также может содержать предметы и символы. Обратите внимание: если я пишу его так, кажется, что элемент принадлежит местоположению, а не местоположению элемента. Я бы сказал, что «у кота есть кошка», а не «у кота есть под ним».

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

Теперь некоторые из ваших существительных станут объектами в программном обеспечении, некоторые станут атрибутами других объектов. Глаголы будут действиями (т. Е. Методами). Но в любом случае, будет легче подумать, если вы сначала подумаете над тем, что вы пытаетесь моделировать, а не прыгаете прямо к программному обеспечению.

Как все это работает вместе? Игрок нажимает клавишу, которая перемещает символ в игре. Вид будет обрабатывать вход, правильно? Был бы вы отправите это контроллеру, который будет проверять все (стены, монстров и т. Д.) На карте/другие массивы, а затем вернуть результат? Или вы отправите его игроку, который пойдет на контроллер, который выполнит все проверки и , а затем вернет результат?

Мне нравится следить за политикой «рассказывать, не спрашивать», в которой говорится, что вы приказываете объекту что-то делать, а не просите его предоставить вам информацию для принятия решения. Таким образом, если поведение изменяется, вам нужно только изменить объект, который будет указан. Что это означает для вашего примера, так это то, что представление обрабатывает событие нажатия клавиши (это происходит потому, что они обрабатываются NSControl), и он сообщает контроллеру, что это событие произошло. Предположим, что View получает нажатие «стрелка влево», и контроллер решает, что игрок должен двигаться влево. Я просто сказал бы игроку «двигаться влево» и позволить игроку разобраться, что происходит, когда движение влево означает натыкаться на стену или монстра.

Чтобы объяснить, почему я хочу сделать это так, представьте, что вы добавляете в игру 1.1 возможность плавать игроком. Теперь у игрока есть свойство ableToSwim, поэтому вам нужно сменить игрока. Если вы говорите игроку двигаться влево, то вы обновляете игрока, чтобы знать, что перемещается по воде, в зависимости от того, могут ли они плавать. Если вместо этого Контролер попросит игрока о перемещении налево и примет решение, то Контролер должен знать, чтобы спросить о возможности плавать, и ему нужно знать, что это означает возле воды. Как и любой другой объект контроллера в игре, который может взаимодействовать с игроком, как и контроллер в игре для iPhone ;-).

+0

Мне понравилось, спасибо. Но это все еще не говорит мне, где это начинается. Как запускается контроллер? С awakefromnib зрения? Я должен упустить что-то. – Sneakyness

+0

Хорошо, поэтому я использую applicationDidFinishLaunching, но как? Я использую это так же, как вы бы использовали awakefromnib? – Sneakyness

+0

Re -applicationDidFinishLaunching: это фактически просто функция, которая вызывается обратно Cocoa. Подумайте об этом как о точке входа, как main() в программе на C, за исключением того, что все объекты, созданные вами в XIB, уже могут использоваться. – 2009-07-30 06:32:15

4

Вы должны создать модель для всей игры. Он должен содержать все, что касается игры, кроме взаимодействия с графическим интерфейсом. Представления, с другой стороны, содержат весь материал GUI, ничего не зная о потоке игры.

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

Затем контроллер заполняет промежуток. Он реагирует на ввод пользователя, просит классы моделей выполнить конкретный ход игры и просит взгляды показать новую ситуацию. Ожидается, что контроллер не будет использоваться повторно. Это клей, который держит игру вместе. Контроллер гарантирует, что классы моделей и классы классов остаются незаменимыми и многоразовыми.

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

Всегда помните третье правило проектирования системы X Window: «Единственное, что хуже, чем обобщение из одного примера, вообще не обобщает ни одного примера».

+0

Это то, что я пытался сделать, но я все время зацикливаюсь на том, куда положить вещи , – Sneakyness

+0

Кстати, у вас не будет ни одной модели, просмотра или контроллера для всей игры. У вас будет монстра, монстр, монстр-контроллер ... все должно быть разбито по назначению, в объектно-ориентированном смысле. Ваш массив монстров будет частью модели в некоторой другой модели, такой как модель уровня. –

+3

@emddudley: Обычно у вас есть _one_ model, но она состоит из нескольких классов и/или функций. :-) – vog

0

Для вашей модели модель будет включать в себя такие вещи, как текущая позиция персонажа, количество точек здоровья и другие значения, которые включают в себя «состояние» игры. Он уведомлял бы мнение всякий раз, когда что-то менялось, чтобы представление могло обновляться. Представление было бы просто кодом, необходимым для отображения всего пользователю. Контроллер отвечает за реагирование на ввод пользователя и, при необходимости, обновляет модель.

Контроллер является центральным компонентом в этом, и это то, что должно создать экземпляр модели и просмотреть.

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

Вы ошибаетесь, что большинство людей не проектируют. Возможно, большинство любителей, или, возможно, люди, которые задают больше всего вопросов в Интернете, но никто не работает над проектом, который даже несколько изощрен. Профессиональные программисты имеют опыт разработки программного обеспечения; без него они буквально не смогут выполнять свою работу (написать программное обеспечение, чтобы сделать X).

Ваша игра звучит так, как будто она достаточно сложна, чтобы гарантировать архитектуру, подобную MVC. Есть несколько случаев, когда часть программного обеспечения достаточно проста, чтобы MVC переборщил и излишне усложнил ситуацию, но порог использования MVC довольно низок.

+0

Да, я решил написать Roguelike для своей первой игры/программы. Оказывается, это был трудный выбор, но я лично считаю, что это правильный выбор. – Sneakyness

0

Возможно, эта статья Introduction to MVC design using C# полезная. Хотя пример приведен в C#, этот принцип должен применяться.

+0

Это все еще ничего мне не говорит о том, что каждый начинает все. Или даже как начать все. – Sneakyness

7

Кажется, что ваше основное недоумение - это то, как вещи создаются во время запуска приложения. Многие люди застряли на этом, потому что кажется, что происходит что-то волшебство, но позвольте мне посмотреть, смогу ли я сломать его.

  1. приложение запускается пользователем
  2. функции
  3. С главной() называется
  4. Основной() вызывает NSApplicationMain()
  5. NSApplicationMain() загрузить MainMenu.nib (или другой наконечник, указанный в информации .plist)
  6. Nib загрузки инициализирует все объекты, определенные в бобах (в том числе делегата приложения)
  7. Nib загрузки делает все соединения, определенные в бобах
  8. Nib loading вызывает awakeFromNib для всех объектов, которые он только что создал
  9. -applicationWillFinishLaunching: вызывается в делете приложения.
  10. NSApplication (или подкласс, указанный в Info.plist) инициализируется
  11. -applicationDidFinishLaunching: вызывается в делете приложения.

Обратите внимание, что делегат приложения инициализируется ПЕРЕД классом NSApplication (sub). Вот почему приложение (Will | Did) FinishLaunching: принимает уведомление, а не NSApplication, он является делегатом.

Главным следствием этого является то, что ваши представления создаются для вас через наконечники, и вы создаете контроллеры как побочный эффект запуска nib, поскольку они склонны либо быть объектами уровня корня в наконечниках, либо владельцами файлов nib , Ваша модель обычно создается либо в делегате приложения, либо в виде сингла, который лениво инициализируется при первом доступе к нему.

+0

Делегат приложения не указан в Info.plist и не создается NSApplicationMain(). Он сериализуется в Nib и подключается к NSApp через File's Owner. Он также не может быть отправлен -applicationWillFinishLaunching: до того, как NSApp будет инициализирован, так как приложение еще не существует для функции FinishLaunching. – 2009-08-20 10:21:09

+0

Вы правы, что он не объявлен в Info.plist, я изменю его и переупорядочу соответствующим образом. applicationWillFinishLaunching: абсолютно можно отправить до того, как приложение было инициализировано, и это его документированное поведение: «Отправлено центром уведомлений по умолчанию непосредственно перед инициализацией объекта приложения.« –

+0

Итак, я только что протестировал и выяснилось, что NSApp инициализирован до примененияWillFinishLaunching :, но это не обязательно должно быть правдой, это не документированное поведение, и я вполне уверен, что это не так, как он работал в более старых версиях ОС ИКС. –

Смежные вопросы