0

Completing a graphics program in Java, I'm trying to animate rain falling using a timer. Right now I am testing my code with a big blue rectangle so I can see where it's going but the animation isn't working for me. I'm very new to Java graphics so I could be making mistakes that just aren't clear to me.

When I try to repaint the square to move, and the paint function is called the whole screen blinks, this may be because I was using recursive functions to draw fractal trees, but I'm not sure. Is there a way to keep everything I have drawn from being repainted and just call repaint on the rain? Any guidance or tips would be appreciated.

import java.awt.*;
import javax.swing.*;
import java.lang.Math;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class FractalTree extends JFrame {
    private int frameWidth = 1440;
    private int frameHeight = 850;
    private int rainX = 0;
    private int rainY = 0;

public FractalTree() 
{
    setBounds(1000, 1000, frameWidth, frameHeight ); //graphics window size
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    ActionListener listener = new TimerListener();
    final int DELAY = 1500;
    Timer t = new Timer(DELAY, listener);
    t.start();
    setResizable(false);

}

public void setRain(int newRainX, int newRainY)
{
    rainX = newRainX;
    rainY = newRainY;
}

public void setSkyGround(Graphics g)
{
   Color sky = new Color(180, 225, 255);
   g.setColor(sky);
   g.fillRect(0, 0, frameWidth, 550);

   Color sun = new Color(225, 225, 150);
   g.setColor(sun);

   g.fillOval(1380, -40, 100, 100);

   g.drawLine(frameWidth, 0, 1350, 550);
   g.drawLine(frameWidth, 0, 1450, 550);
   g.drawLine(1350, 550, 1450, 550);

   int xpoints[] = {frameWidth, 1450, 1350};
   int ypoints[] = {0, 550, 550};
   int npoints = 3;
   g.fillPolygon(xpoints, ypoints, npoints);

   g.drawLine(frameWidth, 0, 1080, 550);
   g.drawLine(frameWidth, 0, 880, 550);
   g.drawLine(880, 550, 1080, 550);

   int xpoints2[] = {frameWidth, 1080, 880};
   int ypoints2[] = {0, 550, 550};
   int npoints2 = 3;
   g.fillPolygon(xpoints2, ypoints2, npoints2);

   g.drawLine(frameWidth, 0, 480, 550);
   g.drawLine(frameWidth, 0, 280, 550);
   g.drawLine(480, 550, 280, 550);

   int xpoints3[] = {frameWidth, 480, 280};
   int ypoints3[] = {0, 550, 550};
   int npoints3 = 3;
   g.fillPolygon(xpoints3, ypoints3, npoints3);

   g.drawLine(frameWidth, 0, 0, 430);
   g.drawLine(frameWidth, 0, 0, 300);
   g.drawLine(0, 430, 0, 300);

   int xpoints4[] = {frameWidth, 0, 0};
   int ypoints4[] = {0, 430, 300};
   int npoints4 = 3;
   g.fillPolygon(xpoints4, ypoints4, npoints4);

   g.drawLine(frameWidth, 0, 0, 100);
   g.drawLine(frameWidth, 0, 0, 0);
   g.drawLine(0, 100, 0, 0);

   int xpoints5[] = {frameWidth, 0, 0};
   int ypoints5[] = {0, 0, 100};
   int npoints5 = 3;
   g.fillPolygon(xpoints5, ypoints5, npoints5);

   Color grassBackground = new Color(150, 255, 170);
   g.setColor(grassBackground);
   g.fillRect(0, 550, frameWidth, frameHeight);

}

public void drawTree(Graphics g, int x1, int y1, double angle, int depth, int red, int green, int blue) 
{
    if (depth == 0)
    { 
        Color doodle = new Color(red, green, blue);
        g.setColor(doodle);
        g.fillOval(x1, y1, 10, 10);  
    }
    else
    {
        Graphics2D g2 = (Graphics2D) g;
        g2.setStroke(new BasicStroke(depth));

        Color brown = new Color(100, 25, 0);
        g.setColor(brown);
        int x2 = x1 + (int) (Math.cos(Math.toRadians(angle)) * depth * 10);
        int y2 = y1 + (int) (Math.sin(Math.toRadians(angle)) * depth * 10);

        g.drawLine(x1, y1, x2, y2);

        drawTree(g, x2, y2, angle - 40, depth - 1, red, green, blue);
        drawTree(g, x2, y2, angle + 20, depth - 1, red, green, blue);
    }
}
public void realFlowers(Graphics g, int x, int y, int lenWid, int petals)
{
    //calculates the increment
    double inc = (2*Math.PI/petals);
    g.setColor(Color.YELLOW);
    //draws petals
    for(int i = 0; i < petals; i++){
        //keeps spacing consistent depandng on number of petals
        double value = i * inc;
        //draws petals with calculated spacing relative to number of petals
        g.fillOval((int)((lenWid)*Math.cos(value)+x-lenWid/4),(int)((lenWid)*Math.sin(value)+y-lenWid/4), lenWid + lenWid/2, lenWid + lenWid/2);
    }
    //draws middle flower bud;
    g.setColor(Color.ORANGE);
    g.fillOval(x - lenWid/4, y - lenWid/4, lenWid + lenWid/2 , lenWid + lenWid/2);
}

public void drawGrass(Graphics g, int width, int height, int interval, int red, int green, int blue)
{
    height = frameHeight - height;
    Color grass = new Color(red, green, blue);

    for(int i = 0; i < width; i= i + interval)
    {
        for(int j = frameHeight; j > height; j = j - interval)
        {
            g.setColor(grass);
            g.fillRect(i, j, 3, 5);
        }
    }
}

public void rainDrops(Graphics g, int x, int y, int w, int h)
{
    setRain(x, y);
    Color rain = new Color(0, 76, 153);
    g.setColor(rain);
    g.fillRect(x, y, w, h);
}

public void moveRainBy(int dx, int dy)
{
    rainX = rainX + dx;
    rainY = rainY + dy;
    repaint();
}

class TimerListener implements ActionListener
{
    public void actionPerformed(ActionEvent event)
    {
    moveRainBy(1, 1);
    }
}
public void paint(Graphics g) {

   setSkyGround(g);

   drawGrass(g, 1440, 315, 5, 0, 255, 0);
   drawGrass(g, 1430, 310, 10, 0, 204, 0);

   drawTree(g, 1085, 730, -90, 10, 255, 102, 102);
   drawTree(g, 250, 600, -90, 8, 255, 255, 255);
   drawTree(g, 1110, 740, -90, 4, 255, 102, 102);
   drawTree(g, 1060, 745, -90, 2, 255, 102, 102);

   realFlowers(g, 700,700, 8, 8);

   rainDrops(g, 200, 200, 30, 30);

}



public static void main(String[] args) {

    new FractalTree().setVisible(true);

}

}

C.StG
  • 69
  • 9

1 Answers1

1

When I try to repaint the square to move, and the paint function is called the whole screen blinks

This is because you've override paint of the a top level container (JFrame) which is not double buffered.

As a general recommendation, you should be basing your core functionality around a JPanel and override it's paintComponent method. Take a look at Performing Custom Painting and Painting in AWT and Swing for more details

You might like to also have a look at How to get the EXACT middle of a screen, even when re-sized and How can I set in the midst? for more details why it's not recommended to extend directly from JFrame and try to paint to it.

Is there a way to keep everything I have drawn from being repainted and just call repaint on the rain?

Painting is destructive, that is, each time paint/paintComponent is called, you are expected to repaint the entire state of the component from scratch.

You could use a buffering technique, using something like BufferedImage to paint your state to and simply have the paint methods draw the image, but that would depend on how complex a solution you want. If you were to use buffering technique, I would consider which elements are "static" and which elements are "dynamic". Painting those static elements to the buffer and then, when paint is called, painting the dynamic elements over the top the buffer

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366