im learning Java2d, and im trying to animate my image in x coordinate using a Timer, but is not working, the idea is between a time frame the image x value increments a value making it to move, can someone figure out what is the problem in my code?
Here is the code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class Screen extends JPanel {
int posX;
Timer timer;
private BufferedImage image;
public Screen() {
setDoubleBuffered(true);
posX = 1;
timer = new Timer();
timer.scheduleAtFixedRate(new Anima(), 100, 10);
//Smile Icon
try{
image = ImageIO.read(getClass().getResource("/smily.png"));
}catch(IOException e){
e.printStackTrace();
}
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(image,this.posX,100,null);
}
class Anima extends TimerTask{
public void run() {
posX += 20;
repaint();
}
}
public void incMove() {
posX += 20;
}
}
Two main things:
The delay is small
The change is large
This means that the object can be moved out side of the visible bounds quickly, usually quicker then the screen can be realized on the screen.
You could play with the timing, but, animation is the illusion of change over time, you may find it better to reduce the size of the change rather the then the delay (although 100fps might be asking a bit much ;))
There is also no bounds checking, so the object is free to move off the viewable area, this might be desirable, but probably would have at least hinted towards the problem you were having.
As Swing is not thread safe (you shouldn't update the UI from outside the context of the EDT), you are also running the risk of inuring a thread race condition. In this example, it would probably be very hard to do, but the concept of how you are changing the state is dangerous.
Because Swing uses a passive rendering engine, a paint cycle may occur at anytime, without your interaction or knowledge, so you should be careful updating variables which the paint methods need to render the state outside of the context of the EDT.
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 Screen extends JPanel {
public static void main(String[] args) {
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 Screen());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
private int posX;
private int delta = 2;
public Screen() {
setDoubleBuffered(true);
posX = 1;
Timer timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
incMove();
repaint();
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
Graphics2D g2d = (Graphics2D) g;
g2d.drawRect(this.posX, 100, 10, 10);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void incMove() {
posX += delta;
if (posX + 10 > getWidth()) {
posX = getWidth() - 10;
delta *= -1;
} else if (posX < 0) {
posX = 0;
delta *= -1;
}
}
}
This simple example uses a Swing Timer as the primary engine, it reduces the amount of change (to 2) as well as adds bounds checking
Take a look at Concurrency in Swing and How to use Swing Timers for more details
Your code is working, but you are updating too fast.
Try
timer.scheduleAtFixedRate(new Anima(), 100, 1000);
To check your animation.
Related
The problem is obvious: I have a big paint command with a very large for loop and a componentResized that runs the paint command a huge amount of time, which makes my JPanel a black screen and unable to exit even when I press the x button(I must terminate it on eclipse), represented here:
package Testing;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class DrawTest extends JPanel implements ComponentListener {
public DrawTest(){
this.addComponentListener(this);
}
#Override
public void paint(Graphics g) {
int gridsize=8;
double width=getWidth()/gridsize;
for(double i=0;i<=getWidth();i+=width){
System.out.println("1");
g.drawLine((int)i,0,(int)i,getHeight());
}
double height=getHeight()/gridsize;
for(double i=0;i<=getHeight();i+=height){
System.out.println("2");
g.drawLine(0,(int)i,getWidth(),(int)i);
}
}
public static void main(String[] args){
JFrame frame=new JFrame("Fill all the squares with Ls");
DrawTest FillSquare=new DrawTest();
frame.add(FillSquare);
frame.setExtendedState( frame.getExtendedState()|frame.MAXIMIZED_BOTH );
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
#Override
public void componentHidden(ComponentEvent e) {
// TODO Auto-generated method stub
}
#Override
public void componentMoved(ComponentEvent e) {
// TODO Auto-generated method stub
}
#Override
public void componentResized(ComponentEvent e) {
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
System.out.println("Reading SMTP Info.");
}
};
Timer timer = new Timer(100 ,taskPerformer);
timer.setRepeats(false);
timer.start();
repaint();
}
#Override
public void componentShown(ComponentEvent e) {
// TODO Auto-generated method stub
}
}
I googled the problem and looked it up on stackoverthrow, and what came up was to use the Timer which prevent the componentResize to run a crazy amount of times when the user resizes the screen, but after implementing it, as shown in the code, it still doesn't seem to be working. I don't know if I did it wrong or what, it is my first time using it. After playing around and testing with the System.out.println();, I seem to run into another problem. The paint command's second loop seem to be going infinitely after the user moves the frame.
Thanks in advance!
Four things:
Don't do for(int i=1;i<=100000;i++){ inside the paint method. Paint methods should return as fast as possible, otherwise, you end up with issues like you have right now. Consider buffering the output so you don't have update it repeatedly and waste time.
Call super.paint before doing any custom painting
Override paintComponent instead of paint (and call super.paintComponent before doing any custom painting)
Every-time componentResized is called, you're creating another Timer, so if it get's called 100 times, you're creating a 100 Timers, this won't scale well
As a possible solution, you don't "need" to use ComponentListener, I tend to just override invalidate, but you will get similar results using either.
You should use a BufferedImage to represent what you want painted, this way, if the paint method is called for some reason other then the component been resized, you can just paint the BufferedImage and not worry about having to recreate the entire state from scratch.
You only need a single Timer, which is restarted each time you are notified that the the component has changed.
When the Timer is triggers the ActionListener, you would invalidate the current buffer and regenerate it. The example I've provided uses a SwingWorker to off load the work to a back ground thread, which will allow the UI to remain responsive while the buffer is updated it. You could display a little message in the paintComponent method when the buffer is null stating that the output is been regenerated, as an idea
import java.awt.Color;
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.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawTest extends JPanel {
private Timer resizeTimer;
private BufferedImage buffer;
private SwingWorker<BufferedImage, BufferedImage> bufferGenerator;
public DrawTest() {
resizeTimer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
buffer = null;
if (bufferGenerator != null) {
bufferGenerator.cancel(true);
}
// Create buffer in background
bufferGenerator = new BufferGeneratorWorker();
bufferGenerator.execute();
}
});
resizeTimer.setRepeats(false);
}
#Override
public void invalidate() {
super.invalidate();
resizeTimer.restart();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (buffer != null) {
g.drawImage(buffer, 0, 0, this);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected class BufferGeneratorWorker extends SwingWorker<BufferedImage, BufferedImage> {
#Override
protected BufferedImage doInBackground() throws Exception {
BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(Color.RED);
System.out.println("-- Started");
int i = 0;
while (i < 100000 && !Thread.currentThread().isInterrupted()) {
i++;
g2d.drawLine(0, i, getWidth(), i);
}
g2d.dispose();
System.out.println("-- Completed");
return img;
}
#Override
protected void done() {
try {
buffer = get();
repaint();
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
}
}
public static void main(String[] args) {
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("Fill all the squares with Ls");
DrawTest FillSquare = new DrawTest();
frame.add(FillSquare);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.dispose();
}
}
}
Take a look at Painting in AWT and Swing, Performing Custom Painting, How to use Swing Timers and Worker Threads and SwingWorker for more details
I have tried loads of ways, but none of them succeeded. they either didn't show the image, or they made the background image disappear... do you have any suggestions? Here is my code:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Main extends JFrame{
int x, y;
Image Dak;
Image Levels;
private Image dbImage;
private Graphics dbg;
public Main(){
setTitle("Help de Pieten");
setSize(2000, 720);
setResizable(true);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
try {
this.setContentPane(
new JLabel(new ImageIcon(ImageIO.read(new File("Image1.gif")))));
} catch (IOException e) {}
validate();
ImageIcon i = new ImageIcon("Image2.gif");
Levels = i.getImage();
x = 100;
y = 100;
}
public void paint(Graphics g){
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
public void paintComponent(Graphics g){
g.drawImage(Levels, x, y, this);
repaint();
}
public static void main(String[] args) {
Main main = new Main();
}
}
So how do I get images in front of the background without making the background dissapear?
To start with, avoid overriding the paint methods of top level containers like JFrame, they aren't double buffered and they have a complex component hierarchy with which you don't want to get involved with
Instead, start by extending from something JPanel, Swing components are double buffered by default, so you don't need to worry about implementing it all yourself, and overriding it's paintComponent method and performing your custom painting within it.
Have a look at Performing Custom Painting and Painting in AWT and Swing for more details.
Paint in Swing follows the "painters canvas" paradigm, that is, whatever is painted first, will be covered over by whatever is painted next, so to this end, make sure you paint your background first, followed by each layer in order you want it to appear.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Images {
public static void main(String[] args) {
new Images();
}
public Images() {
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 BufferedImage background;
private BufferedImage foreground;
public TestPane() {
try {
background = ImageIO.read(new File("background image"));
foreground = ImageIO.read(new File("foreground image"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return background == null ? new Dimension(200, 200) : new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (background != null) {
int x = (getWidth() - background.getWidth()) / 2;
int y = (getHeight() - background.getHeight()) / 2;
g2d.drawImage(background, x, y, this);
}
if (foreground != null) {
int x = (getWidth() - foreground.getWidth()) / 2;
int y = (getHeight() - foreground.getHeight()) / 2;
g2d.drawImage(foreground, x, y, this);
}
g2d.dispose();
}
}
}
I have a program that scales an image to the size of the screen. I currently have a component listener listening for a componentResized event, but this is not what I want. I would like the method to only be called one the user lift's there finger off their mouse, not as they are doing the resizing. This way, my image will not constantly be resizing to the user's specifications.
Thanks!
A solution is to supply a Swing Timer which is reset each time componentResized is called. This injects a small delay between the last resize event and the time you should perform the resize action.
import javax.swing.Timer;
//...
// Declare an instance variable...
private Timer resizeTimer;
//...
// Probably in you classes constructor
resizeTimer = new Timer(250, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
// Actually perform the resizing of the image...
resizeBackgroundImage();
}
});
// Don't want a repeating event...
resizeTimer.setRepeats(false);
//...
public void componentResized(ComponentEvent evt) {
resizeTimre.restart();
}
This basically, sets it up so that it will require 250 milliseconds between resize events before an attempt is made to resize the image. You can play around with the value to suit your own needs.
Updated with runnable example
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.image.BufferedImage;
import java.io.File;
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 RescaleTest {
public static void main(String[] args) {
new RescaleTest();
}
public RescaleTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage master;
private Image scaled;
private Timer resizeTimer;
public TestPane() {
try {
master = ImageIO.read(new File("/path/to/your/image"));
scaled = master;
} catch (IOException exp) {
exp.printStackTrace();
}
resizeTimer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
resizeBackground();
}
});
resizeTimer.setRepeats(false);
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
resizeTimer.restart();
}
});
}
protected void resizeBackground() {
// This is not my preferred scaling process, I prefer to use
// a divide and conqure approach and do so in the background
// where possible, but this is beyond the scope of the question...
if (getWidth() < getHeight()) {
scaled = master.getScaledInstance(getWidth(), -1, Image.SCALE_SMOOTH);
} else {
scaled = master.getScaledInstance(-1, getHeight(), Image.SCALE_SMOOTH);
}
repaint();
}
#Override
public Dimension getPreferredSize() {
return master != null ? new Dimension(master.getWidth(), master.getHeight()) : new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (scaled != null) {
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - scaled.getWidth(this)) / 2;
int y = (getHeight() - scaled.getHeight(this)) / 2;
g2d.drawImage(scaled, x, y, this);
g2d.dispose();
}
}
}
}
nb: The scaling used in this example is not my preferred method and was done for demonstration purposes only. See The Perils of Image.getScaledInstance() for details and Scale the ImageIcon automatically to label size for an alterantive approach...
If you put Toolkit.getDefaultToolkit().setDynamicLayout(false); right inside of main it will disable the frame from updating dynamically as you increase/decrease it's size. The ui will only be updated after you stop resizing.
import MainMenu.GameManager;
import java.awt.*;
import java.io.IOException;
public class Main {
Main(){
}
public static void main(String[] args) throws IOException {
GameManager manager = new GameManager();
Toolkit.getDefaultToolkit().setDynamicLayout(false);
}
}
I have tried several tutorials and searches to figure out how to accomplish what I am trying to do. Basically I have a JLayeredPane with two Jpanels inside it. One for my game's drawing surface and one for my gui, like a pause menu. I have a png file with transparencies that I want to be the background of my gui panel that popups when the user hits escape. No matter what I do, the background of the panel (even tried making it just a component) is always grey with my png file drawn over it.
I have tried what others have recommended such as the following.
setBackground(new Color(0,0,0,0));
and
setOpaque(false);
Neither of these has seemed to help and perhaps I am failing to do something else after these. I have traditionally done them after the constructor or within the constructor of a class that extends jpanel.
I am almost to the point where I am going to have one panel and draw everything myself but I would much rather use the built in java functions like boxlayouts, etc.
Edit Adding Working Example:
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.DisplayMode;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
public class Example {
private MyWindow gWindow;
public static void main(String argv[]) {
Example g = new Example();
g.gameLoop();
}
public Example() {
gWindow = new MyWindow();
// Initialize the keyboard listener
gWindow.frame().addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ESCAPE) // escape key, show menu
{
System.exit(0);
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
});
}
public void gameLoop() {
long lastLoopTime = System.currentTimeMillis();
while(true) {
// Used to calculate movement of sprites
long delta = System.currentTimeMillis() - lastLoopTime;
lastLoopTime = System.currentTimeMillis();
// Clear the canvas
Graphics2D g = (Graphics2D) gWindow.getBufferStrategy().getDrawGraphics();
g.setColor(Color.black);
g.fillRect(0,0,gWindow.frame().getWidth(), gWindow.frame().getHeight());
// Clean up graphics and flip buffer
g.dispose();
gWindow.getBufferStrategy().show();
// Small delay before next cycle
try { Thread.sleep(10); } catch (Exception e) {}
}
}
public class MyWindow {
private JFrame frame;
private JLayeredPane container;
private MyPanel gui;
private JPanel surface;
private Canvas canvas;
private GraphicsDevice vc;
private Dimension dm;
BufferedImage menuImg = null;
BufferedImage menuImgHighlight = null;
BufferedImage gSettings = null;
Font font = null;
public MyWindow() {
frame = new JFrame("Jumper");
vc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
DisplayMode display = vc.getDisplayMode();
dm = new Dimension(display.getWidth(), display.getHeight());
container = new JLayeredPane();
gui = new MyPanel();
gui.setLayout(new BoxLayout(gui, BoxLayout.Y_AXIS));
surface = new JPanel(new BorderLayout(0,0));
frame.add(container, BorderLayout.CENTER);
container.add(surface, new Integer(0), 0);
container.add(gui, new Integer(1), 0);
init_resources();
canvas = new Canvas();
surface.add(canvas);
gui.setBackground(new Color(0,0,0,0));
gui.setVisible(true);
gui.setOpaque(false);
surface.setVisible(true);
setFullScreen(display);
frame.setResizable(false);
frame.setVisible(true);
frame.pack();
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
setScreen(new Dimension(frame.getWidth(), frame.getHeight()));
frame.repaint();
}
});
canvas.setIgnoreRepaint(true);
canvas.createBufferStrategy(2);
canvas.setFocusable(false);
}
public JFrame frame() {
return frame;
}
public BufferStrategy getBufferStrategy () {
return canvas.getBufferStrategy();
}
public void setScreen(Dimension dim) {
int width = (int) dim.getWidth();
int height = (int) dim.getHeight();
this.dm = dim;
container.setPreferredSize(dm);
gui.setPreferredSize(dm);
surface.setPreferredSize(dm);
canvas.setBounds(0,0,width,height);
if(gSettings == null) {
gui.setBounds((int) ((dm.getWidth() - 200) / 2),
(int) ((dm.getHeight() - 200) / 2),
200,
200);
}
else {
gui.setBounds((int) ((dm.getWidth() - gSettings.getWidth()) / 2),
(int) ((dm.getHeight() - gSettings.getHeight()) / 2),
gSettings.getWidth(),
gSettings.getHeight());
}
gui.setBackground(gSettings);
surface.setBounds(0,0,width,height);
container.setBounds(0,0,width,height);
frame.validate();
}
public void setFullScreen(DisplayMode display) {
setScreen( Toolkit.getDefaultToolkit().getScreenSize());
frame.setUndecorated(true);
vc.setFullScreenWindow(frame);
if(dm != null && vc.isDisplayChangeSupported()) {
try {
vc.setDisplayMode(display);
}
catch(Exception e) {}
}
frame.validate();
}
private void init_resources() {
try {
gSettings = ImageIO.read(getClass().getResourceAsStream("/gui/settingsWindow.png"));
}
catch(Exception e)
{
System.out.print("Failed to load resources");
System.out.println();
}
}
}
public class MyPanel extends JPanel {
BufferedImage img = null;
public MyPanel() {
super();
setOpaque(false);
}
public void setBackground(BufferedImage img) {
this.img = img;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(img != null) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(img, 0, 0, null);
}
}
}
}
I've not tested this, but, instead of calling super.paintComponent at the end of you paint method, try calling at the start....
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(img != null) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(img, 0, 0, null);
}
}
The reasoning for this, is one of the jobs of paintComponent is clear the graphics context and ready it to be painted on. Event if the component is transparent, it must still clear/wipe the graphics context of anything that has previously been painted on it. The graphics context is a shared resource, meaning that all the components within a given window may share the same graphics context, so it gets a little dirty if it's not "wiped" first ;)
You may also have issues with mixing heavy and light weight components, but seen as you adding the light weight components to the heavy weight component, it may not be an issue, but it's worth putting in the back of your mind... ;)
JComponent is transparent by default ;)
Try to apply some Physics over here...
The visible white color is combination of RGB max values...
If you are keeping RGB values to Minimum it will give you dark color (Black) and not the transparent one..
try to implement below methods..
(your component).setOpaque(false);
(your component).setContentAreaFilled(false);
(your component).setBorderPainted(false);
Hope so this will help you...
I am using a Swing Timer in my game but when the game is running it appears to have moments when it runs smoothly and moments when it slows down.
Why is the time fluctuating?
And how do I fix it?
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main extends JFrame {
public Main() {
super("JFrame");
// you can set the content pane of the frame
// to your custom class.
setContentPane(new ImagePanel());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800, 400);
setResizable(false);
setVisible(true);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new Main();
}
class ImagePanel extends JPanel {
Timer movementtimer;
int x, y;
public ImagePanel() {
x = 0;
y = 0;
movementtimer = new Timer(12, new ActionListener() {
public void actionPerformed(ActionEvent e) {
long timstarted = System.currentTimeMillis();
moveImage();
repaint();
long timefinished = System.currentTimeMillis() - timstarted;
System.out.println(timefinished + " to run");
};
});
movementtimer.start();
}
public void moveImage() {
x++;
y++;
if (x > 800) {
x = 0;
}
if (y > 400) {
y = 0;
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(0, 0, 800, 400);
g.setColor(Color.BLUE);
g.fillRect(x, y, 50, 50);
}
}
}
Here is an example of my code. In my actual program I am drawing Images and not just a rectangle. There is also a lot of collision detection and other small calculations happening.
Also, here is a link to the Jar file for the game so you can run it and (hopefull) see what I mean. http://dl.dropbox.com/u/8724803/Get%20To%20The%20Chopper%201.3.jar
Thanks
Tom
Because the rendering is trivial, I find this variation of your example to be very smooth. The render time is well below a half millisecond, so the 12 millisecond period (~83 Hz) is plenty of time to finish a frame, typically taking less that 10% of one core. As the render time grows, the timer thread becomes saturated, and events are coalesced. The effect is magnified on a single core, as rendering competes with garbage collection and external processing demands. Java is not a real-time system, and not all schedulers are created equal.
You'll certainly want to profile your actual code, as suggested here, to see any correlation with fluctuating performance. One alternative approach is to lengthen the period (decrease the frequency) to meet your rendering deadline and use a larger increment in moveImage() to get the same velocity.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main extends JFrame {
private static final int W = 800;
private static final int H = 400;
public Main() {
super("JFrame");
this.add(new ImagePanel());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
setSize(W, H);
this.setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Main();
}
});
}
class ImagePanel extends JPanel {
Timer movementTimer;
int x, y;
public ImagePanel() {
x = 0;
y = 0;
movementTimer = new Timer(12, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
moveImage();
repaint();
}
});
movementTimer.start();
}
public void moveImage() {
x++;
y++;
if (x > W) {
x = 0;
}
if (y > H) {
y = 0;
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
long start = System.nanoTime();
g.setColor(Color.RED);
g.fillRect(0, 0, W, H);
g.setColor(Color.BLUE);
g.fillRect(x, y, 50, 50);
double delta = (System.nanoTime() - start) / 1000000d;
g.drawString(String.format("%1$5.3f", delta), 5, 15);
}
}
}
The Swing Timer is notorious for its inaccuracy. Use something else instead.
On prompting, I've decided to undelete this post. OTOH most of the extra information that makes it worth reinstating comes from trashgod, so I'll merely quote/paraphrase their comments.
I'd argue that it's reasonably accurate but easy to saturate.
And trashgod goes on to add in a separate comment that:
(I) might cite Clock Quality. javax.swing.Timer isn't very accurate, but it has usefully precise resolution on modern platforms.
In the panel constructor do:
setBackground(Color.RED);
Then you do not need to erase the background, as you are calling super.paintComponent.
In general calculate positions on actual time passed (System.nanoTime()) and do not rely on timer frames. There are a couple of gaming frameworks out there, so maybe it is worth looking at their solution. I liked the sample.