How to Draw an BufferedImage to a JPanel

前端 未结 2 1679
离开以前
离开以前 2020-12-01 22:57

I am trying to use some sort of draw method to draw a sprite image to my subclass of JPanel called AnimationPanel. I have created a Spritesheet class which can generate a Bu

相关标签:
2条回答
  • 2020-12-01 23:40

    Here's some generic code for drawing an image to a JPanel. This method is called to paint your JPanel component.

    public void paintComponent (Graphics g)
    { 
         super.paintComponent(g);
         //I would have image be a class variable that gets updated in your run() method
         g.drawImage(image, 0, 0, this); 
    } 
    

    I may also modify run() to look something like this:

    public void run() {
      BufferedImage[] frames = sheet.getAllSprites();
      currentFrame = 0;
      while (true) {
        image = frames[currentFrame];
        this.repaint(); //explicitly added "this" for clarity, not necessary.
        currentFrame++;
        if (currentFrame >= frames.length) {
            currentFrame = 0;
        }
      }
    }
    

    In regards to only repainting part of the component, it gets a little more complicated

    public void run() {
      BufferedImage[] frames = sheet.getAllSprites();
      currentFrame = 0;
      while (true) {
        image = frames[currentFrame];
        Rectangle r = this.getDirtyRect();
        this.repaint(r); 
        currentFrame++;
        if (currentFrame >= frames.length) {
            currentFrame = 0;
        }
      }
    }
    
    public Rectangle getDirtyRect() {
      int minX=0; //calculate smallest x value affected
      int maxX=0; //calculate largest x value affected
      int minY=0; //calculate smallest y value affected
      int maxY=0; //calculate largest y value affected 
      return new Rectangle(minX,minY,maxX,maxY);
    }
    
    0 讨论(0)
  • 2020-12-01 23:53
    1. I'd encourage the use of a javax.swing.Timer to control the frame rate, rather than an uncontrolled loop
    2. Once the timer "ticks", you need to increment the current frame, get the current image to be rendered and call repaint on the JPanel
    3. Use Graphics#drawImage to render the image.

    See...

    • Painting in AWT and Swing
    • Performing Custom Painting
    • How to use Swing Timers
    • Graphics#drawImage(Image, int, int, ImageObserver)

    for more details

    There is a cascading series of issues with your Spritesheet class, apart from the fact that it won't actually compile, there are issues with you returning the wrong values from some methods and relying on values which are better calculated...

    I had to modify your code so much, I can't remember most of them

    public int getHeight() {
        return frameWidth;
    }
    

    and

    public BufferedImage[] getAllSprites() {
        BufferedImage[] sprites = new BufferedImage[frames];
        int y = 0;
        for (int i = 0; i < frames; i++) {
            x = i * frameWidth;
            currentSprite = sheet.getSprite(x,y,frameHeight,frameWidth);
            sprites.add(currentSprite);
        }
        return sprites;
    
    }
    

    Stand out as two main examples...

    Run

    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import javax.imageio.ImageIO;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class TestSpriteSheet {
    
        public static void main(String[] args) {
            new TestSpriteSheet();
        }
    
        public TestSpriteSheet() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            private Spritesheet spritesheet;
            private BufferedImage currentFrame;
            private int frame;
    
            public TestPane() {
                spritesheet = new Spritesheet("/Sheet02.gif", 240, 220);
                Timer timer = new Timer(100, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        currentFrame = spritesheet.getSprite(frame % spritesheet.getFrameCount());
                        repaint();
                        frame++;
                    }
                });
                timer.start();
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(240, 220);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                if (currentFrame != null) {
                    Graphics2D g2d = (Graphics2D) g.create();
                    int x = (getWidth() - currentFrame.getWidth()) / 2;
                    int y = (getHeight() - currentFrame.getHeight()) / 2;
                    g2d.drawImage(currentFrame, x, y, this);
                    g2d.dispose();
                }
            }
    
        }
    
        public class Spritesheet {
    
    //Instance Variables
            private String path;
            private int frameWidth;
            private int frameHeight;
            private BufferedImage sheet = null;
            private BufferedImage[] frameImages;
    
    //Constructors
            public Spritesheet(String aPath, int width, int height) {
    
                path = aPath;
                frameWidth = width;
                frameHeight = height;
    
                try {
                    sheet = ImageIO.read(getClass().getResourceAsStream(path));
                    frameImages = getAllSprites();
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
            }
    
            public BufferedImage getSprite(int frame) {
                return frameImages[frame];
            }
    
    //Methods
            public int getHeight() {
                return frameHeight;
            }
    
            public int getWidth() {
                return frameWidth;
            }
    
            public int getColumnCount() {
                return sheet.getWidth() / getWidth();
            }
    
            public int getRowCount() {
                return sheet.getHeight() / getHeight();
            }
    
            public int getFrameCount() {
                int cols = getColumnCount();
                int rows = getRowCount();
                return cols * rows;
            }
    
            private BufferedImage getSprite(int x, int y, int h, int w) {
                BufferedImage sprite = sheet.getSubimage(x, y, h, w);
                return sprite;
            }
    
            public BufferedImage[] getAllSprites() {
                int cols = getColumnCount();
                int rows = getRowCount();
                int frameCount =  getFrameCount();
                BufferedImage[] sprites = new BufferedImage[frameCount];
                int index = 0;
                System.out.println("cols = " + cols);
                System.out.println("rows = " + rows);
                System.out.println("frameCount = " + frameCount);
                for (int row = 0; row < getRowCount(); row++) {
                    for (int col = 0; col < getColumnCount(); col++) {
                        int x = col * getWidth();
                        int y = row * getHeight();
                        System.out.println(index + " " + x + "x" + y);
                        BufferedImage currentSprite = getSprite(x, y, getWidth(), getHeight());
                        sprites[index] = currentSprite;
                        index++;
                    }
                }
                return sprites;
    
            }
    
        }
    }
    

    Remember, animation is the illusion of change over time. You need to provide a delay between each frame of the animation, long enough for the user to recognise it, but short enough to make the animation look smooth.

    In the above example, I've used 100 milliseconds, simply as an arbitrary value. It could be possible to use something more like 1000 / spritesheet.getFrameCount(), which will allow a full second for the entire animation (all the frames within one second).

    You might need to use different values, for longer or short animations, depending on your needs

    0 讨论(0)
提交回复
热议问题