我一直在构建一个简短的程序,基本上在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 <= 0)
x = 0;
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<Direction> movement;
public MoveAction(boolean pressed, Ship s1, Direction dir) {
System.out.println("moveaction class");
movement = new HashSet<Direction>();
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");
}
}
答案 0 :(得分:5)
所以,有很多问题......
您应该在setVisible
上拨打JFrame
,这样可以确保组件的布局
您的键绑定问题似乎与您使用移动Set
的事实有关,但您实际上从未向其添加任何内容。相反,您应该检查dir
值。
可能还有很多其他的东西。您的代码紧密耦合,并且没有对状态进行任何集中管理。
我首先要更好地理解Model-View-Controller范例,并将代码分成适当的责任范围。
&#34;数据&#34;因为游戏应该与'#34;渲染&#34;该游戏应与游戏更新方式的决定分开。
我即将呈现的内容过于简单化,旨在激发创意,而不是具体的解决方案,因为有很多方法可以实现物理实现......
所以,我们需要的是,游戏中的某个概念,AKA和实体&#34;,实体采用多种形式,但我将专注于可渲染/可显示的实体。您需要某种模型来负责对游戏的当前状态进行建模,并负责实施规则。您需要某种视图,它负责呈现模型并响应用户输入。您需要某种控制器来控制模型和视图的桥接方式。
从一系列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<Direction> 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(p.x, p.y + 7, size.width, 10);
g2d.fillRect(p.x + 18, p.y, 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 and BufferCapabilities来接管绘画过程。
模型只需获取当前视图边界和键的当前状态,并更新实体的状态。
视图监视用户输入,通知控制器,并呈现游戏的当前状态。
您会注意到视图和模型永远不会直接相互通信,这是控制器的域。
public class DefaultGameModel implements GameModel {
private final List<Entity> 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<Direction> 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;
}
p.x += xDelta;
if (p.x <= bounds.x) {
p.x = bounds.x;
} else if (p.x + player.getSize().width >= bounds.x + bounds.width) {
p.x = bounds.width - player.getSize().width;
}
player.setLocation(p);
Iterator<Entity> 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 = p.x + ((player.getSize().width - bullet.getSize().width) / 2);
int y = p.y - 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<Direction> 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<Direction> 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<Entity> 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<Direction> 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;
}
p.x += xDelta;
if (p.x <= bounds.x) {
p.x = bounds.x;
} else if (p.x + player.getSize().width >= bounds.x + bounds.width) {
p.x = bounds.width - player.getSize().width;
}
player.setLocation(p);
Iterator<Entity> 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 = p.x + ((player.getSize().width - bullet.getSize().width) / 2);
int y = p.y - 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<Direction> 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(p.x, p.y + 7, size.width, 10);
g2d.fillRect(p.x + 18, p.y, 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);
}
}
}
}
同样,你需要离开并做更多的研究,但这是一般的想法
答案 1 :(得分:0)
您的绘图取决于布尔变量拍摄;有一个地方拍摄真实;如果关键操作不起作用,程序的流程可能永远不会到达那里,而且可能永远不会发生。
所以我觉得你把项目最小化到一个不用按键就可以绘制图形的画面。
如果您可以看到图形,那么您可以逐渐添加按键