【问题标题】:Correctly handling multiple views in a MVC javascript game在 MVC javascript 游戏中正确处理多个视图
【发布时间】:2014-02-08 07:27:56
【问题描述】:

我正在制作一个简单的回合制太空游戏。玩家与代表星系/可玩区域的 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
    }
}

我现在只有一个控制器,MapController。它在键盘/鼠标上创建事件侦听器,以便玩家可以与地图交互,即:

  • 玩家在 MapView 中点击一个扇区 -> 在 ExamineSectorView 中查看该扇区
  • 玩家使用箭头键 -> 滚动地图相机。

在启动时,控制器调用 Map 上的方法(生成它),创建视图并启动 setInterval 计时器以进行视图渲染。

我想要帮助的是我的两个观点。

我觉得他们俩都做得太过分了。

它们确实包含逻辑,但它只是与视图相关的东西,并且是渲染所必需的 - 就像一个 10-switch 语句来确定行星类型并绘制适当的图像或行星的“.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/canvas 环境中的关注点分离。

我刚刚发现:MVC: Data Models and View Models

我想知道这是否是一个可能的解决方案;创建一些视图模型,即 MapViewModel,它包含它需要的所有视图并呈现每个视图。

谢谢!

【问题讨论】:

    标签: javascript design-patterns model-view-controller separation-of-concerns


    【解决方案1】:

    我认为 ViewModel 方法是可行的方法。

    我现在有以下内容:

    UniverseDomainObject(以及其他杂项领域对象,如“Planet”)

    • 创建域;恒星、行星等(此处没有像素值,即位置在 0.0 - 1.0 范围内)

    游戏控制器

    • 实例化 UniverseDomainObject
    • 实例化 MainMapViewModel
    • 在每个游戏渲染循环中调用 MainMapViewModel.prepare(universeDomainObject)
    • 从 MainMapViewModel 监听(玩家输入)事件并适当调用域方法

    MainMapViewModel

    • 实例化与渲染器无关的相机(本质上是一组方法/属性,用于更轻松地处理“地图”滚动/缩放)
    • 实例化 MainMapView - 传递相机
    • 根据模型“大小”计算地图总大小(以像素为单位)
    • 设置精灵的基本像素大小(starSizeInPixels、planetSizeInPixels 等)
    • 在每个渲染周期:

      • 决定要绘制哪些项目(它们是否在相机的视野范围内)
      • 计算要绘制的每个项目的像素位置/大小;基于模型的位置、像素大小和相机位置/缩放
      • 创建一个 POJO 数组 - 精灵 - 包含每个对象的计算位置/大小和图像
      • 调用 MainMapView.render(spritesList, _cameraData)
      • 侦听输入并将适当的事件发布到 GameController

    主地图视图

    • 使用相机尺寸创建画布
    • 在每个渲染周期:

      • 迭代传递的精灵列表并将每个图像渲染到画布

    到目前为止,此解决方案运行良好。随着我添加更多视图(即 SectorView),MainMapViewModel 可能仍会变得相当大,但它确实让我可以让 MainMapView 完全保持沉默。

    MainMapView 只是迭代传递的 POJO 列表并将每个“精灵”呈现到它自己的画布上。它还有少量的特定于画布的旋转代码,以使行星围绕它们的恒星运行。

    MainMapViewModel 完全不知道 MainMapView 中的画布逻辑。这很酷;也许我可以切换到 OpenGL 渲染器而没有太多问题。

    我正在使用一个简单的 Observable/Listener 方法向 GameController 发送事件;因此,它现在更加专注于“游戏”事件,而不是特定的按钮输入。

    我正在考虑将 MainMapViewModel 分成不同的类。目前,它为从星系和恒星到卫星和空间站的所有事物准备渲染数据。将这些东西放入单独的类而不是一大块代码中会很好。另一方面,它准备的渲染数据视图正确渲染所需的一切,所以我认为它确实有一个单一的责任。

    我会感谢任何 cmets,尤其是关于我的 MainMapViewModel 的实现和职责。是不是承担了太多的责任?我在栅栏上。

    【讨论】:

      猜你喜欢
      • 2013-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-31
      • 2018-03-20
      • 1970-01-01
      相关资源
      最近更新 更多