简体   繁体   English

如何向JPanel绘制BufferedImage

[英]How to Draw an BufferedImage to a JPanel

I am trying to use some sort of draw method to draw a sprite image to my subclass of JPanel called AnimationPanel. 我正在尝试使用某种draw方法将一个精灵图像绘制到我的JPanel子类AnimationPanel。 I have created a Spritesheet class which can generate a BufferedImage[] that contains all of the sprites in the sheet. 我创建了一个Spritesheet类,该类可以生成一个BufferedImage [],其中包含工作表中的所有Sprite。 In my AnimationPanel class, which implements Runnable, I am getting that BufferedImage[] from the spritesheet instantiated in the AnimationPanel constructor. 在实现Runnable的AnimationPanel类中,我从AnimationPanel构造函数中实例化的Spritesheet中获取了BufferedImage []。 I want to be able to loop through this array and display each sprite to the screen. 我希望能够遍历此数组并将每个子画面显示到屏幕上。 How would I do this? 我该怎么做? Here are my AnimationPanel and Spritesheet classes. 这是我的AnimationPanel和Spritesheet类。

AnimationPanel

package com.kahl.animation;

import javax.swing.JPanel;

public class AnimationPanel extends JPanel implements Runnable {

//Instance Variables
private Spritesheet sheet;
private int currentFrame;
private Thread animationThread;
private BufferedImage image;

public AnimationPanel(Spritesheet aSheet) {
    super();
    sheet = aSheet;
    setPreferredSize(new Dimension(128,128));
    setFocusable(true);
    requestFocus();

}

public void run() {
    BufferedImage[] frames = sheet.getAllSprites();
    currentFrame = 0;
    while (true) {
        frames[currentFrame].draw(); //some implementation still necessary here
        currentFrame++;
        if (currentFrame >= frames.length) {
            currentFrame = 0;
        }
    }
}

public void addNotify() {
    super.addNotify();
    if (animationThread == null) {
        animationThread = new Thread(this);
        animationThread.start();
    }
}

}

Spritesheet

package com.kahl.animation;

import java.awt.image.BufferedImage;
import java.imageio.ImageIO;
import java.io.IOException;
import java.io.File;

public class Spritesheet {

//Instance Variables
private String path;
private int frameWidth;
private int frameHeight;
private int framesPerRow;
private int frames;
private BufferedImage sheet = null;

//Constructors
public Spritesheet(String aPath,int width,int height,int fpr, int numOfFrames) {

    path = aPath;
    frameWidth = width;
    frameHeight = height;
    framesPerRow = fpr;
    frames = numOfFrames;

    try {
        sheet = ImageIO.read(getClass().getResourceAsStream());
    } catch (IOException e) {
        e.printStackTrace();
    }

}

//Methods

public int getHeight() {
    return frameWidth;
}

public int getWidth() {
    return frameWidth;
}

public int getFramesPerRow() {
    return framesPerRow;
}

private BufferedImage getSprite(int x, int y, int h, int w) {
    BufferedImage sprite = sheet.getSubimage(x,y,h,w);
}

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;

}

}
  1. I'd encourage the use of a javax.swing.Timer to control the frame rate, rather than an uncontrolled loop 我鼓励使用javax.swing.Timer来控制帧频,而不是不受控制的循环
  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 一旦计时器“滴答”,您需要增加当前帧,获取要渲染的当前图像,并在JPanel上调用repaint
  3. Use Graphics#drawImage to render the image. 使用Graphics#drawImage渲染图像。

See... 看到...

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... Spritesheet类存在一系列级联的问题,除了它实际上不会编译之外,还存在一些问题,即您从某些方法返回错误的值并依赖于更好地计算出的值...

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... 作为两个主要例子脱颖而出...

跑

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. 在上面的示例中,我仅用100毫秒作为任意值。 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). 可能可以使用更多类似1000 / spritesheet.getFrameCount() ,它将为整个动画提供一整秒的时间(所有帧在一秒钟之内)。

You might need to use different values, for longer or short animations, depending on your needs 对于更长或更短的动画,可能需要根据需要使用不同的值

Here's some generic code for drawing an image to a JPanel. 这是一些用于将图像绘制到JPanel的通用代码。 This method is called to paint your JPanel component. 调用此方法以绘制您的JPanel组件。

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: 我也可以将run()修改为如下所示:

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

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

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