Java 调用paint()方法时图像闪烁

Java 调用paint()方法时图像闪烁,java,swing,jframe,paint,bufferedimage,Java,Swing,Jframe,Paint,Bufferedimage,我得到了扩展JFrame的第一个类,它包含以下类 package Game; import java.awt.*; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import javax.swing.*; import Maps.*; public class Window extends JFrame implements KeyListener { private Insets inset

我得到了扩展JFrame的第一个类,它包含以下类

package Game;

import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.*;

import Maps.*;

public class Window extends JFrame implements KeyListener
{
    private Insets insets;
    private int currentMapX;
    private int currentMapY;

    public Window()
    {
        super();
        setSize(new Dimension(1920, 1080));
        setLayout(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setUndecorated(true);
        setFocusable(true);
        setContentPane(new Container());
        setBackground(Color.BLACK);
        addKeyListener(this);
    }

    public void startGame()
    {
        insets = getInsets();
        currentMapX = 960 - (Game.level_01.getWidth() / 2); 
        currentMapY = 540 - (Game.level_01.getHeight() / 2);
        Game.level_01.setBounds(currentMapX, currentMapY, Game.level_01.getWidth(), Game.level_01.getHeight());
        add(Game.level_01);
    }

    private void moveMapRight()
    {
        Game.level_01.setBounds(++currentMapX, currentMapY, Game.level_01.getWidth(), Game.level_01.getHeight());
    }

    private void moveMapLeft()
    {
        Game.level_01.setBounds(--currentMapX, currentMapY, Game.level_01.getWidth(), Game.level_01.getHeight());
    }

    public void paint(Graphics g)
    {
        super.paint(g);
        Graphics2D g2d = (Graphics2D)g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        Game.player.paint(g2d);
    }

    public void keyPressed(KeyEvent k) 
    {
        if(k.getKeyCode() == KeyEvent.VK_RIGHT) moveMapRight();
        if(k.getKeyCode() == KeyEvent.VK_LEFT) moveMapLeft();
    }
    public void keyReleased(KeyEvent k){}
    public void keyTyped(KeyEvent k){}
}
最后,这个类是播放器的类,即BuffereImage。我的问题是,当我启动程序时,播放器图像开始闪烁,因为当我在JFrame类中调用paint()方法时,它首先绘制jpanel,然后绘制播放器图像。如何解决图像闪烁问题? 提前谢谢

  • 不要覆盖顶级容器中的
    paint(Graphics)
    ,如
    JFrame
    (非双缓冲)。而是在
    JPanel
    中进行自定义绘制(该是双缓冲的)
  • 此外,当覆盖任何绘制方法时,请立即调用超级方法,从而绘制背景并有效地擦除早期图形
  • 不要覆盖顶级容器中的
    paint(Graphics)
    ,如
    JFrame
    (非双缓冲)。而是在
    JPanel
    中进行自定义绘制(该是双缓冲的)
  • 此外,当覆盖任何绘制方法时,请立即调用超级方法,从而绘制背景并有效地擦除早期图形

  • 正如其他人所说,您不应该覆盖顶级组件中的
    paint(Graphics g)
    ,因此这是问题的一部分,但您还需要小心确保每次重新绘制时只在屏幕上绘制一次:

    我的问题是,当我启动程序时,播放器图像会启动 闪烁,因为当我在JFrame中调用paint()方法时 类,这将首先绘制jpanel,然后绘制播放器图像

    现在的情况是,每次调用一个方法来修改
    绘制(图形屏幕)
    方法中的
    图形
    对象时,它都会直接修改屏幕内容,迫使屏幕在您完成绘制真正想要的东西之前刷新-
    播放器
    。通过先绘制到
    super
    ,然后再次使用自定义渲染,实际上绘制到屏幕至少两次,从而导致闪烁。您可以通过双缓冲来解决这个问题

    双缓冲首先渲染图像,然后将图像绘制到屏幕。使用
    组件
    类提供的内置方法(请记住
    JPanel
    扩展了
    组件
    ),您可以获得组件的可视区域的大小。创建一个大小相同的
    buffereImage
    ,然后调用
    buffereImage.createGraphics()
    -这将为您提供一个
    Graphics2D
    对象,您可以使用它在
    buffereImage
    上绘制。渲染到
    bufferedImage
    后,调用
    screen.drawImage(bufferedImage,0,0,null)
    。这允许您在不进行屏幕刷新的情况下修改
    缓冲区图像
    ,然后在一次调用中将缓冲区的内容绘制到屏幕

    其他信息

    我应该在前面指出,我对屏幕上的实际布局一无所知,因此您可能需要对播放器屏幕区域的边界以及调用
    drawImage(Image,x,y,ImageObserver)
    时左上角的位置进行一些额外的检查。还要注意
    buffereImage
    中的透明度或缺乏透明度-如果在屏幕上的其他重要内容上绘制不透明像素,您很容易迷失方向

    试试这个(但不要保留)

    同样,我不建议您在顶级组件中进行所有绘制,但您可以在过渡期间尝试这样做:

    package Player;
    
    import java.awt.*;
    
    import Game.*;
    
    public class Player
    {
        private int x;
        private int y;
        private int xa;
        private int ya;
        private Graphics2D g2d; 
        public Player(double x, double y)
        {
            super();
            this.x = (int)x;
            this.y = (int)y;
            xa = 0;
            ya = 1;
        }
    
        public void movePlayer()
        {
            x += xa;
            y += ya;
        }
    
        public void setDirection(int xa, int ya)
        {
            this.xa = xa;
            this.ya = ya;   
        }
    
        public void paint(Graphics g)
        {
            g2d = (Graphics2D)g;
            g2d.drawImage(Game.playerImages[6], x , y, Game.tileSize, Game.tileSize, null);
        }
    
        public int getX()
        {
            return x;
        }
    
        public int getY()
        {
            return y;
        }
    
        public int getXA()
        {
            return xa;
        }
    
        public int getYA()
        {
            return ya;
        }
    }
    

    正如其他人所说,您不应该覆盖顶级组件中的
    paint(Graphics g)
    ,因此这是问题的一部分,但您还需要小心确保每次重新绘制时只在屏幕上绘制一次:

    我的问题是,当我启动程序时,播放器图像会启动 闪烁,因为当我在JFrame中调用paint()方法时 类,这将首先绘制jpanel,然后绘制播放器图像

    现在的情况是,每次调用一个方法来修改
    绘制(图形屏幕)
    方法中的
    图形
    对象时,它都会直接修改屏幕内容,迫使屏幕在您完成绘制真正想要的东西之前刷新-
    播放器
    。通过先绘制到
    super
    ,然后再次使用自定义渲染,实际上绘制到屏幕至少两次,从而导致闪烁。您可以通过双缓冲来解决这个问题

    双缓冲首先渲染图像,然后将图像绘制到屏幕。使用
    组件
    类提供的内置方法(请记住
    JPanel
    扩展了
    组件
    ),您可以获得组件的可视区域的大小。创建一个大小相同的
    buffereImage
    ,然后调用
    buffereImage.createGraphics()
    -这将为您提供一个
    Graphics2D
    对象,您可以使用它在
    buffereImage
    上绘制。渲染到
    bufferedImage
    后,调用
    screen.drawImage(bufferedImage,0,0,null)
    。这允许您在不进行屏幕刷新的情况下修改
    缓冲区图像
    ,然后在一次调用中将缓冲区的内容绘制到屏幕

    其他信息

    我应该在前面指出,我对屏幕上的实际布局一无所知,因此您可能需要对播放器屏幕区域的边界以及调用
    drawImage(Image,x,y,ImageObserver)
    时左上角的位置进行一些额外的检查。A.
    package Player;
    
    import java.awt.*;
    
    import Game.*;
    
    public class Player
    {
        private int x;
        private int y;
        private int xa;
        private int ya;
        private Graphics2D g2d; 
        public Player(double x, double y)
        {
            super();
            this.x = (int)x;
            this.y = (int)y;
            xa = 0;
            ya = 1;
        }
    
        public void movePlayer()
        {
            x += xa;
            y += ya;
        }
    
        public void setDirection(int xa, int ya)
        {
            this.xa = xa;
            this.ya = ya;   
        }
    
        public void paint(Graphics g)
        {
            g2d = (Graphics2D)g;
            g2d.drawImage(Game.playerImages[6], x , y, Game.tileSize, Game.tileSize, null);
        }
    
        public int getX()
        {
            return x;
        }
    
        public int getY()
        {
            return y;
        }
    
        public int getXA()
        {
            return xa;
        }
    
        public int getYA()
        {
            return ya;
        }
    }
    
    //This is in the Window class:
    public void paint(Graphics screen)
    {
        //Render everything first to a BufferedImage:
        BufferedImage bufferedImage = ... //Initialize based on Window's
                                          //size and bounds.
        Graphics2D buf = bufferedImage.createGraphics();
        super.paint(buf);
        buf.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                RenderingHints.VALUE_ANTIALIAS_ON);
        Game.player.paint(buf);
    
        //Now paint the buffer to screen:
        int x = ... //set to top-left x-coordinate, probably 0
        int y = ... //set to top-left y-coordinate, probably 0
        screen.drawImage(buf,x,y,null);
    }