Java Swing or AWT repaint frequency - java

I tried to make a simple game in Java and ended up with this code
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
ball.paint(g2d);
}
...
while (true) {
repaint();
Thread.sleep(10);
}
It redraws not frequently enough. But if I move my mouse on top of the window it starts to repaint much more frequently. Pressing buttons on keyboard speeds up too.
I'm using Arch with i3wm.

Don't trust guides on the internet. Think yourself sometimes.
It was the guide with mistake. The problem is that the algorithm is wrong. We just have to draw more frequently than update our world.
Here is a stupid implementation of this. It should may be really wrong in terms of concurrency.
Timer timer1 = new Timer(1, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
game.repaint();
}
});
timer1.start();
Timer timer2 = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
game.move();
}
});
timer2.start();

I had exactly the same problem. Two timers didn't work for me. I managed to move an object smoothly by adding rendering hints and drawing an empty rectangle before my main object:
public void paintComponent(Graphics g) {
g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.WHITE)
g2d.fillRect(0, 0, getWidth, getHeight)
// paint your object here
}

Related

Java: paint doesn't call with repaint

Let me be honest, I'm not really know what I'm doing.
I just moved from python to Java, and i'm still trying to get used to all the classes and the types things.
I decide to make a break with java concepts tutorials and start to get my hands dirty. According to my understanding, I'm using swing to paint a ball on the screen and make it move.
I tried to design a ball object that handle the ball position and the screen bumping, but the ball doesn't moved at all. When I turn on the debug I noticed that the paint() function get called only at creation, but not get called with repaint().
I got a feeling that I'm using a bad tutorial to do this stuff, its look like there is a better way to do it.
Anyway, I will be glad to hear what you guys thinking.
Edit: After I saw your comments I notice that paint actually get called when I put sysout there. Its seems that the debugger doesn't jump to there before I put sysout in paint(). My guess is that I'm not really changing the position of the ball.
#SuppressWarnings("serial")
public class Tennis extends JPanel {
Ball ball = new Ball(50,50);
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int[] position = ball.getPosition();
g2d.fillOval(position[0],position[1], 30, 30);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Mini Tennis");
Tennis game = new Tennis();
frame.add(game);
frame.setSize(300, 300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
while (true) {
// just change the position and check for bump
game.ball.move(game.getHeight(), game.getWidth());
game.repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Change paint() to paintComponent(), for an explanation of the differences therein see this.
#Override
public void paintComponent(Graphics g){ //CHANGE HERE
super.paintComponent(g); //AND HERE
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int[] position = ball.getPosition();
g2d.fillOval(position[0],position[1], 30, 30);
}

Using Draw again vs Repaint

I'm trying to figure out if the repaint method does something that we can't do ourselves.
I mean,how are these two versions different?
public class Component extends JComponent {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Rectangle r = new Rectangle(0,0,20,10);
g2.draw(r);
r.translate(5,5);
g2.draw(r);
}
}
and
public class Component extends JComponent {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Rectangle r = new Rectangle(0,0,20,10);
g2.draw(r);
r.translate(5,5);
repaint();
}
}
The 2nd version can result in a very risky and poor animation since it can result in repaints being called repeatedly, and is something that should never be done. If you need simple animation in a Swing GUI, use a Swing Timer to drive the animation.
i.e.,
public class MyComponent extends JComponent {
private Rectangle r = new Rectangle(0,0,20,10);
public MyComponent() {
int timerDelay = 100;
new Timer(timerDelay, new ActionListener(){
public void actionPerformed(ActionEvent e) {
r.translate(5, 5);
repaint();
}
}).start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.draw(r);
}
}
The use of repaint() is to suggest to the JVM that the component needs to be painted, but it should never be called in a semi-recursive fashion within the paint or paintComponent method. An example of its use can be seen above. Note that you don't want to call the painting methods -- paint or paintComponent directly yourselves except under very unusual circumstances.
Also avoid calling a class Componenet since that name clashes with a key core Java class.

how can i clear my rectangle after drawing it to the screen?

I realize that a major component missing from my game is a sense of time, ticks, or FPS. I am planning on implementing this soon, but wanted to get some feedback on how to set up my "bullets" instead. I want the user to press the space bar and have a bullet fired across the screen. Now I have gotten close without a sense of FPS; however, it just draws as one giant line, meaning the bullet "trail" never clears. I am wondering why, when I call repaint, the rectangle can move around the screen via my keyboard, but whenever I have something set automatically to move then it just leaves a "trail", even though I am calling repaint(); in each method.
Also, how could i create my bullet from another class?
public class drawingComponent extends JComponent implements KeyListener {
public Rectangle hello = new Rectangle(300, 100, 50, 50);
Rectangle bullet = new Rectangle(310,75, 10,10);
boolean goingon = false;
public drawingComponent(){
addKeyListener(this);
}
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.setColor(new Color(255,25,0));
g2.setFont(new Font("monospace", Font.BOLD+Font.ITALIC, 30));
g2.drawString("nothing yet",300,320);
g2.fill(hello);
setFocusable(true);
requestFocus();
g2.setColor(new Color(0,25,0));
if (goingon == true){
while (bullet.y < 1000){
bullet.y=bullet.y+10;
g2.fill(bullet);
}
bullet.y=300;
}
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_W){
hello.y=hello.y-1;
hello.setLocation(hello.x,hello.y);
repaint();
System.out.println(hello.y);
}
if(e.getKeyCode() == KeyEvent.VK_S){
hello.y=hello.y+1;
hello.setLocation(hello.x,hello.y);
repaint();
}
if(e.getKeyCode() == KeyEvent.VK_A){
hello.x=hello.x-1;
hello.setLocation(hello.x,hello.y);
repaint();
}
if(e.getKeyCode() == KeyEvent.VK_D){
hello.x=hello.x+1;
hello.setLocation(hello.x,hello.y);
repaint();
}
if(e.getKeyCode() == KeyEvent.VK_SPACE){
goingon = true;
repaint();
}
}
#Override
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_SPACE){
goingon = false;
repaint();
}
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}
Start by repairing the paint chain...
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(new Color(255,25,0));
g2.setFont(new Font("monospace", Font.BOLD+Font.ITALIC, 30));
g2.drawString("nothing yet",300,320);
g2.fill(hello);
Next, stop changing the state of the component from within the paint method...
//setFocusable(true);
//requestFocus();
g2.setColor(new Color(0,25,0));
if (goingon == true){
while (bullet.y < 1000){
bullet.y=bullet.y+10;
g2.fill(bullet);
}
bullet.y=300;
}
Painting should simply paint the current state of the component, it should never attempt to modify the state of the component, doing so could trigger another paint request which will put your code into an infinite loop of painting and consume your CPU cycles.
Swing uses a passive rendering algorithm, this means, painting is carried out only when the repaint manager thinks it needs to be done, meaning that painting can be done at random and mostly without your intervention...
Based on the fact that you are trying to force focus to the component, I assume you are trying to overcome issues related to KeyListener. Instead, you should use the key bindings API. Take a look at How to Use Key Bindings for more details
Take a look at Painting in AWT and Swing and Performing Custom Painting for more details.
Don't forget to call super.paintComponent() in overridden paintComponent() method that clears the previews view.
Read more...

Does invoking the drawRect method on a Graphics2D trigger the paintComponent method?

I'm trying to figure out the behavior of my program, and that is my best theory as to why its doing what its doing. I was hoping this would use the rand variable to decide which shape to paint, but instead it seems the paintComponent method is being invoked many times in-between timer firings, causing many shapes to be painted and I'm trying to understand why.
This is the code:
public class TestPane extends JPanel {
private int yPos0;
private int yPos1;
private int boundary0=750;
private ActionEvent ae = null;
private Graphics g0 = null;
private int count=1;
public TestPane(Color foreground){
setForeground(foreground);
this.setBackground(Color.BLUE);
Timer timer = new Timer(3000,new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
ae = e;
yPos0 =yPos0+50;
repaint();
}
});
timer.start();
}
#Override
public void paintComponent(Graphics g){
g0 = g;
super.paintComponent(g);
createShape(yPos0);
repaint();
}
public void createShape(int ypos0){
//generate random number between 1 and 3 and assign to rand
int rand = (int)((Math.random()*3)+1);
System.out.println(rand);
if(rand==1){
Graphics2D g2d = (Graphics2D) g0.create();
g2d.setColor(Color.RED);
g2d.drawRect(0, ypos0, 200, 50);
}
if(rand==2){
Graphics2D g2d = (Graphics2D) g0.create();
g2d.setColor(Color.GREEN);
g2d.drawRect(0, ypos0, 150, 50);
g2d.drawRect(50, ypos0+50,50,50);
}
}
}
The reason that paintComponent is being called so many times is that you are calling repaint within that method which causes itself to be called ad infinitum. This is not needed as you're already calling repaint from your Timer.

Why my graphics code don't run unless there is a System.out.println in the code block?

I have this method paint() which receive a Graphics2D parameter. The weird thing that happen is that unless there is a System.out.println present(which i comment out in the block below), the canvas will not draw anything.
public class Map{
public void paint(Graphics2D g){
//fill background to black
g.setColor(Color.black);
g.fillRect(0, 0, TILE_SIZE*WIDTH, TILE_SIZE*HEIGHT);
//draw the tiles and buildings
for(int i=0;i<WIDTH;i++){
for(int j=0;j<HEIGHT;j++){
if(map[j][i] == CLEAR){
//System.out.println("");
g.setColor(Color.gray);
g.fillRect(i*TILE_SIZE, j*TILE_SIZE, TILE_SIZE, TILE_SIZE);
g.setColor(Color.red);
g.drawRect(i*TILE_SIZE, j*TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
}
}
}
}
Here I use BufferStrategy to draw on Canvas and add it to a Frame. This method is in class Map which will be passed a Graphics2D from the getDrawGraphics() method from BufferStrategy(I hope many people are familiar with this stuff to understand what I'm doing).
public class MapTest extends Canvas{
private Map map;
public MapTest(){
Frame frame = new Frame("MAP");
frame.add(this);
frame.setVisible(true);
createBufferStrategy(2);
strategy = getBufferStrategy();
//draw the map
Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
//g.translate(100, 100);
map.paint(g);
g.dispose();
strategy.show();
}
}
This code is from the Canvas class. As you can see the paint() method is separate from the Canvas class(which I name GameTest). So if I comment out the println statement then no graphics is shown in the canvas, otherwise it is displayed correctly. Anyone can help me???
You should use the SwingUtilities to switch to the Event Dispatch Thread(EDT), see below. This is required for almost all interactions with AWT and Swing classes.
SwingUtilities.invokeLater(new Runnable(){
public void run(){
new MapTest();
}
}
Notice that this uses a swing helper library, that should be fine for AWT, but even better is to start using Swing.

Categories

Resources