我测试将图形实现为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);
}
}
答案 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);
}
}