Porlus
JGO n00b  Posts: 22
|
 |
«
on:
2012-02-07 11:57:54 » |
|
Hello, I've got a problem with a spritesheet I'm trying to make using Java 2D. I first load an external PNG into a BufferedImage using the ImageIO class and using a Sprite class I pass in the buffered spritesheet, where I want it in the frame (x, y), the top left hand point of the tile from the spritesheet that I want to extract (xOffset, yOffset) and the width and height of the tile. The external PNG is just a spritesheet containing the characters a-z, 0-9 that I made in photoshop, with each tile being 20x20 pixels. I'll post the code so you can see exactly what I mean, but what the problem is, is that when a new character is drawn using the Component classes paint method, the previously rendered characters disappear. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| import java.awt.BorderLayout; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException;
import javax.imageio.ImageIO; import javax.swing.JFrame;
public class Main { public static final int WIDTH = 300; public static final int HEIGHT = 300; private Screen screen; public Main() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.setSize(WIDTH, HEIGHT); frame.setLocationRelativeTo(null); frame.setResizable(false); frame.setVisible(true); BufferedImage ss = null; try { ss = ImageIO.read(new File("charSprite.png")); } catch(IOException e) { e.printStackTrace(); } screen = new Screen(WIDTH, HEIGHT); screen.loadImage(new Sprite(ss, 0, 0, 20, 20).getSprite(), 20, 20); screen.loadImage(new Sprite(ss, 0, 0, 20, 20).getSprite(), 40, 40); frame.add(screen); } public static void main(String[] args) { new Main(); } } |
Here's the Sprite class, for dissecting the tile from the spritesheet: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import java.awt.image.BufferedImage;
public class Sprite { private BufferedImage sprite; public Sprite(BufferedImage ss, int xOffset, int yOffset, int width, int height) { sprite = ss.getSubimage(xOffset, yOffset, width, height); } public BufferedImage getSprite() { return sprite; } } |
Here's the screen class which holds the bufferedimage containing each element within the screen: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import java.awt.Component; import java.awt.Graphics; import java.awt.image.BufferedImage;
public class Screen extends Component { private BufferedImage display = null; private int[] pixels; public Screen(int w, int h) { display = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); pixels = new int[w * h]; } public void loadImage(BufferedImage im, int x, int y) { int[] p = im.getRGB(0, 0, im.getWidth(), im.getHeight(), null, 0, im.getWidth()); for(int y1 = 0; y1 < im.getHeight(); y1++) { for(int x1 = 0; x1 < im.getWidth(); x1++) { pixels[(y1 + y) * display.getWidth() + (x1 + x)] = p[y1 * im.getWidth() + x1]; } } display.setRGB(0, 0, display.getWidth(), display.getHeight(), pixels, 0, display.getWidth()); repaint(); } public void paint(Graphics g) { g.drawImage(display, 0, 0, display.getWidth(), display.getHeight(), null); } } |
I'm quite new to this and just trying to understand how swing works so any help would be appreciated. Thanks! Paul
|
|
|
|
|
ra4king
JGO Kernel      Posts: 3160 Medals: 196
I'm the King!
|
 |
«
Reply #1 on:
2012-02-07 19:14:17 » |
|
JFrame's ContentPane's default layout is BorderLayout, which uses 1 component for the center, and 1 component for each of the 4 sides. Calling JFrame.add(Component) adds it to the center component. Any further calls will replace the previous one. What you need is 1 component that you draw everything to.
|
|
|
|
Porlus
JGO n00b  Posts: 22
|
 |
«
Reply #2 on:
2012-02-07 19:53:26 » |
|
Thanks for the reply. Yeah, I've just thought to add a screen class to keep a pixels by pixels reference of everything on the screen, then I can just pass in bufferedimages, extract their pixel arrays and then overlay the new image within a nested loop in the correct position.
I've updated my original post now to what I currently have and it seems to work perfectly, except the images only seem to appear roughly 50% of the time once the program is ran. If I minimize the program and maximize it again the images appear, though. Any idea what could be causing that?
|
|
|
|
|
Games published by our own members! Go get 'em!
|
|
GabrielBailey74
Full Member   Posts: 157 Medals: 2
Owner of Elite Demons R.I.P
|
 |
«
Reply #3 on:
2012-02-07 19:56:24 » |
|
Could be because you're only adding the Screen there once, you'd need to add a loop of somekind, than handle your paint method in there so it's constantly repainting.
|
|
|
|
Porlus
JGO n00b  Posts: 22
|
 |
«
Reply #4 on:
2012-02-07 20:05:25 » |
|
I think it stays as it is until you clear the pixel array and update the screen buffer. I'll have to create a clear buffer method or something to clear that out. I think the problem lies around here in the main class: 1
| ss = ImageIO.read(new File("charSprite.png")); |
As when the pictures don't render, they both don't and likewise when they do. So that would suggest that it was a problem with the spritesheet I'm feeding into the sprite classes. Not really sure about a solution though.
|
|
|
|
|
GabrielBailey74
Full Member   Posts: 157 Medals: 2
Owner of Elite Demons R.I.P
|
 |
«
Reply #5 on:
2012-02-07 20:08:12 » |
|
What I would do, and what I do do is: Take the spritesheet, chop it down into proper frames, give it a pinkish background color (if you want it to be transparent), load it through transparency and ignore those pink pixels. Thought i've never tried grabbing a subImage of a image.. You could make your Screen class extend a JPanel, than add the JPanel to the frame, make your Main class "extends Screen". You could do your Screen class like this: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import java.awt.Component; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.awt.Graphics2D; import java.awt.RenderingHints;
public class Screen extends JPanel { private BufferedImage display = null; private int[] pixels; public Screen(int w, int h) { display = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); pixels = new int[w * h]; } public void loadImage(BufferedImage im, int x, int y) { int[] p = im.getRGB(0, 0, im.getWidth(), im.getHeight(), null, 0, im.getWidth()); for(int y1 = 0; y1 < im.getHeight(); y1++) { for(int x1 = 0; x1 < im.getWidth(); x1++) { pixels[(y1 + y) * display.getWidth() + (x1 + x)] = p[y1 * im.getWidth() + x1]; } } display.setRGB(0, 0, display.getWidth(), display.getHeight(), pixels, 0, display.getWidth()); repaint(); } public void cycle() { repaint(); }
public void paint(Graphics g) { super.paint(g); final Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.drawImage(display, 0, 0, display.getWidth(), display.getHeight(), null); } } |
And your Main class like this: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import java.awt.BorderLayout; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException;
import javax.imageio.ImageIO; import javax.swing.JFrame;
public class Main extends Screen implements Runnable { public static final int WIDTH = 300; public static final int HEIGHT = 300; private Screen screen; public Main() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.setSize(WIDTH, HEIGHT); frame.setLocationRelativeTo(null); frame.setResizable(false); BufferedImage ss = null; try { ss = ImageIO.read(new File("charSprite.png")); } catch(IOException e) { e.printStackTrace(); } screen = new Screen(WIDTH, HEIGHT); screen.loadImage(new Sprite(ss, 0, 0, 20, 20).getSprite(), 20, 20); screen.loadImage(new Sprite(ss, 0, 0, 20, 20).getSprite(), 40, 40); frame.add(new Screen()); frame.setVisible(true); } public static void main(String[] args) { new Main(); }
@Override public void run() { super.cycle(); } } |
So your Main class is automaticly calling that "cycle" method from your Screen class which is than telling it to "repaint", which was changed to "super.paint(g)", which would paint your GUI/Main screen. Hope it helps some mate (Would need proper imports/changes made).
|
|
|
|
Porlus
JGO n00b  Posts: 22
|
 |
«
Reply #6 on:
2012-02-07 20:36:51 » |
|
How foolish of me. :] Just moved the setvisible below the add call and now it's rendering in time. Cheers both of you for your help!
|
|
|
|
|
GabrielBailey74
Full Member   Posts: 157 Medals: 2
Owner of Elite Demons R.I.P
|
 |
«
Reply #7 on:
2012-02-07 20:38:17 » |
|
Np mate, i'm fairly newish. Need any help drop me a pm.
|
|
|
|
|