程序无法正确绘制屏幕

我一直在构建一个简短的程序,它基本上在JPanel上绘制一个太空船,并听取指示程序射击子弹的键。 问题是它甚至没有在屏幕上绘制宇宙飞船或子弹。 我还怀疑KeyBindings可能无法工作,因为这是以前的问题(我可能已经或可能没有修复),但手头的主要问题仍然是我的屏幕没有被绘制。 这是我的代码:

public enum Direction { LEFT, RIGHT, SPACE } import javax.swing.JFrame; public class Main { public static void main(String[] args) { JFrame frame; Ship s1; Shoot shoot; // Set the frame up frame = new JFrame(); frame.setSize(400, 300); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setResizable(false); frame.setVisible(true); // Get some more necessary objects s1 = new Ship(); shoot = new Shoot(s1); frame.getContentPane().add(shoot); s1.setShoot(shoot); // Threads Thread ship = new Thread(s1); ship.start(); } } import java.awt.Graphics; import java.awt.event.KeyEvent; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JPanel; import javax.swing.KeyStroke; public class Shoot extends JPanel { Ship s1; public Shoot(Ship s1) { this.s1 = s1; addKeyBinding(KeyEvent.VK_LEFT, "left.pressed", new MoveAction(true, s1, Direction.LEFT), true); addKeyBinding(KeyEvent.VK_LEFT, "left.released", new MoveAction(false, s1, Direction.LEFT), false); addKeyBinding(KeyEvent.VK_RIGHT, "right.pressed", new MoveAction(true, s1, Direction.RIGHT), true); addKeyBinding(KeyEvent.VK_RIGHT, "right.released", new MoveAction(false, s1, Direction.RIGHT), false); addKeyBinding(KeyEvent.VK_SPACE, "space.pressed", new MoveAction(true, s1, Direction.SPACE), true); addKeyBinding(KeyEvent.VK_SPACE, "space.released", new MoveAction(false, s1, Direction.SPACE), false); setDoubleBuffered(true); } @Override public void paintComponent(Graphics g) { // Draw the ship super.paintComponent(g); s1.draw(g); g.fill3DRect(40, 50, 10, 10, false); } protected void addKeyBinding(int keyCode, String name, Action action, boolean keyPressed) { if (keyPressed) { addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0, false), name, action); } else { addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0, true), name, action); } } protected void addKeyBinding(KeyStroke keyStroke, String name, Action action) { InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW); ActionMap actionMap = getActionMap(); inputMap.put(keyStroke, name); actionMap.put(name, action); } } import java.awt.Color; import java.awt.Graphics; import java.awt.Rectangle; public class Ship implements Runnable { int x, y, xDirection, bx, by; boolean readyToFire, shooting = false; Rectangle bullet; Shoot shoot; public Ship() { x = 175; y = 275; bullet = new Rectangle(0, 0, 3, 5); } public void draw(Graphics g) { // System.out.println("draw() called"); g.setColor(Color.BLUE); g.fillRect(x, y, 40, 10); g.fillRect(x + 18, y - 7, 4, 7); if (shooting) { g.setColor(Color.RED); g.fillRect(bullet.x, bullet.y, bullet.width, bullet.height); } shoot.repaint(); } public void move() { x += xDirection; if (x = 360) x = 360; shoot.repaint(); } public void shoot() { if (shooting) { bullet.y--; shoot.repaint(); } } public void setXDirection(int xdir) { xDirection = xdir; } public void setShoot(Shoot shoot) { this.shoot = shoot; } @Override public void run() { try { while (true) { shoot(); move(); Thread.sleep(5); } } catch (Exception e) { System.err.println(e.getMessage()); } } } import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.util.HashSet; import javax.swing.AbstractAction; public class MoveAction extends AbstractAction { boolean pressed; Ship s1; Direction dir; private Set movement; public MoveAction(boolean pressed, Ship s1, Direction dir) { System.out.println("moveaction class"); movement = new HashSet(); this.pressed = pressed; this.s1 = s1; this.dir = dir; } @Override public void actionPerformed(ActionEvent e) { try { if (movement.contains(Direction.LEFT)) { if (pressed) { s1.setXDirection(-1); } else { s1.setXDirection(0); } } else if (movement.contains(Direction.RIGHT)) { if (pressed) { s1.setXDirection(1); } else { s1.setXDirection(0); } } else if (movement.contains(Direction.SPACE)) { if (pressed) { if (s1.bullet == null) s1.readyToFire = true; if (s1.readyToFire) { s1.bullet.x = s1.x + 18; s1.bullet.y = s1.y - 7; s1.shooting = true; } } else { s1.readyToFire = false; if (s1.bullet.y <= -7) { s1.bullet = null; s1.shooting = false; s1.bullet = null; s1.bullet = new Rectangle(0, 0, 0, 0); s1.readyToFire = true; } } } } catch (NullPointerException ex) { System.out.println("NullPointerException"); } } 

所以,有很多问题……

你最后应该在JFrame上调用setVisible ,这样可以确保组件的布局

您的键绑定问题似乎与您使用移动Set的事实有关,但您实际上从未向其添加任何内容。 相反,你应该检查目标值。

可能还有很多其他的东西。 您的代码紧密耦合,并且没有对状态进行任何集中管理。

我首先要更好地理解模型 – 视图 – 控制器范例,并将代码分成适当的责任区域。

游戏的“数据”应该与游戏的“渲染”分开,游戏的“渲染”应该与关于游戏如何更新的决定分开。

我要提出的是过度简化,旨在激发想法,而不是一个具体的解决方案,因为有很多方法可以实现物理实现……

所以,我们需要的是,游戏中的某个概念,AKA是一个“实体”,实体采用多种forms,但我将专注于可渲染/可显示的实体。 您需要某种模型来负责对游戏的当前状态进行建模,并负责实施规则。 您需要某种视图,它负责渲染模型并响应用户输入。 您需要某种控制器来控制模型和视图的桥接方式。

从一系列定义合同期望的interfaces开始并概述预期元素相互通信的预期方法始终是一个好主意。 我再次采取了一种简单直接的方法,但绝不仅仅是……

 public static enum Direction { LEFT, RIGHT, SPACE } public interface Entity { public void paint(Graphics2D g2d); public Point getLocation(); public void setLocation(Point p); public Dimension getSize(); } public interface GameModel { public Entity[] getEntities(); public void update(Rectangle bounds, Set keys); } public interface GameController { public Entity[] getEntities(); public void setDirection(Direction direction, boolean pressed); public void start(); } public interface GameView { public void setController(GameController controller); public GameController getController(); public Rectangle getViewBounds(); public void repaint(); } 

我们来看看实体的实现。 这个例子有两个实体,一个Player和一个Bullet ……

 public abstract class AbstractEntity implements Entity { private final Point location = new Point(0, 0); @Override public Point getLocation() { return new Point(location); } @Override public void setLocation(Point p) { location.setLocation(p); } } public class Player extends AbstractEntity { public Player(Rectangle bounds) { int x = bounds.x + ((bounds.width - getSize().width) / 2); int y = bounds.y + (bounds.height - getSize().height); setLocation(new Point(x, y)); } @Override public Dimension getSize() { return new Dimension(40, 17); } @Override public void paint(Graphics2D g2d) { Point p = getLocation(); Dimension size = getSize(); g2d.setColor(Color.BLUE); g2d.fillRect(px, py + 7, size.width, 10); g2d.fillRect(px + 18, py, 4, 7); } } public class Bullet extends AbstractEntity { @Override public void paint(Graphics2D g2d) { Rectangle bullet = new Rectangle(getLocation(), getSize()); g2d.setColor(Color.RED); g2d.fill(bullet); } @Override public Dimension getSize() { return new Dimension(4, 8); } } 

没什么了不起的,但他们每个人都定义了他们的参数,并且在被问到时可以呈现他

接下来,我们有模型,控制器和视图。 此示例使用控制器作为主要游戏循环,负责更新游戏(模型)状态和调度重绘。 这是通过使用Swing Timer来完成的,因为这可以防止更新循环和绘制循环之间可能的竞争条件。 更复杂的实现需要通过使用BufferStrategy和BufferStrategy以及BufferCapabilities来接管绘制过程。

该模型简单地获取当前视图边界和键的当前状态,并更新实体的状态。

视图监视用户输入,通知控制器,并呈现游戏的当前状态。

您会注意到视图和模型从不直接相互通信,这是控制器的域。

 public class DefaultGameModel implements GameModel { private final List entities; private Player player; private Long lastShot; public DefaultGameModel() { entities = new ArrayList<>(25); } @Override public Entity[] getEntities() { return entities.toArray(new Entity[0]); } @Override public void update(Rectangle bounds, Set keys) { if (player == null) { player = new Player(bounds); entities.add(player); } Point p = player.getLocation(); int xDelta = 0; if (keys.contains(Direction.LEFT)) { xDelta = -4; } else if (keys.contains(Direction.RIGHT)) { xDelta = 4; } px += xDelta; if (px <= bounds.x) { px = bounds.x; } else if (px + player.getSize().width >= bounds.x + bounds.width) { px = bounds.width - player.getSize().width; } player.setLocation(p); Iterator it = entities.iterator(); while (it.hasNext()) { Entity entity = it.next(); if (entity instanceof Bullet) { Point location = entity.getLocation(); Dimension size = entity.getSize(); location.y -= size.height; if (location.y + size.height < bounds.y) { it.remove(); } else { entity.setLocation(location); } } } if (keys.contains(Direction.SPACE)) { if (lastShot == null || System.currentTimeMillis() - lastShot > 100) { lastShot = System.currentTimeMillis(); Bullet bullet = new Bullet(); int x = px + ((player.getSize().width - bullet.getSize().width) / 2); int y = py - bullet.getSize().height; bullet.setLocation(new Point(x, y)); entities.add(bullet); } } } } public class DefaultGameController implements GameController { private GameModel model; private GameView view; private Timer timer; private Set keys = new HashSet<>(25); public DefaultGameController(GameModel gameModel, GameView gameView) { gameView.setController(this); view = gameView; model = gameModel; } @Override public Entity[] getEntities() { return model.getEntities(); } @Override public void setDirection(Direction direction, boolean pressed) { if (pressed) { keys.add(direction); } else { keys.remove(direction); } } @Override public void start() { if (timer != null && timer.isRunning()) { timer.stop(); } timer = new Timer(40, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { model.update(view.getViewBounds(), Collections.unmodifiableSet(keys)); view.repaint(); } }); timer.start(); } } public class DefaultGameView extends JPanel implements GameView { private GameController controller; public DefaultGameView() { addKeyBinding("left.pressed", KeyEvent.VK_LEFT, true, new DirectionAction(Direction.LEFT, true)); addKeyBinding("left.released", KeyEvent.VK_LEFT, false, new DirectionAction(Direction.LEFT, false)); addKeyBinding("right.pressed", KeyEvent.VK_RIGHT, true, new DirectionAction(Direction.RIGHT, true)); addKeyBinding("right.released", KeyEvent.VK_RIGHT, false, new DirectionAction(Direction.RIGHT, false)); addKeyBinding("space.pressed", KeyEvent.VK_SPACE, true, new DirectionAction(Direction.SPACE, true)); addKeyBinding("space.released", KeyEvent.VK_SPACE, false, new DirectionAction(Direction.SPACE, false)); } protected void addKeyBinding(String name, int keyEvent, boolean pressed, DirectionAction action) { addKeyBinding(name, KeyStroke.getKeyStroke(keyEvent, 0, !pressed), action); } protected void addKeyBinding(String name, KeyStroke keyStroke, DirectionAction action) { InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW); ActionMap actionMap = getActionMap(); inputMap.put(keyStroke, name); actionMap.put(name, action); } @Override public void setController(GameController controller) { this.controller = controller; } @Override public GameController getController() { return controller; } @Override public Rectangle getViewBounds() { return new Rectangle(new Point(0, 0), getSize()); } @Override public Dimension getPreferredSize() { return new Dimension(400, 400); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); GameController controller = getController(); for (Entity entity : controller.getEntities()) { // I don't trust you Graphics2D g2d = (Graphics2D) g.create(); entity.paint(g2d); g2d.dispose(); } } public class DirectionAction extends AbstractAction { private Direction direction; private boolean pressed; public DirectionAction(Direction direction, boolean pressed) { this.direction = direction; this.pressed = pressed; } @Override public void actionPerformed(ActionEvent e) { getController().setDirection(direction, pressed); } } } 

好的,但这一切都很好,但你怎么用呢? 比如这样的东西……

玩

 import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.swing.AbstractAction; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.KeyStroke; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class Main { public static void main(String[] args) { new Main(); } public Main() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } GameModel model = new DefaultGameModel(); DefaultGameView view = new DefaultGameView(); GameController controller = new DefaultGameController(model, view); JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(view); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); controller.start(); } }); } public static enum Direction { LEFT, RIGHT, SPACE } public interface Entity { public void paint(Graphics2D g2d); public Point getLocation(); public void setLocation(Point p); public Dimension getSize(); } public interface GameModel { public Entity[] getEntities(); public void update(Rectangle bounds, Set keys); } public interface GameController { public Entity[] getEntities(); public void setDirection(Direction direction, boolean pressed); public void start(); } public interface GameView { public void setController(GameController controller); public GameController getController(); public Rectangle getViewBounds(); public void repaint(); } public class DefaultGameModel implements GameModel { private final List entities; private Player player; private Long lastShot; public DefaultGameModel() { entities = new ArrayList<>(25); } @Override public Entity[] getEntities() { return entities.toArray(new Entity[0]); } @Override public void update(Rectangle bounds, Set keys) { if (player == null) { player = new Player(bounds); entities.add(player); } Point p = player.getLocation(); int xDelta = 0; if (keys.contains(Direction.LEFT)) { xDelta = -4; } else if (keys.contains(Direction.RIGHT)) { xDelta = 4; } px += xDelta; if (px <= bounds.x) { px = bounds.x; } else if (px + player.getSize().width >= bounds.x + bounds.width) { px = bounds.width - player.getSize().width; } player.setLocation(p); Iterator it = entities.iterator(); while (it.hasNext()) { Entity entity = it.next(); if (entity instanceof Bullet) { Point location = entity.getLocation(); Dimension size = entity.getSize(); location.y -= size.height; if (location.y + size.height < bounds.y) { it.remove(); } else { entity.setLocation(location); } } } if (keys.contains(Direction.SPACE)) { if (lastShot == null || System.currentTimeMillis() - lastShot > 100) { lastShot = System.currentTimeMillis(); Bullet bullet = new Bullet(); int x = px + ((player.getSize().width - bullet.getSize().width) / 2); int y = py - bullet.getSize().height; bullet.setLocation(new Point(x, y)); entities.add(bullet); } } } } public class DefaultGameController implements GameController { private GameModel model; private GameView view; private Timer timer; private Set keys = new HashSet<>(25); public DefaultGameController(GameModel gameModel, GameView gameView) { gameView.setController(this); view = gameView; model = gameModel; } @Override public Entity[] getEntities() { return model.getEntities(); } @Override public void setDirection(Direction direction, boolean pressed) { if (pressed) { keys.add(direction); } else { keys.remove(direction); } } @Override public void start() { if (timer != null && timer.isRunning()) { timer.stop(); } timer = new Timer(40, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { model.update(view.getViewBounds(), Collections.unmodifiableSet(keys)); view.repaint(); } }); timer.start(); } } public abstract class AbstractEntity implements Entity { private final Point location = new Point(0, 0); @Override public Point getLocation() { return new Point(location); } @Override public void setLocation(Point p) { location.setLocation(p); } } public class Player extends AbstractEntity { public Player(Rectangle bounds) { int x = bounds.x + ((bounds.width - getSize().width) / 2); int y = bounds.y + (bounds.height - getSize().height); setLocation(new Point(x, y)); } @Override public Dimension getSize() { return new Dimension(40, 17); } @Override public void paint(Graphics2D g2d) { Point p = getLocation(); Dimension size = getSize(); g2d.setColor(Color.BLUE); g2d.fillRect(px, py + 7, size.width, 10); g2d.fillRect(px + 18, py, 4, 7); } } public class Bullet extends AbstractEntity { @Override public void paint(Graphics2D g2d) { Rectangle bullet = new Rectangle(getLocation(), getSize()); g2d.setColor(Color.RED); g2d.fill(bullet); } @Override public Dimension getSize() { return new Dimension(4, 8); } } public class DefaultGameView extends JPanel implements GameView { private GameController controller; public DefaultGameView() { addKeyBinding("left.pressed", KeyEvent.VK_LEFT, true, new DirectionAction(Direction.LEFT, true)); addKeyBinding("left.released", KeyEvent.VK_LEFT, false, new DirectionAction(Direction.LEFT, false)); addKeyBinding("right.pressed", KeyEvent.VK_RIGHT, true, new DirectionAction(Direction.RIGHT, true)); addKeyBinding("right.released", KeyEvent.VK_RIGHT, false, new DirectionAction(Direction.RIGHT, false)); addKeyBinding("space.pressed", KeyEvent.VK_SPACE, true, new DirectionAction(Direction.SPACE, true)); addKeyBinding("space.released", KeyEvent.VK_SPACE, false, new DirectionAction(Direction.SPACE, false)); } protected void addKeyBinding(String name, int keyEvent, boolean pressed, DirectionAction action) { addKeyBinding(name, KeyStroke.getKeyStroke(keyEvent, 0, !pressed), action); } protected void addKeyBinding(String name, KeyStroke keyStroke, DirectionAction action) { InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW); ActionMap actionMap = getActionMap(); inputMap.put(keyStroke, name); actionMap.put(name, action); } @Override public void setController(GameController controller) { this.controller = controller; } @Override public GameController getController() { return controller; } @Override public Rectangle getViewBounds() { return new Rectangle(new Point(0, 0), getSize()); } @Override public Dimension getPreferredSize() { return new Dimension(400, 400); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); GameController controller = getController(); for (Entity entity : controller.getEntities()) { // I don't trust you Graphics2D g2d = (Graphics2D) g.create(); entity.paint(g2d); g2d.dispose(); } } public class DirectionAction extends AbstractAction { private Direction direction; private boolean pressed; public DirectionAction(Direction direction, boolean pressed) { this.direction = direction; this.pressed = pressed; } @Override public void actionPerformed(ActionEvent e) { getController().setDirection(direction, pressed); } } } } 

再一次,你需要离开并做更多的研究,但这是一般的想法

你的绘图取决于布尔变量拍摄; 有一个地方射击被设置为真; 如果关键操作不起作用,程序的流程可能永远不会到达那里,并且可能永远不会发生。

所以我觉得你最小化你的项目到一个屏幕,绘制图形而不依赖于按键。

如果您可以看到图形,那么您可以逐渐添加键