简体   繁体   English

重涂不会更新屏幕

[英]Repaint does not update the screen

I would like to repaint my screen. 我想重新粉刷我的屏幕。 As of now all it does is show the first screen with a dot where the head is supposed to be. 到目前为止,它所做的只是显示第一个屏幕,该屏幕上带有一个应该放置头部的点。 This is fine, however I've written in my code that I want to move the head down 10 pixels every second. 很好,但是我已经在代码中编写了想要将头每秒降低10像素的操作。 I'm printing at what point the head is supposed to be at, and in the command prompt it shows that the y value is indeed increasing. 我正在打印打印头应该位于的位置,并且在命令提示符下显示y值确实在增加。 However on my screen the head is not moving. 但是在我的屏幕上,头没有动。

I have tried using the revalidate method, trying to extend the canvas class instead of jframe, I have tried using different classes just for the paint method, i have tried replacing the paint method with the paintComponent method. 我尝试使用revalidate方法,尝试扩展canvas类而不是jframe,我尝试仅针对paint方法使用不同的类,我尝试将paint方法替换为paintComponent方法。 And as you can probably tell I have a subpar understanding of anything related to painting in java. 正如您可能会告诉我的那样,我对与Java绘画有关的任何内容都不太了解。 I have tried reading into these superclasses but they are too complicated for me to understand. 我尝试阅读这些超类,但是它们太复杂了,以至于我无法理解。 I have also tried running without the sleep declaration. 我还尝试了在没有睡眠声明的情况下运行。 This didn't matter. 没关系

Main class: This class contains the main method to start running the snake game. 主类:该类包含开始运行蛇游戏的主要方法。

import java.util.concurrent.TimeUnit;

public class Main{

    public static void main(String[] args) throws InterruptedException {
        Main programma = new Main();
        programma.rungame();
    }

void rungame() throws InterruptedException {
        AllGUIElements gui = new AllGUIElements();
        gui.gui();
        while (true) {
            TimeUnit.SECONDS.sleep(1);
            gui.setGameScreen();
        }
    }
}

AllGUIElements class: This class makes a new frame, containing a new panel. AllGUIElements类:此类创建一个新框架,其中包含一个新面板。 This panel is being painted using paintComponent. 此面板是使用paintComponent绘制的。 setGameScreen updates the position of the head and is supposed to repaint the screen. setGameScreen更新头部的位置,并应该重新绘制屏幕。

import javax.swing.*;
import java.awt.*;

public class AllGUIElements extends JPanel {

    private JFrame frame;
    private JPanel panel;

    private int screen;

    Snake hoofd = new Head(new Point(30,30),3,null);

    void gui() throws InterruptedException {

        frame = new JFrame("Snake Game");
        panel = new AllGUIElements();
        panel.setBackground(Color.GRAY);
        panel.setSize(1000,500);
        frame.setSize(1000,500);
        frame.add(panel);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

void setGameScreen() {
    repaint();
        if (hoofd.getDirection() == 1) {
            hoofd.setPosition(new Point(hoofd.getPosition().x, hoofd.getPosition().y-10));
        }
        if (hoofd.getDirection() == 2) {
            hoofd.setPosition(new Point(hoofd.getPosition().x+10, hoofd.getPosition().y));

        }
        if (hoofd.getDirection() == 3) {
            hoofd.setPosition(new Point(hoofd.getPosition().x, hoofd.getPosition().y+10));

        }
        if (hoofd.getDirection() == 4) {
            hoofd.setPosition(new Point(hoofd.getPosition().x-10, hoofd.getPosition().y));
        }
}

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
            g.setColor(Color.DARK_GRAY);
            g.fillRect(0, 0, 1000, 10);
            g.fillRect(0, 0, 10, 500);
            g.fillRect(990, 0, 10, 500);
            g.fillRect(0, 490, 1000, 10);

            g.setColor(Color.GREEN);
            g.fillRect(hoofd.getPosition().x, hoofd.getPosition().y, 10, 10);
    }

}

Screenobject class: Parent class of Snake and Food (which is not posted since it's not necessary) which returns the position of the head, bodypart and food. Screenobject类:Snake and Food的父类(由于没有必要而不会发布),它返回头部,身体部位和食物的位置。

import java.awt.*;

public class Screenobject{

    Point pos;

    Screenobject(Point pos){
        this.pos = pos;
    }

    void setPosition(Point pos){
        this.pos = pos;
    }

    Point getPosition() {
        return pos;
    }
}

Snake class: Child of Screenobject and parent of Head and Body. 蛇类:Screenobject的子级,Head和Body的父级。 This class sets the direction of the objects. 此类设置对象的方向。

import java.awt.*;

public class Snake extends Screenobject{

    int direction;
    //directions:
    //1 = up
    //2 = right
    //3 = down
    //4 = left

    Snake nextpart;

    Snake(Point pos, int direction, Snake nextpart){
        super(pos);
        this.direction = direction;
        this.nextpart = nextpart;
    }

    int getDirection() {
        return direction;
    }
}

Head class: Child class of snake and is supposed to be the first object of a linked list for the snake. 头类:蛇的子类,应该被认为是蛇链表的第一个对象。

import java.awt.*;

public class Head extends Snake{
    Head(Point pos, int direction, Snake nextpart){
        super(pos, direction, nextpart);
    }
}

Body class: Child class of Snake and grows for every food eaten. 身体类别:蛇类的儿童类别,每食用一种食物都会增长。

import java.awt.*;

public class Body extends Snake{
    Body(Point pos, int direction, Snake nextpart){
        super(pos, direction, nextpart);
    }

}

First things first: 首先要注意的是:

I have used the "Divide and conquer" method to only post the necessary information 我使用了“分而治之”的方法来仅发布必要的信息

Yes, you posted only the necessary information, but you're still using too many classes for a MCVE / MRE , ideally all your code would fit in a single class. 是的,您只发布了必要的信息,但是对于MCVE / MRE您仍然使用了太多的类,理想情况下,所有代码都可以放在一个类中。

I didn't manage to make it any shorter without omitting code to make it run. 在没有省略代码使其运行的情况下,我没有设法使其变得更短。

That's the idea about us asking for an MRE, you should create a brand new program to isolate the problem, in your case this is: Move a shape on a certain direction and keep moving on that direction. 这就是我们要求MRE的想法,您应该创建一个全新的程序来隔离问题,在您的情况下,这是:在某个方向上移动形状并继续在该方向上移动。

Right now, I can see that you've tried creating it, and to improve, so I will post an example of what is expected in future questions of your so that you ask better questions in the future. 现在,我可以看到您已经尝试创建它并进行改进,因此,我将发布一个示例,说明您将来遇到的问题,以便您将来提出更好的问题。

That being said, let's go and answer your question. 话虽这么说,让我们去回答您的问题。


This is an example with less than 100 lines of code (not including comments) that addresses the following issues in your code: 这是一个示例,其中少于100行代码(不包括注释)解决了代码中的以下问题:

  1. Removes the while(true) statement in favor of a Swing Timer to prevent blocking the Event Dispatch Thread (EDT) 删除while(true)语句以支持Swing计时器,以防止阻塞事件调度线程(EDT)

  2. Places the program on the EDT, see point #2 of this answer 将程序放在EDT上,请参阅此答案的第2点

  3. Makes use of JFrame#pack() method instead of manually setting it with setSize(...) for both the JFrame and JPanel JFrameJPanel使用JFrame#pack()方法,而不是使用setSize(...)手动设置它

  4. Gets rid of the "magic numbers" for the directions and uses an enum for this matter, and thus the code is more readable as we all know that TOP should move it to top, but we don't know that 1 should move it to top. 摆脱了指示的“幻数” ,并为此使用了enum ,因此代码更易读,因为我们都知道TOP应该将其移至顶部,但我们不知道1应该将其移至顶部最佳。

  5. Makes use of the Shape API to draw the shapes on the JPanel as shown in this answer 本答案所示,利用Shape API在JPanel上绘制形状

  6. I would also suggest naming your methods using camelCase so that rungame() becomes runGame() as it's easier to read, same for other methods. 我还建议使用camelCase命名您的方法,以便rungame()变得更易于阅读,而成为runGame() ,与其他方法一样。 And give them more meaningful names such as hoofd , I don't know what that is and if I read it alone without context it would be extremely difficult to say what type of object it is. 并给它们hoofd有意义的名字,例如hoofd ,我不知道那是什么,如果我不带上下文单独阅读它,将很难说出它是什么类型的对象。


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class SnakeGame {
    private JFrame frame;
    private Snake snake;
    private JPanel buttonsPane;
    private JButton[] buttons; // Our array of buttons
    private Timer timer;
    private Direction currentDirection;

    // This enum will be used to determine the direction the snake will take.
    private enum Direction {
        TOP, LEFT, BOTTOM, RIGHT
    }

    public static void main(String[] args) {
        // We place our program on the EDT using Java 8 lambda expressions.
        SwingUtilities.invokeLater(() -> new SnakeGame().createAndShowGUI());
    }

    private void createAndShowGUI() {
        frame = new JFrame(getClass().getSimpleName());
        snake = new Snake();
        buttonsPane = new JPanel();
        buttons = new JButton[Direction.values().length];

        for (int i = 0; i < buttons.length; i++) {
            buttons[i] = new JButton(Direction.values()[i].toString()); // We create a JButton with the current value of the direction
            buttons[i].addActionListener(listener); // We set their ActionListeners
            buttonsPane.add(buttons[i]); // And add them to the buttonsPane
        }

        currentDirection = Direction.RIGHT; // We set a default direction
        timer = new Timer(1000, listener); // And start our Swing Timer

        // We add our components and then pack the frame, after that we start the timer.
        frame.add(snake);
        frame.add(buttonsPane, BorderLayout.SOUTH);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
        timer.start();
    }

    // Our ActionListener for moving the snake
    private ActionListener listener = e -> { // Using Java 8 lambda expressions again
        // We set the current direction using a ternary, if the source of the event is
        // the timer we leave the current direction as is
        // otherwise we set it to the direction from the button clicked
        currentDirection = e.getSource().equals(timer) ? currentDirection : Direction.valueOf(e.getActionCommand());
        snake.move(currentDirection); // And we call the move method
    };

    @SuppressWarnings("serial")
    class Snake extends JPanel {
        private int xPos;
        private int yPos;
        private static final int SPEED = 10; // We set the speed as a constant (10 pixels at a time) in any direction

        // We determine the movement direction
        public void move(Direction direction) {
            switch (direction) {
            case TOP:
                yPos -= SPEED;
                break;
            case LEFT:
                xPos -= SPEED;
                break;
            case BOTTOM:
                yPos += SPEED;
                break;
            case RIGHT:
                xPos += SPEED;
                break;
            }
            this.repaint(); // And repaint the snake
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            g2d.setColor(Color.DARK_GRAY);
            g2d.fill(new Rectangle2D.Double(xPos, yPos, 10, 10)); // We draw a rectangle using the Shape API
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(300, 300); // We set the preferredSize of the JPanel to 300 x 300
        }
    }
}

The above code produces the following result, for Java 7 and below or if you don't want to use lambda expressions or are too difficult for you, check the point #2 on this answer, it shows how to place the program on the EDT without lambda expressions and this answer shows how to write the ActionListener without them as well. 上面的代码产生以下结果,对于Java 7及以下版本,或者如果您不想使用lambda表达式或对您来说太难了,请检查此答案的第二点,它显示了如何将程序放置在EDT上没有lambda表达式, 此答案显示了如何在没有它们的情况下编写ActionListener

There you go, a simple runnable example, it's complete, it's verifiable, it's minimal and that's what we expect from you in future questions. 在这里,您可以找到一个简单的可运行示例,它是完整的,可验证的,最小的,这就是我们在以后的问题中对您的期望。

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM