Why do my java graphics lag so much? - java

I know there are already many questions about slow java graphics, but none of them have solved my problem. The lag in my program seems very unnatrual, and has no explanation as far as I can tell.
I am creating a 2D game, and it has terrible lag. At first I thought it was from all the graphics being drawn in the game, so I did many things to try and improve performance, but none of them changed anything.
To make sure it was the game that was causing lag, I made a very simple (badly written) program that just moves a square across the screen.
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main extends JPanel{
static Main main = new Main();
int x;
int y;
public static void main(String[]args){
JFrame frame = new JFrame("Test");
frame.setSize(1920, 1080);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(main);
frame.setVisible(true);
while(true){
main.update();
main.repaint();
try {
Thread.sleep(1000 / 60);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void update(){
x++;
y++;
}
public void paint(Graphics g){
super.paint(g);
g.fillRect(x, y, 100, 100);
}
}
To my suprise, this simple program lags very badly too! I have been coding the game on linux (ubuntu GNOME 14.10), but I have run it on my Windows 7 partition as well, and it doesn't change the performance. I have also updated all my graphics drivers. My computer can also run other java programs such as Minecraft with no lag at all. I even tried importing the project file for this tutorial into eclipse and running it and it ran fine: https://www.youtube.com/watch?v=ar0hTsb9sxM
Why is this happening, and how can I fix it?

I put Toolkit.getDefaultToolkit().sync(); in my render method which appears to fix it. However you should put a If os is linux before that.

Related

Graphics 2D not drawing images in Java

I'm following a Java course, and the current idea is to draw an image using Java Graphics2D. I'm following the steps one by one, but it seems not to be drawing anything. The panel is shown within the frame and everything is correct, but the image is not drawn. I'm using Java 15 and the course is Java 13.
JPanel class code:
public class MyPanel extends JPanel implements ActionListener {
Image ball;
int x = 0;
int y = 0;
MyPanel(){
this.setPreferredSize(new Dimension(PANEL_WIDTH,PANEL_HEIGHT));
this.setBackground(Color.BLACK);
ball = new ImageIcon("ball.png").getImage();
}
public void paint(Graphics g){
Graphics2D G2D = (Graphics2D) g;
G2D.drawImage(ball,x,y,null);}
JFrame class code:
public class MyFrame extends JFrame{
MyPanel panel;
MyFrame(){
panel = new MyPanel();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(panel);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
Main class code:
new MyFrame();
I picked out most of the relevant code.
First, I recommend reading through Performing Custom Painting and Painting in AWT and Swing to get a better idea of how painting in Swing should work.
I then suggest reading through Reading/Loading an Image
The "problem" with ImageIcon is that
It doesn't report any errors if the load fails
It loads the image in a second thread, which means you don't know (easily) when it's available
ImageIO on the hand will throw an error if the image can't be loaded, which is much easier to diagnose, and will only return AFTER the image is fully loaded and available.
One concept which can be hard to grasp when your starting is the concept of "embedded" resources. Java allows you to package "resources" along with your code. These resources live within the context of your programs class path and make it MUCH easier to load when compared to having to deal with external files.
Have a look Packaging and accessing run-time resources in a runnable Jar for some basics.
Depending on the IDE you're using, it's usually pretty easy to "copy" these resources into your project/src and allow the IDE to package itself.
The problem with a question like this is it's very, very hard for anyone to truely diagnose, as there are so many reasons why the image might not have loaded and the solution is usual one of trial and error.
Me, I'd start by just drawing some lines/rectangles and make sure that paint is been called. I'd then look at things like the image's size, to make sure it's not something like 0x0.
Runnable example
This is a simple runnable example based on comments I made above. I'm using NetBeans and the image was stored in the "Source Package" (so, along with the other source code) under the /images package
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage beachBall;
public TestPane() {
try {
beachBall = ImageIO.read(getClass().getResource("/images/BeachBall.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (beachBall == null) {
return;
}
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - beachBall.getWidth()) / 2;
int y = (getHeight() - beachBall.getHeight()) / 2;
g2d.drawImage(beachBall, x, y, this);
g2d.dispose();
}
}
}
It really looks like your image is not loaded.
As #MadProgrammer just told, new ImageIcon("ball.png") does not raise any error, and getImage() will always return something (not null), even if the file is not properly loaded.
To make sure your image is available, you can try ball.getWidth(null), and
if it returns -1, then something went wrong.
You can check the root path used by the JVM ("execution" location) with System.getProperty("user.dir"), the image file has to be exactly in this folder.
I tried your code with java 1.8 and it works well.

My Window is Forced to Maximized on Java Applet/JFrame

I've tried running both an Applet and a JFrame following certain YouTube tutorials, but when they run on my computer, they ignore the size I specify for the window, and forcibly open maximized and will simply not resize.
Here's a simple example Applet I ran:
package pong;
import java.applet.Applet;
import java.awt.*;
public class Pong extends Applet{
final int WIDTH = 700, HEIGHT = 500;
public void init() {
this.resize(WIDTH, HEIGHT);
}
public void paint(Graphics g) {
}
public void update(Graphics g) {
}
}
I cannot resize the window/panel. I don't know if this is an issue with my computer Yoga 2 Pro.
Any help is greatly appreciated.
EDIT: I think perhaps I wasn't clear enough. I am simply not managing to correctly view ANY JPanel or Applet. They display on my Yoga 2 Pro as maximized, and with their content being strange as well. In this example I'm simply trying to get a 700x500 window, but Java ignores this fact on my computer.
Thanks

Resizing JFrame in one dimension

I am trying to re-size my JFrame in only one dimension (width in this case) and I found this question JFrame re-sizable height ONLY which gave me a good answer for doing so;
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
setSize(new Dimension(preferredWidth, getHeight()));
super.componentResized(e);
}
});
and I edited it slightly so that instead of locking width it locked height to a certain size and allowed width to be re-sizable.
import java.awt.*;
import java.awt.event.*;
import java.awt.Dimension;
import java.awt.Component;
import javax.swing.*;
import java.io.*;
import java.lang.*;
public class mcve
{
JFrame numberConversionWindow = new JFrame("Number Conversion");
public void numberConvertGUI()
{
numberConversionWindow.setBounds(10, 10, 420, 300);
numberConversionWindow.addComponentListener(new ComponentAdapter()
{
#Override
public void componentResized(ComponentEvent e)
{
numberConversionWindow.setSize(new Dimension(numberConversionWindow.getWidth(), 300));
super.componentResized(e);
numberConversionWindow.repaint();
}
});
numberConversionWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
numberConversionWindow.setLayout(new GridLayout(1,1));
numberConversionWindow.setVisible(true);
}
public static void main(String[] args)
{
mcve mc = new mcve();
mc.numberConvertGUI();
}
}
However there is a problem with this code. It often glitches. As I start to re-size it to make it wider there is a black line which flickers just before the frame re-sizes.
The next glitches are caused when re-sizing the height. It may just leave a large black area instead of snapping back to 300, and sometimes it will not snap back at all.
So my question is how can I improve this code to prevent these glitches from happening and instead of just having a height which it will snap back to can I disable the ability to re-size the height? If I can disable this ability, how would I do so?
Edit
I have also tried the following code
numberConversionWindow.setMinimumSize(new Dimension(420, 300));
numberConversionWindow.setMaximumSize(new Dimension(numberConversionWindow.getWidth(), 300));
However this still lets me re-size the height of the JFrame.
Any Help would be greatly appreciated
Edit 2
I have attempted to try and use another answer from JFrame re-sizable height ONLY. My problem for this attempt is a can't find symbol question.
I have the code
public class NumberConverter
{
...
static
{
if (System.getProperty("sun.arch.data.model").equals("32"))
{ // 32-bit JVM
System.loadLibrary("my32bitdll");
System.out.println("Running 32-bit JVM");
}
else
{
// 64-bit JVM
System.loadLibrary("my64bitdll");
System.out.println("Running 64-bit JVM");
}
}
//public static native int getComponentHWND(numberConversionWindow);
//public static native int setMinMaxResizeBoundaries(getComponentHWND, 420, 300, numberConversionWindow.getWidth(), 300);
public void numberConvertGUI()
{
numberConversionWindow.setBounds(10, 10, 420, 300);
int hwndForJFrame = getComponentHWND(numberConversionWindow);
numberConversionWindow.setMinMaxResizeBoundaries(hwndForJFrame, 420, 300, numberConversionWindow.getWidth(), 300);
...
}
public static void main(String[] args)
{
NumberConverter nC = new NumberConverter();
nC.numberConvertGUI();
}
}
When I compile I get the errors cannot find symbol - method setMinMaxResizeBoundaries(int,int,int,int,int) and cannot find symbol - getComponentHWND(numberConversionWindow). I would greatly appreciate someone explaining to me how I am meant to use setMinMaxResizeBoundaries & getComponentHWND properly, and how I am meant to input it in my code. As in wether I am meant to use the public static native int or I am meant to put it in the void numberConvertGUI()
The original answer on JFrame re-sizable height ONLY is
static {
if (System.getProperty("sun.arch.data.model").equals("32"))
{ // 32-bit JVM
System.loadLibrary("my32bitdll");
System.out.println("Running 32-bit JVM");
} else {
// 64-bit JVM
System.loadLibrary("my64bitdll");
System.out.println("Running 64-bit JVM");
}
}
// Sets a window to never be resized above or below these minimum widths/heights
public static native int setMinMaxResizeBoundaries(int hwnd, int minWidth, int minHeight, int maxWidth, int maxHeight);
Extra bit of code
// Returns the HWND for the specified component, or -1 if does not exist
public static native int getComponentHWND(Component c);
The short answer is you can't. In Swing all events go to your Java code, then update the on screen graphics... except for resizing windows. The window itself is a native control. Events go to the window first, then to the Java side. Your original code works on the Java side so the window has already resized by the time you size it back. That's what causes the glitchy behavior.
The only way around this (short of digging into C++ native code) is to disable native window decorations and render your own resize handles. Then your code would receive the resize events before the native window does and the glitches would go away. Not a trivial amount of work, but it might be feasible depending on your use case.

Sliding drawer animation using java swing

I am new to swing and designing an interface using java swing. I want a drawer to pull out with sliding animation on a button click. Firstly, is it possible to do so and if yes, how do I do it. Thank you. I will appreciate a response in terms of some specific method information.
There are a number of possible ways you might achieve depending on what you want to achieve.
The basic method would be simply draw the graphics and a Swing Timer
This would allow you to simply update a variable which would act as the bases for the size of the draw, for example...
import java.awt.BorderLayout;
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.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawerPaneTest {
public static void main(String[] args) {
new DrawerPaneTest();
}
public DrawerPaneTest() {
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 DrawerPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DrawerPane extends JPanel {
private boolean drawIn = false;
private Timer timer;
private int drawWidth = 0;
private int drawDelta = 4;
public DrawerPane() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
timer.stop();
drawIn = !drawIn;
drawDelta = drawIn ? 4 : -4;
timer.start();
}
});
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
drawWidth += drawDelta;
if (drawWidth >= getWidth() / 2) {
drawWidth = getWidth() / 2;
timer.stop();
} else if (drawWidth < 0) {
drawWidth = 0;
timer.stop();
}
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.drawRect(0, 0, drawWidth, getHeight());
g2d.dispose();
}
}
}
This is really basic and doesn't take into account things like slow out/slow in concepts. For those, you'd better look at a dedicated animation framework, like
Timing Framework
Trident
Universal Tween Engine
Which you might use depends on what it is you want to achieve, I personally like the Timing Framework, mostly because it gives me a lot of freedom, where as something like Trident has being (mostly) designed to provide the ability to change properties on a object, such as the size or position, for example. While the Timing Framework can do this do, it would require more work to get it that point.
I've not used the Universal Tween Engine, but have seen some real nice examples, such as the Sliding-Layout, which might actually meeet your needs...
First of all, I think that you shouldn't use Canvas, since, IMO, you shouldn't mix Swing's Components with AWT if it's not necessary (I'm talking in terms of the actual components you place in the GUI - I know Swing is built on top of AWT).
You might instead want to use a JPanel and perform custom painting using its paintComponent method. There are many tutorials about this (e.g. Lesson: Performing Custom Painting).
Once you figured out how to draw your drawer inside your JPanel (simply using Graphics2D drawing), you'll want to animate it: basically, you'll just need to update some of its properties, most likely some position...
Would you like to have some nice easing effects and everything being easily handled and packaged, I would recommend you to have a look to the Universal Tween Engine. I've already used it and could help you with that as well.
Once you have your Universal Tween Engine library linked to your project, simply use some call as following:
Tween
.to(drawerProperties, Type.POSITION, 10.0f) // animate the POSITION property of your drawerProperties on 10.0 units of time
.target(100) // set the number of frames of that animation to
.ease(Quad.OUT) // set a nice quadratic effect on the animation
.start(tweenManager); // launch the animation
Then, elsewhere, you just need to have a Thread running that constantly updates your TweenManager using TweenManager#update.
Once again, it looks like a lot of work but it actually is very easy to use and the Universal Tween Engine's getting started section is extremely good. Moreover, if you want to really add animation to your project, this could definitely be a great tool to use :)
One last point: you might need to update your properties in the Swing's Event Dispatch Thread, depending on how exactly will your solution be implemented. If you don't know so much about that, you could read Lesson: Concurrency in Swing - or just search StackOverflow if you've specific questions :)

Randomly display images on the screen(JFrame)

As you already know everything I started writing little game about space.
"Not a bad start" - https://stackoverflow.com/questions/19818655/simulation-of-spaceplanets-and-stars :D
I wrote a little plan of the work, and the first point in that it is' Random generation of stars.
You could say, easier use the random.
Random random = new Random();
int x = random.nextInt(getWidth()*2);
int y = random.nextInt(getHeight()*2);
g.drawImage(Image,x,y,4,4,this);
But it does not work (
And it is not working because the pictures "jump" on the screen.
As to the video: https://www.youtube.com/watch?v=EELo_-eh3fA
So how do you randomly bring the stars? (Star is a small picture or a white square)
That's all the code:
import java.awt.Graphics;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import java.io.IOException;
public class Game extends JComponent {
public Game() {
try {
image = ImageIO.read(getClass().getResource("star.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
public void paint(Graphics g) {
super.paint(g);
repaint();
Random random = new Random();
int x = random.nextInt(getWidth()*2);
int y = random.nextInt(getHeight()*2);
g.drawImage(Image,x,y,4,4,this);
}
public static void main(String[] args) {
JFrame frame = new JFrame(NAME);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.add(new Game());
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setResizable(true);
frame.addMouseListener(mos);
frame.addMouseMotionListener(mos);
}
}
Here's a code does not work (
Pictures jump again.
public void paintComponent(Graphics g) {
super.paintComponent(g);
repaint();
Random random = new Random();
int x = random.nextInt(getWidth()*2);
int y = random.nextInt(getHeight()*2);
g.drawImage(kor,x,y,10,10,this);
}
#camickr, You said to remove repaint(); but without it I do not get a picture
Custom painting is done by overriding the paintComponent() method not the paint() method.
Never invoke repaint() in a painting method. This will cause an infinite loop.
How to fix the picture? that they did not jump.
Basically the location needs to be determined outside of the painting method.
Maybe you can start with Custom Painting Approaches to get the idea of painting multiple objects on a panel. I would suggest the first approach of adding objects to a List. So you would add multiple objects to the list, but each object would be given a random location.
You said to remove repaint(); but without it I do not get a picture
Did you take the time to look at the link I gave you? The examples show you when to do a repaint().

Categories

Resources