MVC结构中的图形问题

时间:2016-10-30 17:01:50

标签: java model-view-controller graphics

我测试将图形实现为MVC结构,但我有点卡住了。这是我到目前为止所得到的。现在我只想让红球来回反弹。并使用按钮开始启动线程,按钮停止以停止在控制器中运行GameLoop的线程。

但我认为我混合了一点。非常感谢一些反馈!

到目前为止我得到了什么:

GameModell 假设控制弹跳。如果球的位置低于40像素或高于80像素 - 将locationX乘以-1以使球改变方向

GameView 在这里我把标签放在JFrame上。我还想显示按钮开始和停止来控制线程,但我猜他们被 TheGraphics 类中的JPanel隐藏了

GameController 使用ActionListeners启动和停止线程。包含GameLoop

TheGraphics 涂抹球并控制方向

我想我有很多事情都是错的,但这是我现在能做的最好的事情。非常需要一些帮助!

谢谢!

主要

public class MVCgame {

    public static void main(String[] args) {
        GameModel gm = new GameModel();        
        GameView gv = new GameView();
        GameController gc = new GameController(gm, gv);
    }
}

MODEL:

public class GameModel {
    private int multi = 1;


    public void setMulti(int locX) {
        if(locX < 40 || locX > 80) {
            multi = multi * -1;
        }
    }

    public int multi() {
        return multi;
    }
}

查看:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class GameView extends JFrame {

    private JPanel jp = new JPanel();
    private JButton start = new JButton("Start");
    private JButton stop = new JButton("Stop");
    TheGraphics gr = new TheGraphics();

    public GameView() {
        add(jp);
        add(gr);

        jp.add(start);
        jp.add(stop);
        setSize(250, 250);
        setVisible(true);
    }

    public void addListener(ActionListener theListener) {
        start.addActionListener(theListener);
        stop.addActionListener(theListener);
    }

    public JButton getStart() {

        return start;
    }

    public JButton getStop() {
        return stop;
    }

    // GUESS I SHOULD PUT THIS IN THE VIEW???
    public void paintEllipse(Graphics theG) {
        Graphics2D g2d = (Graphics2D) theG;
        g2d.setColor(new Color(255, 0, 0));
        g2d.fillOval(0, 0, 10, 10);
    }
}

控制器:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class GameController implements Runnable {

    GameView gv;
    GameModel gm;
    private Thread thread;
    private boolean running = false;

    public GameController(GameModel gm, GameView gv) {
        this.gv = gv;
        this.gm = gm;
        gv.addListener(theListener);
        start();
    }

    ActionListener theListener = new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == gv.getStart()) {
                start();
                System.out.println("PLAY = ");
            } else if (e.getSource() == gv.getStop()) {
                stop();
                System.out.println("STOP = ");
            }
        }
    };

    public synchronized void start() {
        thread = new Thread(this);
        thread.start();
        running = true;
    }

    public synchronized void stop() {
        thread.interrupt();
        running = false;
    }

    // GameLoop
    public void run() {
        long lastTime = System.nanoTime();
        double amountOfTicks = 60.0;
        double ns = 1000000000 / amountOfTicks;
        double delta = 0;
        long timer = System.currentTimeMillis();
        int frames = 0;

        while (running) {
            long now = System.nanoTime();
            delta += (now - lastTime) / ns;
            lastTime = now;
            while (delta >= 10) {
            // tick();
                delta--;

                // repainting the graphics
                gv.gr.drawer();
                gm.setMulti(gv.gr.drawer());
                System.out.println("gv.gr.drawer() = " + gv.gr.drawer() + " gm.multi() " + gm.multi());
                // I want to use this value in the model to change the direction                
            }
            if (running) {
            }
            frames++;
            if (System.currentTimeMillis() - timer > 1000) {
                timer += 1000;
                System.out.println("FPS: " + frames);
                frames = 0;
            }
        }
    }
}

THE GRAPHICS:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;

public class TheGraphics extends JPanel {

    private int locX = 40;

    public TheGraphics() {

    }

    public int drawer() {
        locX++;

        repaint();
        return locX;
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(new Color(255, 0, 0));
        g2d.fillOval(locX, 30, 10, 10);
    }
}

2 个答案:

答案 0 :(得分:1)

  

GameModell假设控制弹跳。如果球的位置低于40像素或高于80像素 - 将locationX乘以-1以使球改变方向

public void setMulti(int locX) {
    if(locX < 40 || locX > 80) {
        multi = multi * -1;
    }
}

真的很糟糕。您应该始终检查位置方向(符号(速度))。否则,你的对象可能会被困在界外,总是在没有永远离开的情况下改变方向。

除此之外,使用MVC概念在我眼中是过度的,不应该用在这么小的项目中,也不应该用在游戏中。在游戏中,你应该或多或少地把所有三个放在一起。当然你可以,但MVC概念的优点和缺点在很多方面都不符合游戏的需求(可能除了GUI之外)。

你的主循环可能看起来像这样(你已经这样做了,但为什么你的代码中的tick()被注释掉了?):

while (running) {
   update(); // Update all game objects
   paint(); // Paint them all
}

每个游戏对象都有自己的update()和paint()实现。您绝对需要将更新和绘制的逻辑分开,即使它们位于同一个类中。所以这一个:

public int drawer() {
    locX++;

    repaint();
    return locX;
}

是绝对禁止的。

修改(参考您的更新回答)

您正在使用location()方法用于不同目的。根据Java名称约定,您应该根据用途来重命名它getLocation()setLocation()以澄清代码。

(即使这不是真正的MVC,我也允许GameFrame实现ActionListener,而不是将其指定为GameController的变量。)

你应该真正改变的一件事是:

private int locX = 0;

public void location(int loc) {
    this.locX = (int) loc;
}

基本上,您每帧复制位置值并创建未使用的冗余数据。另一个问题是,这可能仅适用于一个变量,但如果稍后向模型添加多个位置会怎样?相反,TheGraphics必须在数据模型的实例上呈现,而不是其值。只要您使用一个GameModel

private GameModel model; // set value once at initialisation

并在paintComponent中呈现其值可以正常工作,但如果要添加多个GameModel(处理GameModel更像GameObjectModel),则需要将其作为参数传递给油漆方法。

public void update() {
    repaint();
}

删除它并尝试不用。从一个地方转发到另一个方法的方法在大多数情况下是一个坏主意,特别是如果它使用不同的名称混淆功能。

            gv.gr.update();
            gv.gr.location(gm.location());

您是先重新绘制图像然后设置位置?基本上,你的游戏一直在后面运行一帧。交换该命令。

            gv.gr.location(gm.location());
            gv.gr.repaint();

会好的(我已经说过location())。

答案 1 :(得分:0)

感谢您的回复!下面是我的代码更新。我认为它仍然令人困惑。现在我的位置在10到80之间来回,但我在渲染方面遇到了一些问题。我该怎么画呢?基本上我觉得我对图形有点困惑。在我添加

之后,现在至少显示它
setPreferredSize(new Dimension(250, 20));

在图形类

拥有这样的图形类是否正确: gv.gr.update(); gv.gr.location(gm.location());

或者我应该使用其他方法吗?

见下我的更新代码:

主要

public class MVCgame {

    public static void main(String[] args) {
        GameModel gm = new GameModel();        
        GameView gv = new GameView();
        GameController gc = new GameController(gm, gv);
    }
}

<强> MODEL

public class GameModel {
    private int locX = 50;
    private int speed = 1;

    public void move() {
        if(locX < 10 || locX > 80) {
            this.speed *= -1;
        }
        locX = locX + this.speed;
    }

    public int location() {
        return this.locX;
    }
}

查看

import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class GameView extends JFrame {

    private JPanel jp = new JPanel();
    private JButton start = new JButton("Start");
    private JButton stop = new JButton("Stop");
    TheGraphics gr = new TheGraphics();

    public GameView() {
        add(jp);
        jp.add(gr);
        jp.add(start);
        jp.add(stop);
        setSize(250, 250);
        setVisible(true);
    }

    public void addListener(ActionListener theListener) {
        start.addActionListener(theListener);
        stop.addActionListener(theListener);
    }

    public JButton getStart() {
        return start;
    }

    public JButton getStop() {
        return stop;
    }
}

<强> CONTROLL

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class GameController implements Runnable {

    GameView gv;
    GameModel gm;
    private Thread thread;
    private boolean running = false;

    public GameController(GameModel gm, GameView gv) {
        this.gv = gv;
        this.gm = gm;
        gv.addListener(theListener);
    }

    ActionListener theListener = new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == gv.getStart()) {
                start();
                System.out.println("PLAY = ");
            } else if (e.getSource() == gv.getStop()) {
                stop();
                System.out.println("STOP = ");
            }
        }
    };

    public synchronized void start() {
        thread = new Thread(this);
        thread.start();
        running = true;
    }

    public synchronized void stop() {
        thread.interrupt();
        running = false;
    }

    // GameLoop
    public void run() {
        long lastTime = System.nanoTime();
        double amountOfTicks = 60.0;
        double ns = 1000000000 / amountOfTicks;
        double delta = 0;
        long timer = System.currentTimeMillis();
        int frames = 0;

        while (running) {
            long now = System.nanoTime();
            delta += (now - lastTime) / ns;
            lastTime = now;
            while (delta >= 100) {
                delta--;
                gm.move();
            }
            if (running) {
                gv.gr.update();
                gv.gr.location(gm.location());
                System.out.println("BALL LOCATION = " + gm.location());
            }
            frames++;
            if (System.currentTimeMillis() - timer > 1000) {
                timer += 1000;
                System.out.println("FPS: " + frames);
                frames = 0;
            }
        }
    }
}

THE GRAPHICS

public class TheGraphics extends JPanel {

    private int locX = 0;

    public TheGraphics() {
        setPreferredSize(new Dimension(250, 20));
    }

    public void location(int loc) {
        this.locX = (int) loc;
    }

    public void update() {
        repaint();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(new Color(255, 0, 0));
        g2d.fillOval(this.locX, 0, 10, 10);
    }
}