Hi, I'm trying to build simple bounching ball (eventually), so I thought I'd start with unrealistic bouncing so balls goes from top to bottom of returns back to the top in a loop. So currently the ball moves vertically in increments of 10 (the ball height), and the animation works but the question I have is I wanted a JFrame height of 180, but I noticed that the blue border (or whatever color border) in the Swing JFrame window that opens is actually part of the JFrame's height so I had to have my if statement indicate if ball_y (y distance travelled so far) to not go past windows' height (JFrame's height) to 140px which I played around to get it to otherwise the ball goes past the Swing window. So shouldn't the height be not including the border of the JFrame? I also tried moving just horizontally and I set JFrame height and width to be same: 180px but I noticed for looping the animation, it's 160px before it reaches right side of JFrame window, why the discrepancy?
import java.awt.Color; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.*;//other stuff like layout managers import javax.swing.Timer; import javax.swing.*; public class Ball_Animation_v0 extends JFrame { private static final long serialVersionUID = 1L;//Q: what is this?//look up (I just let IDE add a default for me) private int ball_x = 0;//cur x component of where ball is private int ball_y = 0;//cur y component of where ball is private int ball_width = 10;//(move in increments of 10px) (b/c 10 is a factor of JFrame ht: 180) private int ball_height = 10;//(move in increments of 10px) private int window_width = 180;//width of JFrame (for version 2, I can't set this b/c I want the JFrame ht to be resizable) private int window_height = 180;//height of JFrame (for ver 2, I can't set this b/c I want JFrame width to be resizable) private int delay = 120; private Canvas aCanvas; private boolean is_reached_bottom = false; public Ball_Animation_v0() { this.setSize(window_width, window_height); this.setVisible(true); this.setResizable(false); aCanvas = new Canvas(); this.add(aCanvas); //the Timer is listener to determine when to update position of ball Timer t = new Timer(delay, new ActionListener(){ public void actionPerformed(ActionEvent e) { change_position(); repaint(); } }); t.start(); } //make sure to go back up when ball encounters the JFrame edge private void change_position()//update position of ball { //System.out.println("cur ball_y: " + ball_y); if ( ball_y == 0 ) is_reached_bottom = false;//reset if ( ball_y < 140 && !is_reached_bottom )//the blue border also part of JFrame...so I can't use 180 ball_y = ball_y + ball_height; else//keep moving back towards ceiling until you're at ceiling of JFrame { is_reached_bottom = true; ball_y = ball_y - ball_height; } } private class Canvas extends JPanel//lightweight component (=> it doesn't have its own window by accessing native OS's window creation) { public void paintComponent(Graphics g) { super.paintComponent(g);//need this b/c of something to do w/ UI delegates...need to look up to refresh Graphics2D g2d = (Graphics2D)g; g2d.setColor(Color.red); g2d.fillOval(ball_x, ball_y, ball_width, ball_height); } } public static void main(String[] args) { new Ball_Animation_v0(); } }
Also, I read it's important to not have to repaint the entire graphics but just update where the animation needs updated, so I tried using parameter repaint method, but then when I run program, the red ball stays stuck at starting position, any help appreciated.
here is what I mean (this is constructor from above program)
public Ball_Animation_v0() { this.setSize(window_width, window_height); this.setVisible(true); this.setResizable(false); aCanvas = new Canvas(); this.add(aCanvas); //the Timer is listener to determine when to update position of ball Timer t = new Timer(delay, new ActionListener(){ public void actionPerformed(ActionEvent e) { change_position(); repaint(ball_x, ball_y, ball_width, ball_height);//Problem here: why can't I use parameter repaint to // just update the actually ball and not repaint entire items in canvas? (because red ball just shows // up at starting position } }); t.start(); } private void change_position()//update position of ball { //System.out.println("cur ball_y: " + ball_y); if ( ball_y == 0 ) is_reached_bottom = false;//reset if ( ball_y < 140 && !is_reached_bottom )//the blue border also part of JFrame...so I can't use 180 ball_y = ball_y + ball_height; else//keep moving back towards ceiling until you're at ceiling of JFrame { is_reached_bottom = true; ball_y = ball_y - ball_height; } }
--- Update ---
Ok, I decided to draw inside the Canvas canvas and the now the red ball animation seems *almost* fine, but it's just when it reaches bottom, it still goes slightly off screen (out of view from JFrame window), any help appreciated.
Updated program
import javax.swing.*;//Timer here import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.*; import javax.swing.SwingUtilities; public class Ball_Animation_v0_1 extends JFrame { private static final long serialVersionUID = 1L;//Q: what is this?//look up (I just let IDE add a default for me) private int ball_x = 0;//cur x component of where ball is private int ball_y = 0;//cur y component of where ball is private int ball_width = 10;//(move in increments of 10px) (b/c 10 is a factor of JFrame ht: 180) private int ball_height = 10;//(move in increments of 10px) private int canvas_width = 180; private int canvas_height = 180; private int delay = 120; // private Canvas aCanvas; private boolean is_reached_bottom = false; private boolean is_reached_right_side = false; public Ball_Animation_v0_1() { this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // this.setSize(window_width, window_height); this.setVisible(true); this.setResizable(false); this.add(new Canvas() ); this.pack(); //the Timer is listener to determine when to update position of ball Timer t = new Timer(delay, new ActionListener(){ public void actionPerformed(ActionEvent e) { change_position(); repaint(/**ball_x, ball_y, ball_width, ball_height*/);//how do I only redraw necessary updates and not entire graphics for the red ball? } }); t.start(); } //Drawing inside Canvas BUT when it reaches bottom, red ball still slight goes off screen... public void change_position() { if ( ball_x == 0 && ball_y == 0 ) { is_reached_bottom = false;//reset is_reached_right_side = false;//reset } if ( !is_reached_bottom && !is_reached_right_side && ball_y < canvas_height && ball_x < canvas_width )//move forward { ball_x = ball_x + ball_width; ball_y = ball_y + ball_height; } else//move backwards { is_reached_bottom = true; is_reached_right_side = true; ball_x = ball_x - ball_width; ball_y = ball_y - ball_height; } } //REMEMBER: a lightwt container can also have its own layout manager private class Canvas extends JPanel//eg of named inner class { public Canvas() { setBorder(BorderFactory.createLineBorder(Color.black)); } public Dimension getPreferredSize() {//overridden method return new Dimension(180,180); } public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; g.setColor(Color.RED); g.fillOval(ball_x, ball_y, ball_width, ball_height); } } //Q: what does invokeLater mean? (look up) public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() {//anonymous inner class (used to ensure Swing/GUI/Event-handling run in EDT // as this will prevent potential race conditions that could lead to deadlock. public void run() { new Ball_Animation_v0_1(); } }); } }