Правильная обработка нескольких представлений в javascript-игре MVC

Я делаю упрощенную пошаговую космическую игру. Игрок взаимодействует с 2D-картой, которая представляет галактику / игровую область. Карта состоит из секторов, и каждый сектор может содержать несколько планет. Игроки могут взаимодействовать с картой, перемещая космический корабль в каждый сектор и колонизируя по желанию.

Итак, часть моего слоя модели выглядит так (я здесь для краткости упрощаю):

function Map() {

    // An array of Sectors
    this.sectors = [];
    . . .
}

function Sector() {

    // Array of Planets
    this.planets = [];

    // Array of player built space stations
    . . .
    this.spaceStations = [];
}

function Planet() {

    // Array of player built buildings
    this.structures = []
    . . .
}

В настоящее время в игре есть два представления: MapView, отображающий видимую вселенную:

function MapView() {

    // @param _map The game Map object
    this.render = function(_map) {

        this.canvas = new Canvas();

        // Camera can examine the Map to find the part that is currently visible on screen
        this.camera = new Camera();
        . . .

        foreach(_map.sectors as sector) {

            LOTS of code and drawImage commands here to . . .
            -ask camera if each sector is in currently in view (no point drawing the entire map)
            -draw each sector background image (star field or nebula or whatever helps make the game look more natural)
            -draw sector boundary lines so we end up with a nice nasty grid over the whole map
            -determine what planet sprite image to draw depending on planet type (i.e ROCK, EARTH, GAS)
            -draw every planet in each sector (the player can watch these planet sprites ‘orbit’ their sun)
            -draw any player created structures in each sector
            -draw any ships currently in each sector
        }
    }
}

и ExamineSectorView, который отображает выбранный сектор:

function ExamineSectorView() {

    // @param _sector A game Sector object
    this.render = function(_sector) {

        this.canvas = new AnotherCanvas();
        . . .
        not-so-much-but-growing-suspiciously-large-amount-of-code here to . . .
        - foreach { draw planets in currently selected sector (selected from Map) }
        - draw space stations
        - you get the idea
    }
}

views

У меня сейчас только один контроллер, MapController. Он создает прослушиватели событий на клавиатуре / мыши, чтобы игрок мог взаимодействовать с картой, то есть:

  • игрок нажимает на сектор в MapView -> просматривать этот сектор в ExamineSectorView
  • игрок использует клавиши со стрелками -> прокрутите карту камеры.

При запуске контроллер вызывает методы на карте (для ее создания), создает представления и запускает таймер setInterval для визуализации представления.

Я бы хотел помочь с моими двумя взглядами.

Я чувствую, что они оба слишком много делают.

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

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

function MapView() {

    this.render = function() {

        this.canvas = new Canvas();

        // Camera can examine the Map to find the part that is currently visible on screen
        this.camera = new Camera();
        . . .

       foreach(_map.sectors as sector) {

           mapSectorView = new MapSectorView();
           mapSectorView.render(sector);
       }
    }
}

function MapSectorView() {

    this.render = function(_sector) {

        this.canvas = new Canvas();

        . . .

       foreach(sector.planets as planet) {

           planetView = new MapSectorPlanetView();
           planetView.render(planet);
       }
    }
}

function MapSectorPlanetView() {

    this.render = function(_planet) {

        this.canvas = new Canvas();

        . . .

       foreach(planet.structure as structure) {

           structureView = new StructureView();
           structureView.render(structure);
       }
    }
}

. . .

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

mapView.render(mapModelObject, mapSectorView, mapPlanetView);

Я тоже не уверен, что это способ решения этой проблемы.

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

Я только что нашел: MVC: модели данных и модели представления

Мне интересно, возможно ли это здесь; создать несколько моделей просмотра, то есть MapViewModel, который содержит все необходимые представления и отображает каждое из них.

Спасибо!


person whoshotdk    schedule 19.01.2014    source источник


Ответы (1)


Я думаю, что метод ViewModel - лучший вариант.

Теперь у меня есть следующее:

UniverseDomainObject (и другие разные объекты домена, например Planet)

  • Создает домен; Звезды, планеты и т. Д. (Здесь нет значений пикселей, т. Е. Позиции находятся в диапазоне 0,0–1,0)

GameController

  • Создает экземпляр UniverseDomainObject
  • Создает экземпляр MainMapViewModel
  • Вызывает MainMapViewModel.prepare (universeDomainObject) в каждом цикле рендеринга игры
  • Слушает (ввод игрока) события из MainMapViewModel и соответствующим образом вызывает методы домена

MainMapViewModel

  • Создает экземпляр камеры, не зависящей от рендерера (по сути, набор методов / свойств для упрощения обработки прокрутки / масштабирования карты)
  • Создает экземпляр MainMapView - передает камеру
  • Вычисляет общий размер карты в пикселях на основе "размера" модели.
  • Устанавливает базовые размеры пикселей для спрайтов (starSizeInPixels, planetSizeInPixels и т. Д.)
  • В каждом цикле рендеринга:

    • Decide which items to draw (are they within camera's view)
    • Вычислить положение / размер каждого элемента для рисования в пикселях; на основе положения от модели, размера пикселя и положения / масштабирования камеры
    • Создайте массив POJO - спрайтов - содержащий рассчитанные позиции / размеры и изображение для каждого объекта.
    • Вызывает MainMapView.render (spritesList, _cameraData)
    • Слушает ввод и публикует соответствующие события в GameController

MainMapView

  • Создает холст с размерами камеры
  • В каждом цикле рендеринга:

    • Iterate passed sprite list and render each image to canvas

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

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

MainMapViewModel совершенно не осведомлен о логике холста в MainMapView. Это довольно круто; Я мог бы переключиться на рендерер OpenGL, возможно, без особых проблем.

Я использую простой метод Observable / Listener для отправки событий GameController; поэтому теперь он более сфокусирован на «игровых» событиях, а не на вводе конкретных кнопок.

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

Буду признателен за любые комментарии, особенно относительно реализации и обязанностей моей MainMapViewModel. Это слишком большая ответственность или нет? Я на заборе.

person whoshotdk    schedule 25.01.2014