Разработка приложений Javascript MVC (холст)


9

Мне трудно понять, как структурировать / спроектировать приложение Canvas, используя MVC-подобный подход в Javascript. Пользовательский интерфейс будет довольно плавным и анимированным, игры довольно упрощенными, но с большим акцентом на анимацию и анимацию. Я понимаю, как MVC работает в принципе, но не на практике. Я погуглил эту ошибку, прочитал очень много и теперь так же растерян, как и когда начинал.

Некоторые подробности об области применения:

  • мультиэкранный игровой фреймворк - в рамках этого фреймворка будут находиться несколько игр. К общим «экранам» пользовательского интерфейса относятся: настройки, информация, выбор сложности, главное меню и т. д.
  • несколько методов ввода
  • общие элементы пользовательского интерфейса, такие как верхняя строка меню на некоторых экранах
  • возможность использования различных методов рендеринга (canvas / DOM / webGL)

На данный момент у меня есть AppModel, AppController и AppView. Отсюда я планировал добавить каждый из «экранов» и прикрепить его к AppView. Но как быть с такими вещами, как верхняя строка меню, они должны быть еще одной триадой MVC? Где и как я могу прикрепить его, не плотно соединяя компоненты?

Является ли общепринятой практикой иметь одну триаду MVC внутри другой? т.е. я могу добавить каждый "экран" в AppView? Является ли "триада" даже принятым термином MVC ?!

Мой разум тает под вариантами ... Я чувствую, что мне здесь не хватает чего-то фундаментального. У меня уже есть готовое и работающее решение без использования подхода MVC, но в итоге я получил тесно связанный суп - логику и представления, и в настоящее время они объединены. Идея состояла в том, чтобы открыть его и упростить смену представлений (например, поменять представление холста на представление на основе DOM).

Используемые текущие библиотеки: require.js, createJS, underscore, GSAP, реализация MVC, выполненная вручную

Любые указатели, примеры и т. Д., Особенно в отношении фактического дизайна вещи и разделения «экранов» на правильные M, V или C, будут приветствоваться.

... или более подходящий метод, кроме MVC

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


1
Похоже, вы, наконец, нашли правильный сайт. Gamedev не хотел вашего вопроса?
Роберт Харви

@RobertHarvey подумал, что это может быть более актуально здесь ... по крайней мере, я на это надеюсь!
wigglyworm

Ответы:


3

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

модель

Это должно быть, где мясо приложения сидит. Он может быть разбит на уровни обслуживания, логического уровня и уровня сущности. Что это значит для вашего примера?

Слой сущности

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

function Location(x,y){
 this.x = x;
 this.y = y;
}
function MineTile(x,y){
 this.flagged = false;
 this.hasMine = false;
 this.pristine = true;
 this.location = new Location(x,y);
}
MineTile.prototype.expose = function(){
 if( this.hasMine ) return false;
 this.pristine = false;
 return this.location;
};

Таким образом, MineTile будет знать свое внутреннее состояние, например, показывает ли он или был проверен ( this.pristine), если это был один из фрагментов, у которого есть mine ( this.hasMine), но не будет определять, должен ли он иметь мой. Это будет до уровня логики. (Чтобы пойти еще дальше в ООП, MineTile может наследовать от общего Tile).

Логический слой

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

var MineSweeperLogic = {
 construct: function(x,y,difficulty){
  var mineSet = [];
  var bombs = 7;
  if( difficulty === "expert" ) bombs = 15;
  for( var i = 0; i < x; i++ ){
   for( var j = 0; i j < y; j++ ){
    var mineTile = new MineTile(i,j);
    mineTile.hasMine = bombs-- > 0;
    mineSet.push(mineTile);
   }
  }
  return mineSet;
 },
 mineAt: function(x,y,mineSet){
  for( var i = 0; i < mineSet.length; i++ )
   if( mineSet[i].x === x && mineSet[i].y === y ) return mineSet[i];
 }
};

Сервисный уровень

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

function MineSweeper(x,y,difficulty){
 this.x = x;
 thix.y = y;
 this.difficulty = difficulty;
 this.mineSet = MineSweeperLogic.construct(x,y,difficulty);
}
MineSweeper.prototype.expose = function(x,y){
 return MineSweeperLogic.mineAt(x,y,this.mineSet).expose();
}

контроллер

Контроллеры должны быть легковесными, по сути это то, что выставляется как клиент модели. Будет много контроллеров, поэтому структурирование их станет важным. Вызовы функций контроллера будут тем, что вызовет JavaScript, основываясь на событиях пользовательского интерфейса. Они должны раскрывать поведение, доступное на уровне сервиса, а затем заполнять или в этом случае изменять представления для клиента.

function MineSweeperController(ctx){
 var this.context = ctx;
}
MineSweeperController.prototype.Start = function(x,y,difficulty){
 this.game = new MineSweeper(x,y,difficulty);
 this.view = new MineSweeperGameView(this.context,this.game.x,this.game.y,this.game.mineSet);
 this.view.Update();
};
MineSweeperController.prototype.Select = function(x,y){
 var result = this.game.expose(x,y);
 if( result === false ) this.GameOver();
 this.view.Select(result);
};
MineSweeperController.prototype.GameOver = function(){
 this.view.Summary(this.game.FinalScore());
};

Посмотреть

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

function MineSweeperGameView(ctx,x,y,mineSet){
 this.x = x;
 this.y = y;
 this.mineSet = mineSet;
 this.context = ctx;
}
MineSweeperGameView.prototype.Update = function(){
 //todo: heavy canvas modification
 for(var mine in this.mineSet){}
 this.context.fill();
}

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

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

var currentGame;
var context = document.getElementById("masterCanvas").getContext('2d');
startMineSweeper.click = function(){
 currentGame = new MineSweeperController(context);
 currentGame.Start(25,25,"expert");
};

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

Для размышления: не позволяйте астронавтам архитектуры пугать вас, Джоэл Спольски


спасибо @TravisJ - я назвал это хорошим объяснением MVC в отношении игр. До сих пор не ясны некоторые моменты, я думаю, как вы говорите, я увяз в нюансах шаблонов, и это мешает мне двигаться вперед. Единственное, что я увидел, было использование this.view.Select () в контроллере - необходим ли этот тип жесткой связи или есть способ развязки дальше?
wigglyworm

@wigglyworm - всегда может быть больше развязки! : D Но на самом деле контроллер должен быть тем, который связывается с моделью, а затем обновляет представление, так что, вероятно, именно в этом месте происходит большинство связей в MVC.
Трэвис Дж

2

Вот что вы уже сделали не так: вы бросили MVC вручную, находясь в состоянии растерянности и не имея MVC под своим поясом.

Взгляните на PureMVC, он не зависит от языка и может стать хорошей платформой, чтобы вымокнуть ногами при использовании MVC.

Его код небольшой и понятный, и это позволит вам настроить его в соответствии с вашими потребностями по мере продвижения.

Начните писать маленькую простую игру с ним, тральщик будет хорошо. Многое из того, что сказал Трэвис Дж, хорошо, особенно о модели. Я бы только добавил, что вам нужно помнить, что контроллеры (по крайней мере, в PureMvc) не имеют состояния, они появляются, выполняют свою КРАТКУЮ работу и уходят. Они знающие. Они как функции. «Заполните сетку, потому что модель изменилась», «Обновите модель, потому что кнопка была нажата»

Представления (Mediators в PureMVC) самые глупые, а Модель лишь немного умнее. Оба абстрагируют реализацию, поэтому вы (контроллеры) никогда напрямую не касаетесь интерфейса или БД.

Каждый элемент вашего пользовательского интерфейса (как, например, в приложении winforms) имеет View (Mediator - вы понимаете, почему этот термин лучше?), Но Mediators также можно использовать для мета-проблем, таких как «Control Color» или «Focus». Менеджер ", которые работают через элементы пользовательского интерфейса. Думайте слоями здесь.

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

Хотя это своего рода обман и требует, чтобы Модель немного знала о том, что там есть, и Посредник понимал, что делать с пакетом данных, но во многих случаях он не позволит вам завладеть мирскими Контроллерами.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.