I have a question.
I'm studying java.
In this code, I want to slower/faster the ball that created after I clicked "slower/faster" Menu button.
I mean, when I added ball and when the ball I added is moving, and then I click slow or fast button, and the ball that was moving has the same speed, and the ball that created after I clicked button, that ball's speed have to be changed.
I tried lock and unlock method in BallRunnable class, but it doesn't work.
Could you give me an advice? Thanks :)
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;
import java.util.List;
import java.util.concurrent.locks.*;
public class BallBounce {
public static void main(String[] args) {
JFrame frame = new BounceFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
A runnable that animates a bouncing ball.
*/
class BallRunnable implements Runnable {
public BallRunnable(Ball aBall, JPanel ballPanel) {
ball = aBall; this.ballPanel = ballPanel;
ballLock = new ReentrantLock();
}
public void run() {
ballLock.lock();
try {
for (int i = 1; i <= STEPS; i++) {
ball.move(ballPanel.getBounds()); // update the location of the ball
ballPanel.paint(ballPanel.getGraphics());
Thread.sleep((long)(DELAY * change));
}
} catch (InterruptedException e) { System.out.println("what"); }
finally { ballLock.unlock(); }
}
public static void print() { System.out.println(DELAY * change); }
public static void setdoublechange() {
change = change * 2;
}
public static void sethalfchange() {
change = change / 2;
}
private Ball ball;
private JPanel ballPanel;
public static final int STEPS = 1000;
public static final int DELAY = 3;
private static double change = 1;
private static Lock ballLock;
}
class BallRunnable2 implements Runnable {
public BallRunnable2(Ball aBall, JPanel ballPanel) {
ball = aBall; this.ballPanel = ballPanel;
}
public void run() {
ballLock.lock();
try {
Thread.sleep(100);
for (int i = 1; i <= STEPS; i++) {
ball.move(ballPanel.getBounds()); // update the location of the ball
ballPanel.paint(ballPanel.getGraphics());
Thread.sleep(DELAY * change);
}
} catch (InterruptedException e) { }
finally { ballLock.unlock(); }
}
public static void setdoublechange() {
change = change * 2;
}
public static void sethalfchange() {
change = change / 2;
}
private Ball ball;
private JPanel ballPanel;
public static final int STEPS = 1000;
public static final int DELAY = 3;
public static int change = 1;
private Lock ballLock;
}
/**
A ball that moves and bounces off the edges of a rectangle
*/
class Ball {
/**
Moves the ball to the next position, reversing direction if it hits one of the edges
*/
public void move(Rectangle2D bounds) { // java.awt.geom.Rectangle2D
x += dx; y += dy;
if (x < bounds.getMinX()) { x = bounds.getMinX(); dx = -dx; }
if (x + XSIZE >= bounds.getMaxX()) { x = bounds.getMaxX() - XSIZE; dx= -dx; }
if (y < bounds.getMinY()) { y = bounds.getMinY(); dy = -dy; }
if (y + YSIZE >= bounds.getMaxY()) { y = bounds.getMaxY() - YSIZE; dy = -dy; }
}
/**
Gets the shape of the ball at its current position.
*/
public Ellipse2D getShape() { return new Ellipse2D.Double(x, y, XSIZE, YSIZE); }
private static final int XSIZE = 15;
private static final int YSIZE = 15;
private double x = 0;
private double y = 0;
private double dx = 1;
private double dy = 1;
}
/**
The panel that draws the balls.
*/
class BallPanel extends JPanel {
/**
Add a ball to the panel.
#param b the ball to add
*/
public void add(Ball b) {
balls.add(b);
}
public void paintComponent (Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
for (Ball b : balls) { g2.fill(b.getShape()); }
}
private List<Ball> balls = new ArrayList<>();
}
class BounceFrame extends JFrame {
public BounceFrame() {
setTitle("BounceThread");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
ballPanel = new BallPanel(); add(ballPanel, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
addButton(buttonPanel, "Add 1", new ActionListener() {
public void actionPerformed(ActionEvent event) { addBall1(); }
});
addButton(buttonPanel, "Add 2", new ActionListener() {
public void actionPerformed(ActionEvent event) { addBall2(); }
});
addButton(buttonPanel, "Close", new ActionListener() {
public void actionPerformed(ActionEvent event) { System.exit(0); }
});
// addButton(buttonPanel, "Close", (ActionEvent event) -> System.exit(0));
add(buttonPanel, BorderLayout.SOUTH);
JMenu speedMenu = new JMenu("Speed");
JMenuItem fasterItem = speedMenu.add(new fasterAction("Faster"));
JMenuItem slowerItem = speedMenu.add(new slowerAction("Slower"));
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
menuBar.add(speedMenu);
}
private void addButton(Container container, String title, ActionListener listener) {
JButton button = new JButton(title);
container.add(button);
button.addActionListener(listener);
}
/**
Adds a bouncing ball to the canvas and starts a thread to make it bounce
*/
public void addBall1() {
Ball b = new Ball();
ballPanel.add(b);
Runnable r = new BallRunnable(b, ballPanel);
Thread t = new Thread(r);
t.start();
}
public void addBall2() {
Ball b1 = new Ball();
Ball b2 = new Ball();
ballPanel.add(b1);
ballPanel.add(b2);
Runnable r1 = new BallRunnable(b1, ballPanel);
Runnable r2 = new BallRunnable2(b2, ballPanel);
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
private BallPanel ballPanel;
public static final int DEFAULT_WIDTH = 450;
public static final int DEFAULT_HEIGHT = 350;
}
class fasterAction extends AbstractAction {
public fasterAction(String name) { super(name); }
public void actionPerformed(ActionEvent event) {
BallRunnable.sethalfchange();
BallRunnable2.sethalfchange();
}
}
class slowerAction extends AbstractAction {
public slowerAction(String name) { super(name); }
public void actionPerformed(ActionEvent event) {
BallRunnable.setdoublechange();
BallRunnable2.setdoublechange();
}
}
Increase Thread.sleep() value (it is in milliseconds)
Changing dx and/or dy of the ball (when a certain condition is met).
If I am correct <dx, dy> is the velocity of the ball.
Related
Im trying to code a simple 2D game. I have a Square that you can move with a/w/d. I got jumping working with a sort of gravity but now I cant get myself to land on a platform. I know how to detect collision between two things but even still idk what to do. My gravity always pulls me down until I reach groundLevel which is part of the problem. Here is my code so far. There's a lot of experimenting happing so its pretty messy.
import javax.swing.Timer;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.geom.*;
/**
* Custom Graphics Example: Using key/button to move a line left or right.
*/
public class PlatformingGame extends JFrame implements ActionListener, KeyListener{
// Name-constants for the various dimensions
public static final int CANVAS_WIDTH = 800;
public static final int CANVAS_HEIGHT = 400;
public static final Color LINE_COLOR = Color.BLACK;
public static final Color CANVAS_BACKGROUND = Color.WHITE;
public int GRAVITY = 10;
public int TERMINAL_VELOCITY = 300;
public int vertical_speed = 0;
public int jumpSpeed;
public int groundLevel = CANVAS_HEIGHT;
int Shapes;
private DrawCanvas canvas; // the custom drawing canvas (extends JPanel)
public enum STATE {
PLAYING,
PAUSED,
ONGROUND,
INAIR
};
JButton btnStartRestat, btnExit;
Timer timer;
Rectangle2D.Double guy, platform, platform2;
Shape[] shapeArr = new Shape[10];
int dx, dy;
/** Constructor to set up the GUI */
ArrayList myKeys = new ArrayList<Character>();
public static STATE gameState = STATE.PAUSED;
//public static STATE playerState = STATE.ONGROUND;
public PlatformingGame() {
dx = 3;
dy = 3;
guy = new Rectangle2D.Double( CANVAS_WIDTH/2 - 20, CANVAS_HEIGHT, 30, 20);
platform2 = new Rectangle2D.Double( CANVAS_WIDTH/4, CANVAS_HEIGHT/2+130, 50, 10);
platform = new Rectangle2D.Double( CANVAS_WIDTH/3, CANVAS_HEIGHT/2+50, 50, 10);
timer = new Timer(10, this);
// Set up a panel for the buttons
JPanel btnPanel = new JPanel();
// btnPanel.setPreferredSize(new Dimension(CANVAS_WIDTH/2, CANVAS_HEIGHT));
btnPanel.setLayout(new FlowLayout());
btnStartRestat = new JButton("Start/Restart");
btnExit = new JButton("Exit");
btnPanel.add(btnStartRestat);
btnPanel.add(btnExit);
btnStartRestat.addActionListener(this);
btnExit.addActionListener(this);
// Set up a custom drawing JPanel
canvas = new DrawCanvas();
canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
// Add both panels to this JFrame
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
cp.add(canvas, BorderLayout.CENTER);
cp.add(btnPanel, BorderLayout.SOUTH);
// "this" JFrame fires KeyEvent
addKeyListener(this);
requestFocus(); // set the focus to JFrame to receive KeyEvent
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Handle the CLOSE button
pack(); // pack all the components in the JFrame
setVisible(true); // show it
}
public void generateSpikes(){
Rectangle2D.Double spikes = null;
for (int i = 0; i < 10; i++) {
spikes = new Rectangle2D.Double (CANVAS_WIDTH - 300 + i*20 , CANVAS_HEIGHT - 30 , 20, 30);
shapeArr[i] = spikes;
}
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource()==btnStartRestat)
{
generateSpikes();
dx = 3;
dy = 3;
guy = new Rectangle2D.Double( 100, CANVAS_HEIGHT - guy.height, 15, 30);
platform = new Rectangle2D.Double( CANVAS_WIDTH/3, CANVAS_HEIGHT/2+50, 50, 10);
platform2 = new Rectangle2D.Double( CANVAS_WIDTH/4, CANVAS_HEIGHT/2+130, 50, 10);
gameState = STATE.PLAYING;
}
else if (e.getSource()==btnExit)
{
// requestFocus(); // change the focus to JFrame to receive KeyEvent
}
else if (e.getSource()== timer){
if (isHitDetected(platform2, guy)){
// playerState = STATE.ONGROUND;
guy.y = platform2.y;
}
if (myKeys.contains('a')
&& (guy.x > 0)){
guy.x = guy.x - 5; }
if (myKeys.contains('d')
&& (guy.x < CANVAS_WIDTH - guy.width)){
guy.x = guy.x + 5; }
{
updateGuyPosition();
}
requestFocus();
canvas.repaint();
}
}
public void updateGuyPosition(){
double guyHeight = guy.height;
if (guy.x >= CANVAS_WIDTH - guy.width){
}
if(gameState == STATE.PLAYING) {
guy.y += jumpSpeed;
if (guy.y < groundLevel - guyHeight) {
// if(playerState == STATE.INAIR) {
jumpSpeed += 1;
}
// }
else {
// if(playerState == STATE.INAIR) {
//playerState = STATE.ONGROUND;
jumpSpeed = 0;
guy.y = groundLevel - guyHeight;
}
// }
if (myKeys.contains('w') == true && guy.y == groundLevel - guyHeight) {
jumpSpeed = -15;
// playerState = STATE.INAIR;
}
}
}
public static boolean isHitDetected(Shape shapeA, Shape shapeB) {
Area areaA = new Area(shapeA);
areaA.intersect(new Area(shapeB));
return !areaA.isEmpty();
}
public void keyPressed(KeyEvent evt) {
if (!myKeys.contains(evt.getKeyChar())){
myKeys.add(evt.getKeyChar());
}
}
public void keyReleased(KeyEvent evt) {
myKeys.remove(myKeys.indexOf(evt.getKeyChar()));
}
public void keyTyped(KeyEvent evt) {
}
/** The entry main() method */
public static void main(String[] args) {
PlatformingGame myProg = new PlatformingGame(); // Let the constructor do the job
myProg.timer.start();
}
/**
* DrawCanvas (inner class) is a JPanel used for custom drawing
*/
class DrawCanvas extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(CANVAS_BACKGROUND);
g.setColor(LINE_COLOR);
Graphics2D g2d = (Graphics2D)g;
if(gameState == STATE.PLAYING) {
g2d.fill(guy);
g.setColor(Color.lightGray);
g2d.fill(platform);
g2d.fill(platform2);
for (int i = 0; i < 10; i++) {
g2d.fill(shapeArr[i]);
}
}
if(gameState == STATE.PAUSED) {
g.drawString("Game Paused", CANVAS_WIDTH/2, CANVAS_HEIGHT/2);
}
}
}
}
I have to drag multiple JLabels simultaneously after selecting them. I figured out how to drag a single JLabel and how to select multiple ones but I don't how move more than one at a time.
Code of JFrame class
import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
public class HallViewer extends JFrame {
private String nomeSala;
private JPanel draggableSeatsPanel;
private List<MyDraggableSeat> draggableSeats = new ArrayList<>();
public HallViewer(String nomeSala) {
this.nomeSala = nomeSala;
initDraggableSeatsList();
initDraggableSeatsPanel();
initFrame();
addMouseListener(mouseHandler);
addMouseMotionListener(mouseMotionHandler);
}
private void initDraggableSeatsList() {
MyDraggableSeat mds1 = new MyDraggableSeat(253, 210);
mds1.setText("A1");
MyDraggableSeat mds2 = new MyDraggableSeat(408, 341);
mds2.setText("A2");
MyDraggableSeat mds3 = new MyDraggableSeat(157, 213);
mds3.setText("A3");
draggableSeats = new ArrayList<>();
draggableSeats.add(mds1);
draggableSeats.add(mds2);
draggableSeats.add(mds3);
}
private void initDraggableSeatsPanel() {
draggableSeatsPanel = new JPanel();
draggableSeatsPanel.setMinimumSize(new Dimension(300, 150));
draggableSeatsPanel.setLayout(null);
for(MyDraggableSeat mds : draggableSeats) {
draggableSeatsPanel.add(mds);
initMouseListenerForMDS(mds);
repaint();
}
}
private void initFrame() {
setTitle("Piantina " + nomeSala);
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent windowEvent) {
setVisible(false);
}
});
setLayout(new GridLayout(1,1));
add(draggableSeatsPanel);
setSize(700,500);
setLocationRelativeTo(null);
setVisible(true);
}
private int screenX = 0;
private int screenY = 0;
private int myX = 0;
private int myY = 0;
private void initMouseListenerForMDS(MyDraggableSeat mds) {
mds.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
screenX = e.getXOnScreen();
screenY = e.getYOnScreen();
myX = mds.getX();
myY = mds.getY();
}
});
mds.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
int deltaX = e.getXOnScreen() - screenX;
int deltaY = e.getYOnScreen() - screenY;
mds.setLocation(myX + deltaX, myY + deltaY);
}
});
}
private int xbegin;
private int ybegin;
private int xend;
private int yend;
private int width;
private int height;
private int x;
private int y;
private boolean isNewRect = false;
private List<MyDraggableSeat> selectedMDSList = new ArrayList<>();
private MouseListener mouseHandler = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
for(MyDraggableSeat mds : selectedMDSList) {
mds.setBorder(new LineBorder(Color.BLUE, 3));
}
selectedMDSList.clear();
xbegin = e.getX();
ybegin = e.getY();
isNewRect = true;
}
#Override
public void mouseReleased(MouseEvent e) {
xend = e.getX();
yend = e.getY();
isNewRect = true;
repaint();
Rectangle rectangle = new Rectangle(x,y,width,height);
for(MyDraggableSeat mds : draggableSeats) {
if(rectangle.intersects(new Rectangle(mds.getX(), mds.getY(), mds.getWidth(), mds.getHeight()))) {
selectedMDSList.add(mds);
}
}
for(MyDraggableSeat mds : selectedMDSList) {
mds.setBorder(new LineBorder(Color.CYAN, 3));
}
}
};
private MouseMotionListener mouseMotionHandler = new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
xend = e.getX();
yend = e.getY();
isNewRect = false;
repaint();
}
};
#Override
public void paint(Graphics g) {
super.paint(g);
if((xbegin-xend)<0) {
width = Math.abs(xbegin-xend);
x = xbegin;
} else {
width = Math.abs(xend-xbegin);
x = xend;
}
if((ybegin-yend)<0) {
height = Math.abs(ybegin-yend);
y = ybegin;
} else {
height = Math.abs(yend-ybegin);
y = yend;
}
Graphics2D g2D = (Graphics2D) g;
AlphaComposite transparency = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
g2D.setComposite(transparency);
g2D.setColor(new Color(0x0073b5e9));
if(!isNewRect) {
g2D.fill(new Rectangle2D.Float(x,y,width,height));
}
}
public static class Main {
public static void main(String[] argv){ new HallViewer("Example"); }
}
}
Code of MyDraggableSeat JLabel
import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
public class MyDraggableSeat extends JLabel {
public MyDraggableSeat(int x, int y) {
setBorder(new LineBorder(Color.BLUE, 3));
setBackground(Color.WHITE);
setBounds(x, y, 30, 25);
setOpaque(true);
setHorizontalAlignment(JLabel.CENTER);
setVerticalAlignment(JLabel.CENTER);
}
}
The plan is to be able to select multiple JLabels like you select multiple files on your desktop (blue selection box) and to drag them around in the JFrame. I still want to be able to drag a single one if I choose to
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MovingCircleGUI
{
JFrame frame;
public int x,y;
public int vx = 30,vy=20;
public int width = 500,height = 500;
public int diameter=100;
CircleDrawPanel drawPanel;
Color color = Color.magenta.darker();
JButton button;
Timer timer2 = new Timer(10, new AnimateCircleListener());
boolean isRunning = false;
public static void main (String[] args)
{
MovingCircleGUI gui = new MovingCircleGUI();
gui.go();
}
//this method sets up the JFrame and adds the draw panel to the frame
public void go()
{
frame = new JFrame("MovingCircleGUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawPanel = new CircleDrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
button = new JButton("Click me to start the animation");
drawPanel.add(button);
frame.getContentPane().add(BorderLayout.SOUTH , button);
button.addActionListener(new AnimateCircleListener());
frame.setSize(width,height);
frame.setVisible(true);
}
class CircleDrawPanel extends JPanel
{
public void paintComponent (Graphics g)
{
super.paintComponent(g);
Graphics2D g2=(Graphics2D)g;
g2.setColor(color);
g2.fillOval(x,y,diameter,diameter);
}
}
public void MovingBall()
{
x = x + vx;
y = y + vy;
if( y >= height)
{
y=0;
boolean xIsSame = true;
int randomX = 0;
do
{
randomX = Math.round((float)Math.random()*width);
if (randomX != x)
{
x = randomX;
}
}
while(!xIsSame);
}
if(x <= 0)
{
x = width+x;
}
if (x >= width)
{
x = x-width;
}
timer2.start();
frame.repaint();
}
class AnimateCircleListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource()== button)
{
if(isRunning)
{
isRunning = false;
button.setText("Click me to start the animation");
button.revalidate();
}
else
{
isRunning = true;
MovingBall();
button.setText("Click me to stop the animation");
button.revalidate();
}
}
}
}
public int getX()
{
return x;
}
public void setX(int x)
{
this.x = x;
}
public int getY()
{
return y;
}
public void setY(int y)
{
this.y = y;
}
}
I am trying to construct a one button which handles two events mainly to start and stop the animation. What I am trying to do is when the user clicks the button the animation of the bouncing ball will start, and the text of the button will change to "Click me to stop", and when the user clicks the button again the animation will stop. I am using the timer.
The animation is okay, I worked that out, and that when the user clicks the button, the animation start that's okay too. The only problem I've got is how am I going to handle another event to the same button?
You could do something similar to this. This way you only need one Action Listener and a boolean to tell it which action to do.
boolean isRunning = false
button.addActionListener(new YourActionListener());
public class YourActionListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==button)
{
if(isRunning)
{
isRunning = false;
button.setText("Click me to start");
button.revalidate();
}
else
{
isRunning = true;
button.setText("Click me to stop");
button.revalidate();
}
}
}
}
Edit
Your code now works. What you wanted to do was call the MoveBall method when timer2 fired the Action Listener.
This can be done by
if(e.getSource()==timer2)
{
MovingBall();
}
so in your code it would look like.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MovingCircleGUI
{
JFrame frame;
public int x,y;
public int vx = 10,vy=5;
public int width = 500,height = 500;
public int diameter=50;
CircleDrawPanel drawPanel;
Color color = Color.magenta.darker();
JButton button;
Timer timer2 = new Timer(25, new AnimateCircleListener());
boolean isRunning = false;
public static void main (String[] args)
{
MovingCircleGUI gui = new MovingCircleGUI();
gui.go();
}
//this method sets up the JFrame and adds the draw panel to the frame
public void go()
{
frame = new JFrame("MovingCircleGUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawPanel = new CircleDrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
button = new JButton("Click me to start the animation");
drawPanel.add(button);
frame.getContentPane().add(BorderLayout.SOUTH , button);
button.addActionListener(new AnimateCircleListener());
frame.setSize(width,height);
frame.setVisible(true);
}
class CircleDrawPanel extends JPanel
{
public void paintComponent (Graphics g)
{
super.paintComponent(g);
Graphics2D g2=(Graphics2D)g;
g2.setColor(color);
g2.fillOval(x,y,diameter,diameter);
}
}
public void MovingBall()
{
x = x + vx;
y = y + vy;
if( y >= height)
{
y=0;
boolean xIsSame = true;
int randomX = 0;
do
{
randomX = Math.round((float)Math.random()*width);
if (randomX != x)
{
x = randomX;
}
}
while(!xIsSame);
}
if(x <= 0)
{
x = width+x;
}
if (x >= width)
{
x = x-width;
}
frame.repaint();
}
class AnimateCircleListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource()== button)
{
if(timer2.isRunning())
{
button.setText("Click me to start the animation");
button.revalidate();
timer2.stop();
}
else
{
button.setText("Click me to stop the animation");
button.revalidate();
timer2.start();
}
}
if(e.getSource()==timer2)
{
MovingBall();
}
}
}
public int getX()
{
return x;
}
public void setX(int x)
{
this.x = x;
}
public int getY()
{
return y;
}
public void setY(int y)
{
this.y = y;
}
}
You can remove the previous ActionListener on the JButton using button.removeActionListener(<your first action listener>) and then add the second action listener using button.addActionListener(<your second action listener>). Then, to change the text on the button, just use button.setText("Your Text Here").
The problem is that this ball after it is dragged and exited click, it is supposed to repaint according to the new y component given. This is calculated from the gravity final and added to the velocity which is added to the existing y component in a loop.
I have debugged many times and I just cant hit it on the head.
Its supposed to..
move to where you drag it >>> when you let go it is supposed to fall until it hits the ground.
Thank you ahead of time.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DragBallPanel extends JPanel implements MouseListener, MouseMotionListener
{
private static final int BALL_DIAMETER = 40;
private int screen_size_x = 300;
private int screen_size_y = 300;
private int ground_lvl = screen_size_y - 15;
private int _ballX = ground_lvl/2;
private int _ballY = ground_lvl - BALL_DIAMETER;
private final double GRAVITY = -9.8;
private double velocity;
private static final double TERM_VEL = -100;
private int _dragFromX = 0;
private int _dragFromY = 0;
private boolean _canDrag = false;
public DragBallPanel() throws InterruptedException
{
setPreferredSize(new Dimension(screen_size_x, screen_size_y));
setBackground(Color.darkGray);
setForeground(Color.darkGray);
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g); // Required for background.
g.setColor (Color.green);
g.fillRect (0, 280, 400, 50 );
g.setColor (Color.black);
g.fillOval(_ballX, _ballY, BALL_DIAMETER, BALL_DIAMETER);
}
public void mousePressed(MouseEvent e)
{
int x = e.getX();
int y = e.getY();
if (x >= _ballX && x <= (_ballX + BALL_DIAMETER)
&& y >= _ballY && y <= (_ballY + BALL_DIAMETER))\
{
_canDrag = true;
_dragFromX = x - _ballX;
_dragFromY = y - _ballY;
} else
{
_canDrag = false;
}
}
//===== mouseDragged ======
/** Set x,y to mouse position and repaint. */
public void mouseDragged(MouseEvent e)
{
if (_canDrag)
{ // True only if button was pressed inside ball.
//--- Ball pos from mouse and original click displacement
_ballX = e.getX() - _dragFromX;
_ballY = e.getY() - _dragFromY;
//--- Don't move the ball off the screen sides
_ballX = Math.max(_ballX, 0);
_ballX = Math.min(_ballX, getWidth() - BALL_DIAMETER);
//--- Don't move the ball off top or bottom
_ballY = Math.max(_ballY, 0);
_ballY = Math.min(_ballY, getHeight() - BALL_DIAMETER);
this.repaint();
}
}
public void mouseExited(MouseEvent e)
{
while(_ballY < ground_lvl)
{
simulateGravity();
}
}
public void simulateGravity()
{
if(_canDrag)
{
try{
velocity = velocity + GRAVITY;
if (velocity < TERM_VEL)
{
velocity = TERM_VEL;
}
if (_ballY >= ground_lvl - BALL_DIAMETER)
{
velocity = velocity/4;
}
_ballY += velocity;
Thread.sleep(400);
this.repaint();//**problem occurs here**
}catch(InterruptedException ie)
{
}
}
}
public void mouseMoved (MouseEvent e){}
public void mouseEntered (MouseEvent e){}
public void mouseClicked (MouseEvent e){}
public void mouseReleased(MouseEvent e){}
}
main() class
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class DragDemo extends JApplet
{
public static void main(String[] args) throws InterruptedException
{
JFrame window = new JFrame();
window.setTitle("Drag Demo");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//window.add(new DragBallPanel());
window.setContentPane(new DragBallPanel());
window.setResizable(false);
window.pack();
window.show();
}
public DragDemo() throws InterruptedException
{
new DragBallPanel();
}
}
This SSCCE begins to show the problems in the code.
Compile the code.
Run it.
Drag the ball upwards.
Release the ball.
Remove the mouse from the drawing area, to see..
The ball fall upwards!
You seem to have gotten the Y values upside down. They start at top of screen, and go downwards. Also, the code was blocking the EDT in an infinite loop. To solve that, run the animation using a Swing Timer.
Please read the document on the SSCCE & ask if there is anything in it you do not understand. I am well placed to explain. :)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DragBallPanel extends JPanel implements MouseListener, MouseMotionListener
{
private static final int BALL_DIAMETER = 40; // Diameter of ball
private int screen_size_x = 300;
private int screen_size_y = 300;
private int ground_lvl = screen_size_y - 15;
private int _ballX = ground_lvl/2;
private int _ballY = ground_lvl - BALL_DIAMETER;
private final double GRAVITY = -9.8;
private double velocity;
private static final double TERM_VEL = 100;
private int _dragFromX = 0; // pressed this far inside ball's
private int _dragFromY = 0; // bounding box.
/** true means mouse was pressed in ball and still in panel.*/
private boolean _canDrag = false;
public DragBallPanel()
{
setPreferredSize(new Dimension(screen_size_x, screen_size_y));
setBackground(Color.darkGray);
setForeground(Color.darkGray);
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g); // Required for background.
g.setColor (Color.green);
g.fillRect (0, 280, 400, 50 );
g.setColor (Color.black);
g.fillOval(_ballX, _ballY, BALL_DIAMETER, BALL_DIAMETER);
}
public void mousePressed(MouseEvent e)
{
int x = e.getX(); // Save the x coord of the click
int y = e.getY(); // Save the y coord of the click
if (x >= _ballX && x <= (_ballX + BALL_DIAMETER)
&& y >= _ballY && y <= (_ballY + BALL_DIAMETER)) {
_canDrag = true;
_dragFromX = x - _ballX; // how far from left
_dragFromY = y - _ballY; // how far from top
} else {
_canDrag = false;
}
}
//========= mouseDragged =================
/** Set x,y to mouse position and repaint. */
public void mouseDragged(MouseEvent e)
{
if (_canDrag) { // True only if button was pressed inside ball.
//--- Ball pos from mouse and original click displacement
_ballX = e.getX() - _dragFromX;
_ballY = e.getY() - _dragFromY;
//--- Don't move the ball off the screen sides
_ballX = Math.max(_ballX, 0);
_ballX = Math.min(_ballX, getWidth() - BALL_DIAMETER);
//--- Don't move the ball off top or bottom
_ballY = Math.max(_ballY, 0);
_ballY = Math.min(_ballY, getHeight() - BALL_DIAMETER);
this.repaint(); // Repaint because position changed.
}
}
//====================================================== method mouseExited
/** Turn off dragging if mouse exits panel. */
public void mouseExited(MouseEvent e)
{
System.out.println("Exited: " + e);
//_canDrag = false;
runGravity();
/* while(_ballY < ground_lvl)
{
simulateGravity();
}*/
}
Timer timer;
ActionListener animate;
public void runGravity() {
if (animate==null) {
animate = new ActionListener() {
public void actionPerformed(ActionEvent ae) {
System.out.println("Ground: " + (_ballY-ground_lvl));
if (_ballY > ground_lvl) {
timer.stop();
} else {
simulateGravity();
}
}
};
timer = new Timer(100,animate);
}
timer.start();
}
public void simulateGravity()
{
System.out.println("_canDrag: " + _canDrag);
if(_canDrag)
{
velocity = velocity + GRAVITY;
if (velocity > TERM_VEL)
{
velocity = TERM_VEL;
}
if (_ballY >= ground_lvl - BALL_DIAMETER)
{
//We have hit the "ground", so bounce back up. Reverse
//the speed and divide by 4 to make it slower on bouncing.
//Just change 4 to 2 or something to make it faster.
velocity = velocity/4;
}
_ballY += velocity;
//this.revalidate();
this.repaint();
}
}
public void mouseMoved (MouseEvent e){}
public void mouseEntered (MouseEvent e){}
public void mouseClicked (MouseEvent e){}
public void mouseReleased(MouseEvent e){}
public static void main(String[] args) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
DragBallPanel dbp = new DragBallPanel();
JOptionPane.showMessageDialog(null, dbp);
}
});
}
}
Try updateUI() instead of repaint().
If there is no effect remove the component and add it again.
UPDATE: semicomplex animation + swing timer = trainwreck. The ultimate source of the problems was the java timer, either the swing or utility version. They are unreliable, especially when performance is compared across operating systems. By implementing a run-of-the-mill thread, the program runs very smoothly on all systems. http://zetcode.com/tutorials/javagamestutorial/animation/. Also, adding Toolkit.getDefaultToolkit().sync() into the paintComponent() method noticeably helps.
I wrote some code that animated smoothly in an awt.Applet (but flickered), then I refactored it to java swing. Now it doesn't flicker but it looks choppy. I've messed with the timer but that doesn't work. Any tips or suggestions for smoothly animating swing components would be greatly appreciated.
import java.util.Random;
import java.util.ArrayList;
import java.awt.event.;
import java.awt.;
import javax.swing.*;
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
public class Ball extends JApplet{
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setTitle("And so the ball rolls");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initContainer(frame);
frame.pack();
frame.setVisible(true);
}
});
}
public static void initContainer(Container container){
GraphicsPanel graphicsPanel = new GraphicsPanel();
MainPanel mainPanel = new MainPanel(graphicsPanel);
container.add(mainPanel);
graphicsPanel.startTimer();
}
#Override
public void init(){
initContainer(this);
}
}
///////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
class MainPanel extends JPanel {
JLabel label = new JLabel("Particles");
GraphicsPanel gPanel;
public MainPanel(GraphicsPanel gPanel){
this.gPanel = gPanel;
add(gPanel);
add(label);
}
}
///////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
class GraphicsPanel extends JPanel implements MouseListener {
private ArrayList<Particle> ballArr = new ArrayList<Particle>();
private String state="s"; //"s"=spiral, "p"=particle
private int speed=10; //~20 Hz
private Timer timer;
public GraphicsPanel(){
System.out.println("echo from gpanel");
setPreferredSize(new Dimension(500,500));
timer = new Timer(speed, new TimerListener());
addMouseListener(this);
}
public void startTimer(){
timer.start();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
for (Particle b: ballArr){
g.setColor(b.getColor());
g.fillOval(b.getXCoor(),b.getYCoor(),
b.getTheSize(),b.getTheSize());
}
}
public void mousePressed(MouseEvent e) {
ballArr.add(new Particle(e.getX(), e.getY(), state));
}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mouseClicked(MouseEvent e) {}
class TimerListener implements ActionListener {
public void actionPerformed(ActionEvent e){
for (Particle b: ballArr)
b.move();
setBackground(Color.WHITE);
repaint();
}
}
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
class Particle
{
private static int instanceCount; {{instanceCount++;}}
private int z = 11, t=1, u=1;
private int[] RGB = new int[3];
private int[] randomizeColor = new int[3];
private double radius, theta;
private int x, y, centerX, centerY, size, spiralDirection=1,
ballSizeLowerBound, ballSizeUpperBound,
radiusLowerBound, radiusUpperBound,
mouseInputX, mouseInputY,
radiusXMultiplier, radiusYMultiplier;
private Color color;
private String state;
private Random random = new Random();
///////////////////////////////////////////////////////////////////////////
public Particle(int x, int y, int centerX, int centerY, int radius,
int theta, int size, Color color){
this.x=x;this.y=y;this.centerX=centerX;this.centerY=centerY;
this.radius=radius;this.theta=theta;this.size=size;this.color=color;
}
public Particle(int mouseInputX, int mouseInputY, String state){
this.mouseInputX=mouseInputX;
this.mouseInputY=mouseInputY;
this.state=state;
//randomize color
RGB[0] = random.nextInt(252);
RGB[1] = random.nextInt(252);
RGB[2] = random.nextInt(252);
randomizeColor[0] = 1+random.nextInt(3);
randomizeColor[0] = 1+random.nextInt(3);
randomizeColor[0] = 1+random.nextInt(3);
centerX=mouseInputX;
centerY=mouseInputY;
if (state.equals("s")){ //setup spiral state
ballSizeLowerBound=5;
ballSizeUpperBound=18;
radiusLowerBound=0;
radiusUpperBound=50;
radiusXMultiplier=1;
radiusYMultiplier=1;
}
if (state.equals("p")){ //setup particle state
ballSizeLowerBound = 15;
ballSizeUpperBound =20 + random.nextInt(15);
radiusLowerBound = 5;
radiusUpperBound = 15+ random.nextInt(34);
radiusXMultiplier=1 + random.nextInt(3);
radiusYMultiplier=1 + random.nextInt(3);
}
size = ballSizeUpperBound-1; //ball size
radius = radiusUpperBound-1;
if (instanceCount %2 == 0) // alternate spiral direction
spiralDirection=-spiralDirection;
}
///////////////////////////////////////////////////////////////////////////
public int getXCoor(){return centerX+x*spiralDirection;}
public int getYCoor(){return centerY+y;}
public int getTheSize(){return size;}
public Color getColor(){return color;}
//////////////////////////////////////////////////////////////////////////
void move(){
//spiral: dr/dt changes at bounds
if (radius > radiusUpperBound || radius < radiusLowerBound)
u = -u;
//spiral shape formula: parametric equation for the
//polar equation radius = theta
x = (int) (radius * radiusXMultiplier * Math.cos(theta));
y = (int) (radius * radiusYMultiplier * Math.sin(theta));
radius += .1*u;
theta += .1;
//ball size formula
if (size == ballSizeUpperBound || size == ballSizeLowerBound)
t = -t;
size += t;
//ball colors change
for (int i = 0; i < RGB.length; i++)
if (RGB[i] >= 250 || RGB[i] <= 4)
randomizeColor[i] = -randomizeColor[i];
RGB[0]+= randomizeColor[0];
RGB[1]+= randomizeColor[1];
RGB[2]+= randomizeColor[2];
color = new Color(RGB[0],RGB[1],RGB[2]);
}
}
Don't set a constant interval timer. Set the timer to go off once -- in the handler
Get the current time (save in frameStartTime)
Do your frame
Set the timer to go off in: interval - (newCurrentTime - frameStartTime)
Should be smoother. If you want to go really pro (and stay in Java), I think you have to consider JavaFX.