0

I am making a game in java using the MVC design pattern (with swing) and I do not know how to make the Controller class separate from the view Class.

until now I have a model that contains all the data, a controller which in charge of all the logic and now I think about how to separate the view.

I have the GameView which extends the Jpanel and has a 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);
}

but on the other hand, I have the GameEngine which in charge of the configuration and run

@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){}

    }
}

the run() method in the engine invokes the paintComponent() method of the view (which for me sounds like the controller --> invokes the viewer) but I find it is not the best way to do that and it is not recommended to invoke the paintComponent() directly.

So I want a clear separation of the controller and the view but I having trouble finding the appropriate way to do that.

Ori Bentov
  • 27
  • 3
  • Never, ever, call `paintComponent` yourself, there's a reason why it's `protected`. Equally, you should never use `getGraphics`. Doing this is simply fighting the paint system which already exists. Instead, you should simply call `repaint` and let the paint system to take care of the rest. – MadProgrammer Feb 08 '20 at 21:08
  • *"clear separation of the controller and the view"* - Ok, if you take the time look at MVC from it's origins, you'll find that it was designed more as a "recommendation" as a way to do something (which at the time was quite complex) rather then a hard and fast rule or paradigm. In general the responsibility of the controller is to coordinate actions between the model and the view, so having your controller schedule a paint pass is within its realm of responsibility - it's reacting to a change in the model (game state) and passing the change on to the view – MadProgrammer Feb 08 '20 at 21:11
  • 1
    One way I think I might do this is to allow your controller / game loop paint an image (like BufferedImage) and then just have the GUI update that image to the screen. That separates things and also makes the actual update in Swing as fast as possible. – markspace Feb 08 '20 at 21:11
  • "If" this still doesn't work for you, you could have a "paint controller" which the game engine talks to, which would then schedule the paint passes on the view. This is important and if you look at the Swing architecture, is similar. A controller can manage other controllers. A view can act as a controller for sub views/controllers – MadProgrammer Feb 08 '20 at 21:13
  • "If" you also need direct control over the painting process, you should consider using a `java.awt.Canvas` and `BufferStrategy`. The take away here is, don't try and force a API or design to utilities a pure M-V-C, but try and utilities MVC to help seperate the areas of responsibly where it's feasible to do so. – MadProgrammer Feb 08 '20 at 21:15
  • 1
    Also if you're determined to use Swing have a look at this: [Low Latency Painting in AWT and Swing](https://pavelfatin.com/low-latency-painting-in-awt-and-swing/) I personally would consider using something like JOGL or LWJGL instead though. – markspace Feb 08 '20 at 21:15
  • See mvc implementation of using a thread to update a swing view [here](https://stackoverflow.com/a/58528000/3992939) – c0der Feb 12 '20 at 11:48

2 Answers2

0

Okay, so you're running into a series of problems.

First

Swing is already a MVC

Second

The form of "MVC" which is taught and discussed today isn't actually how the original intent of MVC was meant to be used. It was considered more of guide line or recommendation than a stead fast rule or paradigm.

The problem is, you end up with "purests" who insist on an absolute separation of all models, controllers and views, in some cases, to the detriment of the API or solution (this when you end up with "massive view controller" problems).

Why? Consider a button. It's a view, it has a model and it can act as a controller.

You don't need a seperate "controller" to control the button. It can register the keyboard and mouse actions it needs to monitor with the underlying system. It can coordinate changes between the model and itself (the view) and it becomes a self contained unit of work.

Seriously, could you imagine having to create a seperate controller every time you created a button or any other UI component ...

When you add the button to another container (view), the button is acting as it's own view/controller. The parent container may then have a larger model and a number of sub views which it is "controlling" or coordinating.

The container may also be controlled via an external controller which is acting to coordinate the changes between the container and the large model, allowing it to be decoupled, but in this case, I would design it in such away so that the implementation logic was decoupled, so that the outter controller didn't care about "how" stuff got done, just that there was a contract in place to allow of the passage of information between these layers.

So, what's the point?

Don't get hung up on trying to produce a "clean" MVC, especially in Swing, you're going to be tearing your hear out.

Also remember, you can have a controller talk to another controller, this allows it to disseminate information where sub controllers don't need or want all the information which might be available to the parent controller. It also allows it to control distinctly different controllers, allowing for a greater decoupling of elements and a nice plug and play approach.

So, how does this help you?

This should provide you with some places to jump off from.

If you don't want the GameEngine to talk directly to the view, you could establish a "rendering" controller. Who's responsibility would be to coordinate changes made by the game engine to the model with the underlying view (ie, schedule a rendering pass).

This would allow the GameEngine to "control" one or more "rendering" controllers and allow for a separation in how the underlying rendering is actual done from the GameEngine

You can do this because a controller can be responsible for other controllers - it's just a coordinator between the model and the next layer (typically a view)

In this example, you could change the rendering system simply by changing the implementation of the "rendering" controller (and if you're using interfaces, this becomes much simpler) without effecting the GameEngie, which, at the end of the day is the benefit of following this type of design - separation of responsibility through de-coupling.

Passing thoughts...

  • Don't, ever, call paintComponent directly. There is a reason why it's protected
  • Don't use getGraphics. This isn't how custom painting is done in Swing. getGraphics is at best just a snapshot of the last paint pass made by Swing. Remember, in Swing, you don't control the painting process, this is taken care of for you, instead, you schedule a paint pass (ie repaint) and let the system take care of the reset
  • If you want/need control over the painting process, then you need to use a java.awt.Canvas and a BufferStrategy instead.
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • For some additional thoughts on MVC architectures, see Martin Fowler's article on the subject: https://www.martinfowler.com/eaaDev/uiArchs.html – markspace Feb 08 '20 at 23:13
0

Solution:
A good way to split data and logic in a Game is using an Entity Component System.
LibGDX's Ashley is what I use.
These Systems help keeping even the largest games relatively clean and understandable.

However there is some initial work required, so for small games you might just be better off with mixing logic and data in classes like Player or Enemy, put all instances of your Game Objects into a List and have a Canvas that paints them all ordered by their Z-Index.

Brief explanation of an Entity Component System:
In an Entity Component System you Entities as your Game Objects. These Entites are like empty objects, but with the capability to take posess an indefinite amout of Components (one per Component Type). These Components give the Entity the ability to be something (like extending an Class, but not limited to one).
So if your Entity has a TextureComponent it is displayable, but without giving it a TransformComponent it cannot yet be drawn at a specific location. If you now also give it a BodyComponent it can have a physical body in your world and can be pushed around. The TransformComponent will be updated everytime the Body is moved.
All the Logic is performed by Systems. The process all Entites with specific Component Types.
A self drawn visualization (I apologize for the looks, I'm not an artist): ECS Visualization

Lahzey
  • 373
  • 5
  • 17