【问题标题】:how to seperate view and model in java game?如何在java游戏中分离视图和模型?
【发布时间】:2020-02-08 21:00:00
【问题描述】:

我正在使用 MVC 设计模式(使用 Swing)在 Java 中制作游戏,但我不知道如何将 Controller 类与视图类分开。

到目前为止,我有一个包含所有数据的模型,一个负责所有逻辑的控制器,现在我考虑如何分离视图。

我有 GameView,它扩展了 Jpanel 并有一个paintComonent:

@Override
public void paintComponent(Graphics g){
    super.paintComponent(g);
    Graphics2D g2D = (Graphics2D) g;
   draw(g2D);

}


public void draw(Graphics2D g2D){
    drawComponent(background, g2D);
    drawComponent(arenaController.getArena().getPlane(), g2D);
    drawComponent(arenaController.getArena().getPlayer().getBoat(), g2D);

    ArrayList<PlaneDrop> planeDrops = arenaController.getArena().getPlaneDrops();
    for(PlaneDrop planeDrop : planeDrops){
        drawComponent(planeDrop, g2D);
    }
    g2D.drawString("Life: " + arenaController.getArena().getPlayer().getLife(), 10, 30);
    g2D.drawString("Score: " + arenaController.getArena().getPlayer().getScore(), GAME_WIDTH - 50, 30);
}

但另一方面,我有负责配置和运行的 GameEngine

@Override
public void run() {
    arenaController.init();
    long waitTime = 0;

    Graphics g = this.getGraphics();
   gameViewer.paintComponent(g);

    while(arenaController.isRunning()){
        long startTime = System.currentTimeMillis();

        gameViewer.paintComponent(g);
        update(); // update game
        gameViewer.repaint();
        long endTime = System.currentTimeMillis() - startTime;
        waitTime = (MILLISECOND / FPS) - endTime / MILLISECOND;

        try{
            Thread.sleep(waitTime);
        } catch (Exception e){}

    }
}

引擎中的 run() 方法调用视图的 paintComponent() 方法(对我来说这听起来像控制器 --> 调用查看器)但我发现这不是最好的方法,它是不建议直接调用paintComponent()。

所以我想要明确分离控制器和视图,但我找不到合适的方法来做到这一点。

【问题讨论】:

  • 永远不要自己打电话给paintComponent,之所以叫protected是有原因的。同样,您永远不应该使用getGraphics。这样做只是与已经存在的油漆系统作斗争。相反,您应该简单地调用repaint 并让绘图系统来处理其余的事情。
  • “控制器和视图的清晰分离” - 好吧,如果你花时间看看 MVC 从它的起源,你会发现它的设计更像是“建议”是一种做某事的方式(当时相当复杂),而不是硬性规定或范式。一般来说,控制器的职责是协调模型和视图之间的动作,因此让控制器安排绘制通道在其职责范围内 - 它对模型(游戏状态)的变化做出反应并将变化传递给到视图
  • 我想我可能会这样做的一种方法是让您的控制器/游戏循环绘制图像(如 BufferedImage),然后让 GUI 将该图像更新到屏幕上。这将事物分开,也使 Swing 中的实际更新尽可能快。
  • “如果”这仍然对您不起作用,您可以拥有一个游戏引擎与之对话的“绘制控制器”,然后它会在视图上安排绘制通道。这很重要,如果您查看 Swing 架构,就会发现类似。一个控制器可以管理其他控制器。视图可以充当子视图/控制器的控制器
  • 如果你决定使用 Swing 看看这个:Low Latency Painting in AWT and Swing 我个人会考虑使用类似 JOGL 或 LWJGL 的东西。

标签: java swing model-view-controller jframe


【解决方案1】:

好的,所以你遇到了一系列问题。

首先

Swing 已经是一个 MVC

第二

今天教授和讨论的“MVC”形式实际上并不是 MVC 的初衷。它被认为更多的是指导方针或建议,而不是固定的规则或范式。

问题是,你最终会得到坚持所有模型、控制器和视图绝对分离的“纯粹主义者”,在某些情况下,这会损害 API 或解决方案(当你最终得到“大量视图”时)控制器”问题)。

为什么?考虑一个按钮。它是一个视图,它有一个模型,它可以充当控制器。

您不需要单独的“控制器”来控制按钮。它可以向底层系统注册它需要监控的键盘和鼠标操作。它可以协调模型和自身(视图)之间的变化,成为一个独立的工作单元。

说真的,您能想象每次创建按钮或任何其他 UI 组件时都必须创建一个单独的控制器...??

当您将按钮添加到另一个容器(视图)时,该按钮将充当它自己的视图/控制器。然后父容器可能有一个更大的模型和一些它正在“控制”或协调的子视图。

容器也可以通过一个外部控制器来控制,该控制器用于协调容器和大模型之间的变化,允许它被解耦,但在这种情况下,我会将它设计成这样,以便实现逻辑是解耦的,因此外部控制器不关心“如何”完成这些事情,只关心这些层之间的信息传递有一个约定。

那么,有什么意义呢?

不要拘泥于试图产生一个“干净”的 MVC,尤其是在 Swing 中,你会听得一头雾水。

另外请记住,您可以让一个控制器与另一个控制器对话,这允许它在子控制器不需要或想要父控制器可能可用的所有信息的地方传播信息。它还允许它控制截然不同的控制器,从而实现更好的元素解耦和良好的即插即用方法。

那么,这对你有什么帮助?

这应该为您提供一些可以跳出的地方。

如果您不希望GameEngine 直接与视图对话,您可以建立一个“渲染”控制器。谁负责协调游戏引擎对模型所做的更改与底层视图(即安排渲染通道)。

这将允许GameEngine“控制”一个或多个“渲染”控制器,并允许将底层渲染的实际完成方式与GameEngine分开

您可以这样做,因为控制器可以负责其他控制器 - 它只是模型和下一层(通常是视图)之间的协调器

在这个例子中,你可以简单地通过改变“渲染”控制器的实现来改变渲染系统(如果你使用interfaces,这会变得更简单)而不影响GameEngie,归根结底,遵循这种设计的好处是 - 通过解耦实现责任分离。

传递想法...

  • 永远不要直接致电paintComponentprotected是有原因的
  • 不要使用getGraphics。这不是在 Swing 中完成自定义绘制的方式。 getGraphics 充其量只是 Swing 最后一次油漆通道的快照。请记住,在 Swing 中,您不控制绘制过程,这由您负责,相反,您安排绘制通道(即repaint)并让系统负责重置
  • 如果您想要/需要控制绘画过程,则需要改用java.awt.CanvasBufferStrategy

【讨论】:

【解决方案2】:

解决方案:
在游戏中拆分数据和逻辑的一个好方法是使用Entity Component System
LibGDX 的Ashley 是我使用的。
这些系统有助于保持最大的游戏相对干净和易于理解。

但是需要一些初始工作,因此对于小型游戏,您最好在 PlayerEnemy 等类中混合逻辑和数据,将游戏对象的所有实例放入 @ 987654328@ 并有一个画布,可以按他们的Z-Index 订购它们。

实体组件系统简述:
在实体组件系统中,您将实体作为游戏对象。这些实体就像是空的对象,但能够拥有无限数量的组件(每个组件类型一个)。这些组件使实体能够成为某种东西(例如扩展一个类,但不限于一个)。
因此,如果您的实体有TextureComponent,它是可显示的,但如果没有给它TransformComponent,它还不能在特定位置绘制。如果您现在也给它一个BodyComponent,它可以在您的世界中拥有一个物理身体并且可以被推来推去。每次移动 Body 时,TransformComponent 都会更新。
所有逻辑都由Systems 执行。处理具有特定组件类型的所有实体。
自绘可视化(我为外观道歉,我不是艺术家):

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-09
    • 1970-01-01
    相关资源
    最近更新 更多