Bouncing Ball applet - java

I made the applet Bouncing Ball and in the class Ball.java I made inner class TimerListener with method repaint(), and when I run the applet, instead of repaint the ball, java paint the ball again and again(not delete, and then paint).
here is my code for the class Ball.java
import javax.swing.Timer;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Ball extends JPanel {
private int delay = 10;
Timer timer=new Timer(delay, new TimerListener());
private int x=0;
private int y=0;
private int dx=20;
private int dy=20;
private int radius=5;
private class TimerListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
repaint();
}
}
public void paintComponent(Graphics g){
g.setColor(Color.red);
if(x<radius) dx=Math.abs(dx);
if(x>(getWidth()-radius)) dx=-Math.abs(dx);
if(y>(getHeight()-radius)) dy=-Math.abs(dy);
if(y<radius) dy=Math.abs(dy);
x+=dx;
y+=dy;
g.fillOval(x-radius, y-radius, radius*2, radius*2);
}
public void suspend(){
timer.stop();
}
public void resume(){
timer.start();
}
public void setDelay(int delay){
this.delay=delay;
timer.setDelay(delay);
}
}
here is my code for class BallControl.java
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class BallControl extends JPanel{
private Ball ball = new Ball();
private JButton jbtSuspend = new JButton("Suspend");
private JButton jbtResume = new JButton("Resume");
private JScrollBar jsbDelay = new JScrollBar();
public BallControl(){
JPanel panel = new JPanel();
panel.add(jbtSuspend);
panel.add(jbtResume);
//ball.setBorder(new javax.swing.border.LineBorder(Color.red));
jsbDelay.setOrientation(JScrollBar.HORIZONTAL);
ball.setDelay(jsbDelay.getMaximum());
setLayout(new BorderLayout());
add(jsbDelay, BorderLayout.NORTH);
add(ball, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
jbtSuspend.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ball.suspend();
}
});
jbtResume.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ball.resume();
}
});
jsbDelay.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
ball.setDelay(jsbDelay.getMaximum() - e.getValue());
}
});
}
}

Isn't the Timer supposed to also change the Ball object's position? In other words, isn't it supposed to change its x and y values? i.e.,
private class TimerListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
// first change x and y here *****
repaint();
}
}
Else, how is the ball supposed to move much less bounce?
You seem to have this change position code in your paintComponent(...) method, and that is not good since you don't have full control over when or even if this method gets called. For this reason, program logic and code that changes this object's state does not belong inside of that method.
Also, your paintComponent(...) method override needs a call to to the super's paintComponent(...) method on its first line so that the old ball can be erased before a new ball is drawn.

Related

Repainting-Thread doesn't repaint Inner-Class JPanel

I want to make a little rain program in swing, but for some reason I cannot repaint the panel from another class. I tried using an inner class for the panel this time, but it doesn't seem to work with repainting it from another class/thread. Does someone know why?
sscce:
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
public class UI extends JFrame {
public static void main(String[] args) {
UI myProgram = new UI();
myProgram.setVisible(true);
}
public UI() {
this.setSize(new Dimension(500,300));
this.setBackground(Color.WHITE);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
UserPanel p = new UserPanel(this);
}
public class UserPanel extends JPanel implements ActionListener {
private Timer time = new Timer(1, this);
private UI myFrame;
public UserPanel(UI myFrame) {
this.myFrame = myFrame;
this.setSize(myFrame.getSize());
time.start();
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("painting");
g.setColor(Color.BLACK);
g.fillRect(this.getWidth()/2, this.getHeight()/2, 50,50);
}
}
}
UI Class (with inner class JPanel):
package Rain;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
public class UI extends JFrame {
public UI() {
this.setSize(new Dimension(500,300));
this.setBackground(Color.WHITE);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
UserPanel p = new UserPanel(this);
}
private class UserPanel extends JPanel implements ActionListener {
private Timer time = new Timer(1, this);
private UI myFrame;
private ArrayList<Raindrop> rain = new ArrayList<Raindrop>();
private static final int AMOUNT = 50;
private Random rand = new Random();
public UserPanel(UI myFrame) {
this.myFrame = myFrame;
this.setSize(myFrame.getSize());
for(int i = 0; i < AMOUNT; i++) {
createRain();
}
new Painter(this);
time.start();
}
public void createRain() {
float distance = rand.nextFloat() * 90 + 10;
int x = rand.nextInt(this.getWidth());
int y = 100;
rain.add(new Raindrop(distance,x,y));
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("tick");
for(Raindrop r : rain) {
r.fall();
}
}
public void paintComponent(Graphics g) {
System.out.println("painting");
g.setColor(this.getBackground());
g.fillRect(0,0,this.getWidth(),this.getHeight());
for(Raindrop r : rain) {
r.draw(g);
}
}
}
}
Painter:
package Rain;
import javax.swing.JPanel;
public class Painter extends Thread {
private JPanel p;
public Painter(JPanel p) {
this.p = p;
this.start();
}
public void run() {
while(true) {
System.out.println("trying to paint..");
p.repaint();
}
}
}
Console Output:
trying to paint..
tick
trying to paint..
tick
...
Expected Output:
trying to paint..
painting
tick
trying to paint..
...
The thread does work but it never calls the paintComponent(Graphics g) function in the panel
All Swing applications must run on their own thread, called EDT. (Hopefully, you start your application by calling SwingUtilities#invokelater method). So, repainting a component outside of Event Dispatch Thread is really bad bad (bad) idea. Instead of creating new Thread, repaint the component inside javax.swing.Timer's action listener since it will run in EDT.
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("tick");
for(Raindrop r : rain) {
r.fall();
}
repaint(); //repaint in EDT
}
Also, when you #Override paintComponent method, always start by calling super.paintComponent(g);
public void paintComponent(Graphics g) {
super.paintComponent(g);//let component get painted normally
System.out.println("painting");
g.setColor(this.getBackground());
g.fillRect(0,0,this.getWidth(),this.getHeight());
for(Raindrop r : rain) {
r.draw(g);
}
}
UPDATE after your SSCCE
In order a component to get painted, it must have a parent. You UserPanel p = new UserPanel(this); but you never add it to the frame:
UserPanel p = new UserPanel(this);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(p);
The complete SSCCE:
public class UI extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> { //Run in EDT
UI myProgram = new UI();
myProgram.setVisible(true);
});
}
public UI() {
super("title");//call super for frame
this.setSize(new Dimension(500, 300));
this.setBackground(Color.WHITE);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
UserPanel p = new UserPanel(this);
//Use border layout to make p fit the whole frame
getContentPane().setLayout(new BorderLayout());
getContentPane().add(p, BorderLayout.CENTER);
}
public class UserPanel extends JPanel implements ActionListener {
private Timer time = new Timer(1, this);
private UI myFrame;
public UserPanel(UI myFrame) {
this.myFrame = myFrame;
this.setSize(myFrame.getSize());
time.start();
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("painting");
g.setColor(Color.BLACK);
g.fillRect(this.getWidth() / 2, this.getHeight() / 2, 50, 50);
}
}
}
Don't ignore the SwingUtilities.invokeLater.

Moving Graphics in JPanel

I'll like to make an Oval move from one place to the other in a JPanel when a button is clicked. This is the code I came up with. When I click the button however it all happens at once without visible movement the slow from the start to finish seen. The Oval just appears in a new location.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.JPanel;
public class testtest implements ActionListener{
JButton button;
MyDrawPanel panel;
int x = 0;
int y = 0;
public static void main(String[]args){
testtest test = new testtest();
test.go();
}
public void go(){
JFrame frame = new JFrame("Balloon Balls");
panel = new MyDrawPanel();
button = new JButton("Restart");
button.addActionListener(this);
panel.add(button);
frame.setSize(300, 300);
frame.add(panel);
frame.setVisible(true);
}
#Override
public void actionPerformed (ActionEvent e){
for(int i=0;i<130;i++){
x++;
y++;
panel.repaint();
try {
Thread.sleep(100);
} catch(Exception ex) { }
}
}
class MyDrawPanel extends JPanel{
#Override
public void paintComponent(Graphics g){
g.fillOval(x, y, 30, 30);
g.setColor(Color.BLACK);
}
}
}
Swing is single thread AND not thread safe.
Using Thread.sleep(100) within the ActionListener is blocking the Event Dispatching Thread, preventing anything from been painted. A new paint pass won't occur until after the actionPerformed method exists.
See Concurrency in Swing for more details.
Swing is also not thread safe, this means you should never make changes to the UI from outside the context of the EDT.
The easiest solution is to make use of a Swing Timer, which will allow to establish regularly timed callbacks, which are executed within the Event Dispatching Thread, but which won't block the EDT.
You're also missing one of the important concepts of OO, encapsulation. The x/y properties should actually be managed by the MyDrawPanel, not testtest
For example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class testtest implements ActionListener {
JButton button;
MyDrawPanel panel;
public static void main(String[] args) {
testtest test = new testtest();
test.go();
}
public void go() {
JFrame frame = new JFrame("Balloon Balls");
panel = new MyDrawPanel();
button = new JButton("Restart");
button.addActionListener(this);
panel.add(button);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private Timer timer;
public void actionPerformed(ActionEvent e) {
if (timer != null) {
return;
}
timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
if (panel.update()) {
timer.stop();
timer = null;
}
}
});
timer.start();
}
class MyDrawPanel extends JPanel {
private int xPosy = 0;
private int yPosy = 0;
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
public boolean update() {
xPosy++;
yPosy++;
repaint();
return xPosy > getWidth() || yPosy > getHeight();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(xPosy, yPosy, 30, 30);
g.setColor(Color.BLACK);
}
}
}
paintComponent does just that, it paints the panel. Initially the panel paints the oval at the start x y. You push the button and the window is erased, and repainted at the new XY.
Movement is a concept you'll need to teach the computer. If we update the panel multiple times a second and slowly move the x y, we would make an illusion of movement.
Make a timer that refreshes every 10ms. Each time it refreshes, slightly increment the x and y values and repaint the panel.
In addition to the explanation about Swing thread issues in MadProgrammer's answer I would recommend separating the gui from its control by implementing the MVC Pattern.
This offers better encapsulation, better separation of responsibilities, and makes it easier to use threads for off-edt processing.
Have a model that holds all the information that the view (gui) needs:
/*
* The model contains the information for the view and information from the view
* The model is independent of the user interface.
* It notifies Listener on changes.
*/
class Model {
private Listener listener;
private int x = 0, y = 0;
synchronized int getX() {return x;}
synchronized void setX(int x) { this.x = x; }
synchronized int getY() {return y;}
synchronized void setY(int y) { this.y = y; }
void setListener(Listener listener){
this.listener = listener;
}
//notify listener when changed
void notifyListener(){
if(listener != null) {
listener.onChange();
}
}
}
In this case synchronization was added to allow the model to be used by threads.
Listener is defined by :
/*
* A simple interface used to link View and Model
*/
interface Listener {
void onChange();
}
View is just that. It implements Listener so it can listen to Model changes:
/*
* View is just that: a dumb as possible display
*/
public class View implements Listener{
private final JButton button;
private final MyDrawPanel panel;
private final Model model;
public View(Model model) {
this.model = model;
panel = new MyDrawPanel();
button = new JButton("Restart");
panel.add(button);
}
public void go(){
JFrame frame = new JFrame("Balloon Balls");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.add(panel);
frame.setVisible(true);
}
class MyDrawPanel extends JPanel{
#Override
public void paintComponent(Graphics g){
super.paintComponent(g); //always call super
g.fillOval(model.getX(), model.getY(), 30, 30);
g.setColor(Color.BLACK);
}
}
#Override
public void onChange() {
panel.repaint();
}
void addActionListener(ActionListener listener){
button.addActionListener(listener);
}
}
Putting it all together: see the following mvce : it adds a controller that controls the model and view.
For convenience and simplicity, the following code can be copy-pasted into one file called View.java, and run.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
/*
* View is just that: a dumb as possible display
*/
public class View implements Listener{
private final JButton button;
private final MyDrawPanel panel;
private final Model model;
public View(Model model) {
this.model = model;
panel = new MyDrawPanel();
button = new JButton("Restart");
panel.add(button);
}
public void go(){
JFrame frame = new JFrame("Balloon Balls");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.add(panel);
frame.setVisible(true);
}
class MyDrawPanel extends JPanel{
#Override
public void paintComponent(Graphics g){
super.paintComponent(g); //always call super
g.fillOval(model.getX(), model.getY(), 30, 30);
g.setColor(Color.BLACK);
}
}
#Override
public void onChange() {
panel.repaint();
}
void addActionListener(ActionListener listener){
button.addActionListener(listener);
}
public static void main(String[]args){
new Controller();
}
}
/*
* A simple interface used to link View and Model
*/
interface Listener {
void onChange();
}
/*
* The model contains the information for the view and information from the view
* The model is independent of the user interface.
* It notifies Listener on changes.
*/
class Model {
private Listener listener;
private int x = 0, y = 0;
synchronized int getX() {return x;}
synchronized void setX(int x) { this.x = x; }
synchronized int getY() {return y;}
synchronized void setY(int y) { this.y = y; }
void setListener(Listener listener){
this.listener = listener;
}
//notify listener when changed
void notifyListener(){
if(listener != null) {
listener.onChange();
}
}
}
/*
* The controller "wires" the view and model, and does the processing.
*/
class Controller implements ActionListener{
private final Model model;
private final View view;
public Controller() {
model = new Model();
view = new View(model);
model.setListener(view);
view.addActionListener(this);
view.go();
}
#Override
public void actionPerformed (ActionEvent e){
new Thread(()->{
for(int i=0;i<130;i++){
model.setX(model.getX()+1);
model.setY(model.getY()+1);
model.notifyListener();
System.out.println(model.getX()+" - "+ model.getY());
try {
Thread.sleep(100);
} catch(Exception ex) { }
}
}).start();
}
}

i can't see circle moving

While using Swing in java, I am trying to move a circle slowly from a starting position to an end position when clicking a button. However, I can't see the circle moving. It just moves from start to end in an instant.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MyApp {
private int x = 10;
private int y = 10;
private JFrame f;
private MyDraw m;
private JButton b;
public void go() {
f = new JFrame("Moving circle");
b = new JButton("click me to move circle");
m = new MyDraw();
f.add(BorderLayout.SOUTH, b);
f.add(BorderLayout.CENTER, m);
f.setSize(500, 500);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
b.addActionListener(new Bute());
}
public static void main(String[] args) {
MyApp m = new MyApp();
m.go();
}
private class Bute implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < 150; i++) {
++x;
++y;
m.repaint();
Thread.sleep(50);
}
}
}
private class MyDraw extends JPanel {
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.white);
g.fillRect(0, 0, 500, 500);
g.setColor(Color.red);
g.fillOval(x, y, 40, 40);
}
}
}
I think the problem is with the action listener because when I'm doing it without using button it is working. Any suggestions?
As Andrew Thompson said, calling Thread.sleep() without defining a second thread freezes everything, so the solution is to define and run another thread like so:
class Bute implements ActionListener, Runnable {
//let class implement Runnable interface
Thread t; // define 2nd thread
public void actionPerformed(ActionEvent e) {
t = new Thread(this); //start a new thread
t.start();
}
#Override //override our thread's run() method to do what we want
public void run() { //this is after some java-internal init stuff called by start()
//b.setEnabled(false);
for (int i = 0; i < 150; i++) {
x++;
y++;
m.repaint();
try {
Thread.sleep(50); //let the 2nd thread sleep
} catch (InterruptedException iEx) {
iEx.printStackTrace();
}
}
//b.setEnabled(true);
}
}
The only problem with this solution is that pressing the button multiple times will speed up the circle, but this can be fixed by making the button unclickable during the animation via b.setEnabled(true/false). Not the best solution but it works.
As said in the comments and another answer, don't block the EDT. Thead.sleep(...) will block it, so you have two options:
Create and manage your own (new) thread.
Use a Swing Timer
In this answer I'll be using a Swing Timer, since it's easier to use. I also changed the paintComponent method to use the Shape API and change the button text to start and stop accordingly as well as reusing the same ActionListener for the button and the timer:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MovingCircle {
private JFrame frame;
private CustomCircle circle;
private Timer timer;
private JButton button;
public static void main(String[] args) {
SwingUtilities.invokeLater(new MovingCircle()::createAndShowGui);
}
private void createAndShowGui() {
frame = new JFrame(this.getClass().getSimpleName());
circle = new CustomCircle(Color.RED);
timer = new Timer(100, listener);
button = new JButton("Start");
button.addActionListener(listener);
circle.setBackground(Color.WHITE);
frame.add(circle);
frame.add(button, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private ActionListener listener = (e -> {
if (!timer.isRunning()) {
timer.start();
button.setText("Stop");
} else {
if (e.getSource().equals(button)) {
timer.stop();
button.setText("Start");
}
}
circle.move(1, 1);
});
#SuppressWarnings("serial")
class CustomCircle extends JPanel {
private Color color;
private int circleX;
private int circleY;
public CustomCircle(Color color) {
this.color = color;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(color);
g2d.fill(new Ellipse2D.Double(circleX, circleY, 50, 50));
}
#Override
public Dimension preferredSize() {
return new Dimension(100, 100);
}
public void move(int xGap, int yGap) {
circleX += xGap;
circleY += yGap;
revalidate();
repaint();
}
public int getCircleX() {
return circleX;
}
public void setCircleX(int circleX) {
this.circleX = circleX;
}
public int getCircleY() {
return circleY;
}
public void setCircleY(int circleY) {
this.circleY = circleY;
}
}
}
I'm sorry, I can't post a GIF as I wanted but this example runs as expected.

I cannot draw objects and have them move on the screen

I can draw static things to the screen, but I want to make them move with user key input. I don't know what to do, I've been searching and searching and haven't come up with an answer yet. Please help!
package com.Game.game;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Game extends JFrame
{
final static int width = 500;
final static int height = 500;
public int x = 250;
public int y = 250;
public int changeX = 10;
public int changeY = 10;
public static void main(String[] args)
{
new Game();
}
public Game()
{
KeyListener listener = new KeyListening();
addKeyListener(listener);
setFocusable(true);
DrawingStuff drawingstuff = new DrawingStuff();
add(drawingstuff);
setSize(width, height);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public class DrawingStuff extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawString("Hey there!", 300, 300);
g.setColor(Color.RED);
g.fillRect(x, y, 50, 50);
}
}
public class KeyListening implements KeyListener
{
DrawingStuff drawingstuff = new DrawingStuff();
#Override
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == KeyEvent.VK_UP)
{
y = y + changeY;
System.out.println("Hey");
drawingstuff.repaint();
}
}
#Override
public void keyReleased(KeyEvent e)
{
}
#Override
public void keyTyped(KeyEvent e)
{
}
}
public void update()
{
}
}
EDIT: Fixed it. I took away the key listener stuff in the constructor method, added a command to focus on "drawingstuff" in the constructor method, and, most importantly, added this bit of code to the end of the constructor method:
while(true)
{
drawingstuff.repaint();
}
The problem is that your KeyListening object has a reference to a different DrawingStuff object than the one you added to your UI inside the Game constructor.
public class KeyListening implements KeyListener
{
DrawingStuff drawingstuff = new DrawingStuff();
...
You should pass a DrawingStuff reference to the KeyListening instance so that it can tell the right object to repaint itself.

The most efficient way to draw a lot of circles java

Ok so I have looked around and I didn't find anything that helped me with this so I thought I would go ahead and ask here. I want to have a bunch of circles that collide bounce and generally interact with each other. The problem I am running into is that drawing a lot of circles eats away at the frame rate. So given an array of circles which I have to draw, all of them. Is the only way to draw them simply to draw them all using g.fillOval() in the paintComponent?
For example, in the paint component, I call a function drawBalls(Graphics2D g) that draws all the balls it contains. (that's how I draw stuff by passing it through to the object that draws I don't know if there is a better way)
for(Ball ball:balls){
ball.drawYourself(g) //g is the Graphics2D
}
Thanks
Edit. Sorry if it was a bad question here is the code of my program. I hope it isn't too long.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import java.util.*;
import javax.swing.Timer;
//###################################################\\
public class Game extends JFrame implements ActionListener{
private Timer myTimer;
private GamePanel panel;
public Game() {
super("Things.py");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(1000,800);
panel = new GamePanel(this);
add(panel);
myTimer = new Timer(1, this);
myTimer.start();
setResizable(false);
setVisible(true);
}
public void actionPerformed(ActionEvent evt){
panel.repaint();
}
public static void main(String[] arguments) {
Game frame = new Game();
}
}
//###################################################\\
//###################################################\\
class GamePanel extends JPanel implements MouseMotionListener, MouseListener,KeyListener{
// ------------ Variables ----------------------------------------------
private int mx,my;//Mouse position x and y
private boolean[]keys;//If the keys are pressed
private Boolean mousepressed=false;//If the mouse is pressed down
private Game mainframe;//The panel that created this GamePanel
private Color backmenuc;
private Color backcolor;
private bounceBalls bballs;
private bounceBalls bballs2;
// ------------ Constructor --------------------------------------------
public GamePanel(Game m){
//----- Listeners -----
addMouseMotionListener(this);//LISTEN TO ME!!!
addMouseListener(this);//Listen to me!
addKeyListener(this);//listen to me pls
//----- Variables -----
mainframe=m;
keys=new boolean[KeyEvent.KEY_LAST+1];//creates the array for keys
backcolor=new Color(0,0,0);
backmenuc=new Color(255,255,255);
bballs=new bounceBalls(this);
bballs2=new bounceBalls(this);
//----- Load Images -----
//----- End -----
}
// ------------ Drawing ------------------------------------------------
public void paintComponent(Graphics gg){
Graphics2D g=(Graphics2D) gg;
paint.drawRect(g,0,0,getWidth(),getHeight(),backcolor);
if (mousepressed){
bballs.addBall(mx,my,10);
bballs2.addBall(mx+100,my,10);
}
bBallThreading b1=new bBallThreading(g,bballs,"B1");
b1.start();
bBallThreading b2=new bBallThreading(g,bballs2,"B1");
b2.start();
//bballs.update();
//bballs2.update();
bballs.draw(g);
bballs2.draw(g);
System.out.println(bballs.size());
}
// ------------ Misc ---------------------------------------------------
public void addNotify() {
super.addNotify();
}
// ------------ MouseListener ------------------------------------------
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {
mousepressed=false;
}
public void mouseClicked(MouseEvent e){
}
public void mousePressed(MouseEvent e){
mousepressed=true;
}
// ---------- MouseMotionListener --------------------------------------
public void mouseDragged(MouseEvent e){
mx=e.getX();
my=e.getY();
}
public void mouseMoved(MouseEvent e){
mx=e.getX();
my=e.getY();
}
// ---------- KeyListener -----------------------------------------------
public void keyTyped(KeyEvent e) {}
public void keyPressed(KeyEvent e) {
keys[e.getKeyCode()] = true;
}
public void keyReleased(KeyEvent e) {
keys[e.getKeyCode()] = false;
}
}
//###################################################\\
class bounceBall{
private double bx,by;
private int br;
private double vx,vy;
public bounceBall(int x,int y,int radius){
bx=(double)x;
by=(double)y;
br=radius;
vx=0;
vy=0;
}
public void move(){
if((by+vy)>800){
vy*=-.9;
}
bx+=vx;
by+=vy;
}
public void addV(double x, double y){
vx+=x;
vy+=y;
}
public void draw(Graphics2D g){
g.setColor(new Color(255,0,0));
g.fillOval((int)bx-br,(int)by-br,br*2,br*2);
}
}
class bounceBalls{
GamePanel gp;
ArrayList<bounceBall> balls;
double gravity;
public bounceBalls(GamePanel gamep){
gp=gamep;
gravity=.1;
balls=new ArrayList<bounceBall>();
}
public void draw(Graphics2D g){
//paint.setAA(g,true);
paint.setAlpha(g,.1f);
for(bounceBall ball:balls.toArray(new bounceBall[balls.size()])){
ball.draw(g);
}
paint.setAlpha(g,1f);
//paint.setAA(g,false);
}
public void setGravity(double g){
gravity=g;
}
public void update(){
for(bounceBall ball:balls.toArray(new bounceBall[balls.size()])){
ball.addV(0,gravity);
ball.move();
}
}
public void addBall(int x, int y, int radius){
balls.add(new bounceBall(x,y,radius));
}
public int size(){
return balls.size();
}
}
class bBallThreading implements Runnable{
private Thread t;
private String threadname;
Graphics2D g;
private bounceBalls bballs;
public bBallThreading(Graphics2D g, bounceBalls bballs,String s){
this.g=g;
this.bballs=bballs;
threadname=s;
}
public void run(){
bballs.update();
}
public void start(){
if (t == null)
{
t = new Thread (this, threadname);
t.start ();
}
}
}
//###################################################\\
When I run this I get 4000 circles at 30 fps
bBallThreading b1=new bBallThreading(g,bballs,"B1");
b1.start();
bBallThreading b2=new bBallThreading(g,bballs2,"B1");
b2.start();
The paintComponent() method is for painting. That is all is should do.
You should NOT be starting a Thread in a painting method. Every time Swing paints the component you will be starting two more Threads.
Add a System.out.println(...) statement to that method to see how many times that method is executed.
You should start the Thread when you start the animation.

Categories