程序没有正确绘制屏幕

Eam*_*mes 0 java null swing key-bindings paintcomponent

我一直在构建一个简短的程序,基本上在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");
        }
    }
Run Code Online (Sandbox Code Playgroud)

Mad*_*mer 5

所以,有很多问题......

你应该叫setVisible上了JFrame最后,这将确保组件布局

您的键绑定问题似乎与您正在使用该运动的事实相关Set,但您实际上从未向其中添加任何内容.相反,你应该检查dir价值.

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

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

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

What I'm about to present is an oversimplification intended to spark ideas, rather than been a concrete solution, as there are a number of ways you could achieve the physical implementation...

So, what we need is, a concept of something in the game, AKA an "entity", entities take many forms, but I'm going to focus on renderable/displayable entities. You need some kind of model that is responsible for modeling the current state of the game and which is responsible for implementing the rules. You need some kind of view, which is responsible for rendering the model and responding to user input. You need some kind of controller which controls how the model and the view bridged.

从一系列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();
}
Run Code Online (Sandbox Code Playgroud)

我们来看看实体的实现.这个例子有两个实体,a Player和a 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);
    }

}
Run Code Online (Sandbox Code Playgroud)

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

接下来,我们有模型,控制器和视图.此示例使用控制器作为主要游戏循环,负责更新游戏(模型)状态和调度重绘.这是通过使用Swing完成的,Timer因为这可以防止更新循环和绘制循环之间可能的竞争条件.更复杂的实现需要通过使用a BufferStrategyBufferStrategy和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);
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

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

玩

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);
            }

        }

    }

}
Run Code Online (Sandbox Code Playgroud)

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