I am creating a Java game, and in this, I have a plane that can be moved around by the user, however if the plane hits any of the sides, it should explode - meaning there should be an explosion at that location. I used links such as: Why gif animation doesn't animate when using it in paintComponent()? and other ones to find out how to animate a gif, however, my code doesn't work.
Here is the relevant code:
ImageIcon ii = new ImageIcon(this.getClass().getResource("explosion.gif"));
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(ii.getImage(), planeX, planeY, this);
System.out.println("drawn");
Although "drawn" is being printed out on the console, the explosion gif doesn't load. I checked the file name and it was correct. However, I did use the same gif somewhere else in my code and it worked there, so is there something that prevents having a gif being used twice in Java?
Here is my entire paintComponent() method:
super.paintComponent(g);
setFocusable(true);
requestFocus();
background = new ImageIcon("bg.png").getImage();
g.drawImage(background, -6, 0, 700, 700, null); //background of JPanel
if (!hit) g.drawImage(plane, planeX, planeY, planeLen, planeHei, null); //only draw plane if not exploded
else if (count == 0)
{
ImageIcon ii = new ImageIcon(this.getClass().getResource("explosion.gif"));
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(ii.getImage(), planeX, planeY, this); //doesn't work
System.out.println("drawn"); //printed
}
title = new ImageIcon("title.png").getImage(); //transparent, is drawn
g.drawImage(title, -9, 20, 700, 218, null);
title2 = new ImageIcon("title2.png").getImage(); //transparent, is drawn
g.drawImage(title2, 10, 245, 670, 20, null);
I have checked title and title2 and they are transparent, meaning that the explosion should be seen. I even tried it below those two images, and the explosion still is not visible. I am using JButtons and KeyListeners on this panel as well.
Please let me know if I should add anything else, and thank you in advance!
You should always pass this as the final argument to drawImage in a painting method. If an image has multiple frames, or the image is loaded in the background, this tells the component to paint itself again as more of the image becomes available.
Painting of components happens frequently. It is controlled by the system, not just by code calling the component’s repaint methods. In fact, it’s common for painting to happen many times per second. Many system events beyond a programmer’s control can trigger painting, such as a user moving a window, or another window moving, or even the user moving the mouse.
For this reason, painting should not perform slow operations like loading files. Move the loading of your images out of your painting method, and into the class’s constructor. Store the Image objects in private fields, where paintComponent can use them again and again without reloading them for every painting run.
Since the timing of painting is beyond your control, it is important that you never change the state of a component in a painting method. Move setFocusable and requestFocus out of your paintComponent method. Those calls should be in the code that builds your component and adds it to a window.
Finally, be aware that loading images from files will not work if you ever package your application in a .jar file. A .jar file is a single compressed archive; the entries in it are not files anymore, they’re just sequences of bytes inside the archive. Loading all files using getResource will address this, since getResource looks in every .jar file in the classpath.
Never mind, I had to make a duplicate of "explosion.gif". It works fine now!
However, please let me know if there is another way of doing this, much faster AND efficiently. Thanks!
Related
I need to draw strings on a painted image. The project I am working on also requires the string to move around the screen at least 22 times in a second. Hence, its position can be anywhere on the image. So, redrawing the image with the string on it won't be possible as I feel there are better ways of doing this and that would unnecessarily consume resources redrawing the whole image. I have also tried using panel.getGraphics and then painting on the image but then all the drawn text is all over the screen(the code is below). I was wondering if someone could guide me in the right direction on how I can draw text over a paintedImage but it also needs to reset its position when required. The code I have tried that doesn't reset its previous position is below.
Original Panel with the Image:
public class PanelForImages extends JPanel{
private BufferedImage image;
public PanelForImages(File image) throws IOException{
//this.image = image;
//URL resource = getClass().getResource("so2.jpg");
this.image = ImageIO.read(image);
}
#Override
public void paintComponent(Graphics g){
//super.paint(g);
//super.paintComponents(g);
super.paintComponent(g);
//g.drawImage(image, 3, 4, this);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
g.drawString("HELLOOOOOOOOOOOOOOOOOOOOOOOO", 400, 400);
//repaint();
}
}
Method with which I am trying to draw the string over the image.
public void drawFixationsOnFrame(GazeData gazeData){
Graphics g = this.panelForImages.getGraphics();
g.drawString("TEsting 123", (int)gazeData.smoothedCoordinates.x, (int)gazeData.smoothedCoordinates.y);
g.dispose();
jF.revalidate();
}
I have also tried making a new panel and then adding it to the current one but it doesn't seem to work. I am not sure how I can make it so it comes on top of the panelForImages without it hiding panelForImages.
The project I am working on also requires the string to move around the screen at least 22 times in a second. Hence, its position can be anywhere on the image. So, redrawing the image with the string on it won't be possible as I feel there are better ways of doing this and that would unnecessarily consume resources redrawing the whole image.
Then don't redraw the whole image. The JComponent repaint(...) method has an override, one that allows you to repaint only a select rectangle (check out the JComponent API for more on this). You will want to move the image in a Swing Timer, then repaint its old location (to get rid of the old String image) and repaint its new location. If you need the dimensions of the String, then use FontMetrics to help you get its bounding rectangle.
I note that your code has two other issues that are worrisome, some of it commented out:
Calling getGraphics() on a component to get its graphics component and draw with it -- you don't want to do this as this will result in a Graphics object that is short-lived risking broken images or a NPE
Calling repaint() from within the paintComponent. This is a bad and completely uncontrolled way of doing animation. Instead use a Swing Timer so you can have complete control of the animation and so you don't use paintComponent for something it was not intended to be used for.
I was working on creating a fade-in animation and I ran into a problem where whenever I try to draw a BufferedImage with:
g2d.drawImage(unknown.getScaledInstance(100, 200, BufferedImage.SCALE_SMOOTH),
10,button.getLocation().y - 200, this);
after using
.setCompositeAlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity)
My JPanel's paint method was constantly being repeatedly and continuously being called. I tried commenting out the drawImage bit and everything worked fine again. I also noticed that when I called the paint method from another class using:
answerPanel.paint(answerPanel.getGraphics());
The paint method stopped being continuously called.
So could anyone shed some light on why the drawImage line is causing the paintMethod to be constantly called until I call it from another class? Thanks!
Agree with #user2864740, you should never call paint(...) directly, but I don't think that is the root of the problem here.
The last parameter to drawImage(...) (e.g. this) is an ImageObserver. It receives callbacks whenever an Image is updated (changed), and will cause a repaint() (which will eventually call paint()).
When you invoke Image.getScaledInstance(...) this will immediately return a new Image instance, but the actual scaling (or, resampling, really) will happen later. The image observer will handle "automagic" repaints of your component, as soon as (parts of) the scaled image is done. You component will be requested to repaint one or more times.
You should be able to observe the effects of this, by passing null as the last parameter, instead of this (probably, nothing will be painted, but you'll avoid the repaint loop).
The reason your component will go into a repaint loop, is because each time the component is asked to repaint itself, a new scaled instance is returned, causing a restart of the scaling and the automatic repainting by the image observer, as described above.
To fix the problem, don't use getScaledInstance() and instead, try:
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2d.drawImage(unknown, 10, button.getLocation().y - 200, 100, 200, this);
Another way, would be to make sure you create the rescaled image only once, and continue using it for each repaint:
Image scaled;
public void paint(Graphics g) {
...
if (scaled == null) {
scaled = unknown.getScaledInstacnce(100, 200);
}
...
g2d.drawImage(scaled, 10, button.getLocation().y - 200, this);
}
Hi I've been trying to get rid of my flickering on a JFrame app. Have searched around and seen that setDoubleBuffered(true) can be used for paintComponent of Jpanel, but not paint method of JFrame, nor applets.
Managed to diminish but not eliminate flickering by introducing the instructions this.createbufferstrategy(2) within paint method, and further reduced flickering with the instruction this.setignorerepaint(true) inside paint.
But I finally found an example of code that completely removes flickering and it works by drawing the static elements within the update function.
Tested fillRect within update in an applet and it works, but when copy pasted into a regular java application it does not work there within the jframe's update function.
Here's the code
Graphics graphics;
Image image;
public void update(Graphics g)
{
if (image == null) {
image = createImage(this.getWidth(), this.getHeight());
graphics = image.getGraphics(); }
graphics.setColor(Color.blue);
graphics.fillRect(0, 0, this.getWidth(), this.getHeight());
g.drawImage(image, 0, 0, this);//}
With an empty paint function this draws to the screen and fills it with blue in an applet, it also gets rid of flickering. But in a normal application of jframe it does nothing.
What needs to be done to allow either fillRect or drawImage to work from within update in a non applet regular application environment?
BTW, I'm a bit new to this if the graphics object is being modified itself by calling fillRect, how does that modify the image object? Because drawImage is necessary for the screen to turn blue.
PS I've tried not using both createbufferedstrategy and setignorerepaint, and nothing changes.
When using setIgnorePaint(true), its not allowing you to mess with the Graphics.
I'm guessing by the looks of your update(Graphics g) parameter, you call that within your paint method, using the paintMethod's graphic g to paint (inside paint method, you call update(g))
If you ignore paint, its not gonna allow you to use the paintComponent's Graphic parameter.
Post all code that includes graphics (Where you made your strategy, where you're calling this method, ect..)
The flickering is a pretty mainstream issue with the strategy, and I can't promise that I'll be able to fix it (my friend brought the problem up to me a while ago, but i didnt care enough to try and figure it out), but that should at least explain why your graphics aren't rendering
I have an application that plots images on a JPanel. I want to dispose of the GUI altogether, so that the application is called from code only, with no user interface, and so that the images are saved to a gif file only.
I have experimented with code from this question, and it seems to work fine when I call the save gif code from within the old GUI. However, the save gif code is not seeming to work when I remove the GUI, and I am wondering if that might be because the JPanel it is trying to print has not actually made it to a GUI container like a JFrame, etc.
The reason I am not posting code is that it is too verbose, an I am just asking for a simple, boilerplate answer.
I have experimented with writeablerasters for other applications. But that would require a major recoding project for this application, which paints by using the graphics.drawLine() method. Can anyone suggest a code-efficient way to take contents of a "phantom" JPanel, get it into a BufferedImage, and then save it as a gif without ever putting the JPanel in a GUI?
Since your application draws on a JPanel then it just needs a Graphics object. You can use one from a BufferedImage which then you will save to file. Example code:
BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
// Pass this graphics object to your application to perform the drawing
g.setColor(Color.red);
g.drawRoundRect(10, 10, 20, 20, 5, 5);
// save it to disk
ImageIO.write(image, "png", new File("test.png"));
This is difficult to answer without any example code, however.
If the component you are trying to render is not displayed on the screen, it is likely that it has not being laid out.
Before painting, you should make sure you size the component.
componentToBePainted.setSize(componentToBePainted.getPreferredSize());
You should also be using print or printAll over paint. When not attached to a native peer, paint can cause issue
I am working on some NetBeans platform app, and i am currently stuck on some detail in Visual Library. Ok, here is problem. I have Visual Editor for my app, with pallet, scene and everything works great, just there is a problem when i am dragging icons from pallet to scene. They are not displayed during drag event, i would like to create that effect, can someone help on this?
I do this in two phases:
1) Create a screenshot (an image) of a palette element. I create the screenshot lazily and then cache it within the view. To create the screenshot, you can use this snippet:
screenshot = new BufferedImage(getWidth(), getHeight(), java.awt.image.BufferedImage.TYPE_INT_ARGB_PRE);// buffered image
// creating the graphics for buffered image
Graphics2D graphics = screenshot.createGraphics();
// We make the screenshot slightly transparent
graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f));
view.print(graphics); // takes the screenshot
graphics.dispose();
2) Paint the screenshot on the receiving view. When the drag gesture is recognized, find a way to make the screenshot available to the receiving view or one of its ancestor (you could make it available on the frame or its content pane, depending on where you want to make the screenshot drag available) and paint the image within the paint method. Something like this:
a. Make the screenshot available:
capturedDraggedNodeImage = view.getScreenshot(); // Transfer the screenshot
dragOrigin = SwingUtilities.convertPoint(e.getComponent(), e.getDragOrigin(), view); // locate the point where the click was made
b. As the mouse is dragged, update the location of the screenshot
// Assuming 'e' is a DropTargetDragEvent and 'this' is where you want to paint
// Convert the event point to this component coordinates
capturedNodeLocation = SwingUtilities.convertPoint(((DropTarget) e.getSource()).getComponent(), e.getLocation(), this);
// offset the location by the original point of drag on the palette element view
capturedNodeLocation.x -= dragOrigin.x;
capturedNodeLocation.y -= dragOrigin.y;
// Invoke repaint
repaint(capturedNodeLocation.x, capturedNodeLocation.y,
capturedDraggedNodeImage.getWidth(), capturedDraggedNodeImage.getHeight());
c. paint the screenshot in paint method:
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.drawImage(capturedDraggedNodeImage, capturedNodeLocation.x,
capturedNodeLocation.y, capturedDraggedNodeImage.getWidth(),
capturedDraggedNodeImage.getHeight(), this);
}
Instead of calling repaint() and perform the painting in the paint() method, you could invoke paintImmediately() as the mouse moves but the rendering will be a lot poorer and you could observe some flickering, so I would not recommend that option. Using paint() and repaint() provides a better user experience and a smooth rendering.
If I hear you well, you are creating a graphical editor of some sort, with drag and drop of elements, and you want to create an effect during that drag and drop?
If so, you basically need to create a ghost of the object you're dragging and attach it to the move of the mouse. Easier said than done, of course, but you get the gist.
So what you need is to take the image of what you're dragging (it shouldn't be too much trouble) and move it according to the position of the mouse (think of substracting the relative position of the mouse cursor in the object you're dragging).
But I think that kind of code is available somewhere. I'd advise you to look that up :
http://free-the-pixel.blogspot.fr/2010/04/ghost-drag-and-drop-over-multiple.html
http://codeidol.com/java/swing/Drag-and-Drop/Translucent-Drag-and-Drop/
Hope that helps you!