停止 JPanel 的paintComponent 方法绘制背景

问题描述

我正在用 Java 制作贪吃蛇游戏。

为了提高效率,我只绘制了改变这一帧的位置(蛇的第一个和最后一个单元格)。

这是我的代码

public class GameCanvas extends JPanel {

private final List<GameChange> changes;

public GameCanvas() {
    setBackground(Color.darkGray);

    changes = new ArrayList<>();
}

public void paintComponent(Graphics g) {
    super.paintComponent(g);

    Graphics2D g2d = (Graphics2D) g;

    for (GameChange change : changes) {
        Vector2 pos = change.position;
        int val = change.value;

        if (val != 0) g2d.setColor(Color.BLACK);
        else g2d.setColor(Color.WHITE); //getBackground();

        g2d.fillRect(GameWindow.pixelSize * pos.x,GameWindow.pixelSize * pos.y,GameWindow.pixelSize,GameWindow.pixelSize);
    }

    changes.clear();
}

public void applyChanges(List<GameChange> changes) {
    this.changes.addAll(changes);
    repaint();
}
}

唯一的问题是 paintComponent 方法正在重新绘制背景,而蛇的中间部分正在消失。

黑色单元格是新的头部,白色的单元格是我要删除的单元格。中间的单元格应该是黑色的。

编辑:

每个人都告诉我要绘制整个地图,但我真的很想提高性能。这是我到目前为止所做的:

private void paintChanges(List<GameChange> changes) {
    Graphics2D g2d = (Graphics2D) getGraphics();

    // Here I paint only the changes
}

public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g;

    // Here I paint everything
}

当蛇移动时,我只绘制变化,但如果框架自动重新绘制,蛇不会消失。它似乎有效,但我不确定使用 getGraphics() 方法是否安全。

解决方法

这显然不是你的游戏,但它可能有助于消除你需要做一些特别的事情来提高绘画效率的观念。

  • 要创建蛇,请按住左键并拖出一条曲线。
  • 然后松开按钮并在面板中四处移动鼠标并观察 "snake" 跟随鼠标移动。
  • 您可以随时拖动鼠标添加更多点。

这通过简单地创建一个点列表并在它们之间画一条线来工作。当鼠标移动时,它会移除第一个点并将当前点添加到最后。 Paint 方法只是为每次鼠标移动遍历列表并绘制所有线条,从而呈现移动的外观。

这有一个明显的(也许还有其他的,不那么明显的)缺陷。点的数量是恒定的,但蛇 expandscontracts 的大小是因为随着鼠标移动得更快,这些点会散开,因此点之间的线更长。但这与绘画无关,而与鼠标移动的速度有关。

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

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

public class SimpleSnake extends JPanel {
    JFrame frame = new JFrame("Simple Snake");
    List<Point> snake = new ArrayList<>();
    final static int WIDTH = 500;
    final static int HEIGHT = 500;
    public static void main(String[] args) {
        SwingUtilities.invokeLater(()-> new SimpleSnake());
    }
    public SimpleSnake() {
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        addMouseMotionListener(new MyMouseListener());
        setBackground(Color.white);
        frame.add(this);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
    
    @Override
    public Dimension getPreferredSize() {
        return new Dimension(WIDTH,HEIGHT);
    }
    
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (snake.size() < 2) {
            return;
        }
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setColor(Color.black);
        g2d.setStroke(new BasicStroke(3)); // line thickness
        Point start = snake.get(0);
        for(int i = 1; i < snake.size(); i++) {
            Point next = snake.get(i);
            g2d.drawLine(start.x,start.y,next.x,next.y);
            start = next;
        }
        g2d.dispose();
    }
    
    public class MyMouseListener extends MouseAdapter {
        public void mouseDragged(MouseEvent me) {
            snake.add(new Point(me.getX(),me.getY()));
            repaint();
        }
        
        public void mouseMoved(MouseEvent me) {
            if(snake.isEmpty()) {
                return;
            }
            snake.remove(0);
            snake.add(new Point(me.getX(),me.getY()));
            repaint();
        }       
    }
}