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.
Related
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.
I'm writing a program that displays a circle every time you click the Jpanel. I have it all set up and I want to be able to use the drawCircle method I created in my circle class to draw the circles in the paintComponent method. I'm storing all of the circles created in a linked list. Then I interate through each Circle in the list and try to use the method in my Circle class called drawCircle().
For some reason, if I try to use c1.drawCircle() in a for loop in the My panel class it only draws the last circle that was created. But if I just use g.fillOval(with the correct parameters grabbing the values from the Circle class) in the for loop it works properly and displays all the circles. Why is it doing this and how do I go about using the method in the Circle class properly
I'm unsure what to try right now.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.LinkedList;
public class MouseTest {
private int borderWidth = 20;
private JFrame frame;
private boolean tracking;
private boolean start;
private boolean clearBol;
private int xstart;
private int ystart;
private int xend;
private int yend;
private LinkedList<Circle> circles;
public MouseTest() {
tracking = false;
start = false;
circles = new LinkedList<Circle>();
frame = new JFrame();
frame.setBounds(250, 98, 600, 480);
frame.setTitle("Window number three");
Container cp = frame.getContentPane();
JButton clear = new JButton("Clear");
JToggleButton circleButton = new JToggleButton()("Circles");
JToggleButton drawButton = new JToggleButton("Draw");
ButtonGroup circleOrDraw = new ButtonGroup();
MyPanel pane = new MyPanel();
clear.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
clearBol = true;
frame.repaint();
}
});
JPanel top = new JPanel();
top.setLayout(new FlowLayout());
top.add(clear);
circleOrDraw.add(circleButton);
circleOrDraw.add(drawButton);
top.add(circleOrDraw);
cp.add(top, BorderLayout.NORTH);
cp.add(pane, BorderLayout.CENTER);
pane.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
xstart = e.getX();
ystart = e.getY();
start = false;
}
public void mouseReleased(MouseEvent e) {
xend = e.getX();
yend = e.getY();
if (xend < xstart) {
int tmp = xstart;
xstart = xend;
xend = tmp;
}
if (yend < ystart) {
int tmp = ystart;
ystart = yend;
yend = tmp;
}
start = true;
frame.repaint();
}
});
pane.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
if (tracking) {
int x = e.getX();
int y = e.getY();
msg("(" + x + ", " + y + ")");
}
}
});
frame.setVisible(true);
} // constructor
public static void main(String[] arg) {
MouseTest first = new MouseTest();
} // main
public void msg(String s) {
System.out.println(s);
}
public void trackMouse() {
tracking = !tracking;
} // trackMouse
public class Circle extends JPanel {
Graphics g;
int x;
int y;
int r;
Color color;
public Circle(Graphics g, int x, int y, int r) {
this.g = g;
this.x = x;
this.y = y;
this.r = r;
int red = (int) (256 * Math.random());
int green = (int) (256 * Math.random());
int blue = (int) (256 * Math.random());
this.color = new Color(red, green, blue);
}
public void drawCircle() {
int x2 = x - (r / 2);
int y2 = y - (this.r / 2);
g.setColor(color);
g.fillOval(x2, y2, this.r, this.r);
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Color getColor() {
return color;
}
public int getR() {
return r;
}
}
public class MyPanel extends JPanel {
public void paintComponent(Graphics g) {
if (start) {
circles.add(new Circle(g, xend, yend,
(int) ((250 * Math.random() + 4))));
//Area where I'm having issues
for (Circle c1 : circles) {
msg("" + c1.getX());
// this method that I created in the circle class will only draw the first circle
//c1.drawCircle();
int r = c1.getR();
int x = c1.getX();
int y = c1.getY();
g.setColor(c1.getColor());
g.fillOval((c1.getX() - (r / 2)), (c1.getY() - (r / 2)),
r, r); // this will display all the circles
}
int size = circles.size();
msg(size + " Size");
msg("" + circles.getLast().getX());
}
if (clearBol) {
super.paintComponent(g);
circles.clear();
clearBol= false;
}
Thank you!
Most of the structure of your class needs to be changed
Your MyPanel should have a better name to give its functionality, maybe something like DrawingPanel.
The DrawingPanel is then responsible for managing the Circles to be painted. So typically you would just use an ArrayList to hold the Circle information.
Then you would add a method to the class, like addCircle(...) to add the Circle information to the ArrayList and then invoke repaint().
Then in your paintComponent(...) method the first thing you do is invoke super.paintComponent(...) to clear the panel. Then you iterate through the ArrayList and paint all the Circles. There will be no need for the Boolean values to check the state of the class. The ArrayList will either have circles or it won't.
You would also need a method like clearCircles(). This would simply remove all the Circles from the ArrayList and invoke repaint() on itself.
Your Circle class should NOT extend JPanel. It should just be a class that contains the information need to paint the circle: x/y location, size of circle and color of circle.
Now your frame is responsible of displaying your DrawingPanel and the buttons.
When you click the "Clear" button you simply invoke the clearCircles() method of the DrawingPanel.
For your MouseListener you simply invoke the addCircle(...) method of your DrawingPanel once you have all the information needed to create a Circle instance.
For a complete working example that incorporates all these suggestions check out the DrawOnComponent example found in Custom Painting Approaches
I'm looking for some guidance in finishing my program for my project. It is a simple simulation where multiple graphical objects are drawn at runtime and using a button to add another ball in this case to the jpanel, I had to rewrite my whole code to take in multiple objects at once just 2 days ago and last night I did some research when I got an exception...changed some code then got another exception in thread AWT EventQueue 0, after doing some more research many people suggested that dynamically drawing objects on a jpanel at runtime with a thread is a pain in the.... and I should just use a timer instead so this is where I am at right now. The Grid_Bag_Constraints can be ignored for the time being as its just to lay it out at the moment.
My question is how could i make it so a new ball is added at runtime on the press of a button...sorry forgot to mention
Ps yeh i tried playing round with validating the jpanels already :/
All guidance is appreciated :)
Sim
=============
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.*;
public class Sim extends JPanel
private static final int UPDATE_RATE = 60; // Number of refresh per second
public static int X = 640;
public static int Y = 480;
public static int numberOfBall = 3;
public static boolean isRunning = true;
public Sim() {
// have some code here all commented out
}
public static void main(String[] args) {
// Run GUI in the Event Dispatcher Thread (EDT) instead of main thread.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Set up main window (using Swing's Frame)
JFrame frame = new JFrame("Simulation");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = 3;
c.gridheight = 3;
balls balls = new balls();
frame.add(balls);
gui GUI = new gui();
frame.add(GUI);
frame.setPreferredSize(new Dimension(1280,720));
frame.setResizable(false);
//frame.setContentPane(new Sim());
frame.pack();
frame.setVisible(true);
new Thread(new bounceEngine(balls)).start();
}
});
}
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
invalidate();
repaint();
}
gui
---------
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class gui extends JPanel {
boolean shouldFill;
public gui(){
setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
if(shouldFill){
c.fill = GridBagConstraints.HORIZONTAL;
}
AbstractButton addBallButton = new JButton("Add Ball");
addBallButton.setMultiClickThreshhold(1500);
addBallButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent actionEvent) {
balls.listBalls.add(new Ball((new Color(Sim.random(255),Sim.random(255), Sim.random(255))),20));
for(Ball ball : balls.listBalls){
System.out.print("I work ");
}
invalidate();
//repaint();
}
});
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.weighty = 0.5;
add(addBallButton);
bounceEngine
------------------
import java.awt.*;
import javax.swing.*;
public class bounceEngine implements Runnable {
private balls parent;
private int UPDATE_RATE = 144;
public bounceEngine(balls parent) {
this.parent = parent;
}
public void run() {
int width = Sim.X;
int height = Sim.Y;
// Randomize the starting position...
for (Ball ball : getParent().getBalls()) {
int x = Sim.random(width);
int y = Sim.random(height);
int size = ball.getRadius();
if (x + size > width) {
x = width - size;
}
if (y + size > height) {
y = height - size;
}
ball.setLocation(new Point(x, y));
}
while (getParent().isVisible()) {
// Repaint the balls pen...
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
getParent().repaint();
}
});
//I know this is dangerous to update it as the repaint method is called
for (Ball ball : getParent().getBalls()) {
move(ball);
}
try{
Thread.sleep(1000/UPDATE_RATE);
}catch (InterruptedException e){
}
}
}
public balls getParent() {
return parent;
}
public void move(Ball ball) {
Point p = ball.getLocation();
Point speed = ball.getSpeed();
int size = ball.getRadius();
int vx = speed.x;
int vy = speed.y;
int x = p.x;
int y = p.y;
if (x + vx < 0 || x + (size*2) + vx > getParent().getWidth()) {
vx *= -1;
}
if (y + vy < 0 || y + (size*2) + vy > getParent().getHeight()) {
vy *= -1;
}
x += vx;
y += vy;
ball.setSpeed(new Point(vx, vy));
ball.setLocation(new Point(x, y));
}
balls(for all the balls)
---------------------------------
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.*;
public class balls extends JPanel {
public static ArrayList<Ball> listBalls;
public balls() {
setPreferredSize(new Dimension(640,480));
setBackground(Color.white);
listBalls = new ArrayList<Ball>(10);
for(int i = 0; i < Sim.numberOfBall; i++){
listBalls.add(new Ball((new Color(Sim.random(255),Sim.random(255), Sim.random(255))),20));
}
}
public ArrayList<Ball> getBall(){
return listBalls;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Ball ball : listBalls) {
ball.paint(g2d);
}
g2d.dispose();
}
public ArrayList<Ball> getBalls(){
return listBalls;
}
Ball(for 1 ball object blank class with basic constructor)
----------------------------------------------------------------------
import java.awt.*;
public class Ball {
private Color color;
private Point location;
private int radius;
private Point speed;
public Ball(Color color, int radius) {
setColor(color);
speed = new Point(5 - Sim.random(20), 5 - Sim.random(20));
this.radius = radius;
}
public int getRadius() {
return radius;
}
public void setColor(Color color) {
this.color = color;
}
public void setLocation(Point location) {
this.location = location;
}
public Color getColor() {
return color;
}
public Point getLocation() {
return location;
}
public Point getSpeed() {
return speed;
}
public void setSpeed(Point speed) {
this.speed = speed;
}
protected void paint(Graphics2D g2d) {
Point p = getLocation();
if (p != null) {
g2d.setColor(getColor());
g2d.fillOval(p.x, p.y, radius*2, radius*2);
}
}
}
I am not sure how to really word this, but I have a game that is arrow key based.
Anyways there is an options menu but after I select options when I try to hit arrow keys and move nothing happens...
I am assuming it is because I am "active" in another JFrame that is now hidden (the options menu) rather than the game screen.
Is there a way I can have the program know that I want the keyboard actions to refer back to the original JFrame when I close the options menu?
And while I am at it, I am trying to figure out how to make the game window full-screened. Right now I have it setUndecorated so no border is up and I tried the code: setExtendedState(JFrame.MAXIMIZED_BOTH); But the game is shifted way off to the bottom right of the screen.
I am an external monitor right now, would that matter?
I also have non-resizable checked (I'm on netbeans), and I have "set" sizes for the Jframe and Jpanels, should I remove those?
I hope that makes sense,
Thanks,
-Austin
*All in netbeans too.
I am assuming that you're using a KeyListener to capture key strokes, and if so, KeyListeners only work if the component being listened to has focus. Your problem is that on swapping your views, your listened to component does not have focus. One way to solve this is to call requestFocusInWindow() on the listened component after the swap.
But there's a bigger issue afoot, and that's in your use of KeyListeners to begin with, something that in general should be avoided with Swing applications. Instead use Key Bindings, a much higher level concept and thus one that should be used in favor of the low level KeyListeners.
Also, to maximize a JFrame, you'll want to call it's setExtendedState(...) method passing in Frame.MAXIMIZED_BOTH as the parameter as it appears you are doing. Are you calling pack()? Also, you're not calling setLocation(...), setBounds(...) or setSize(...) on the JFrame, right?
Edit: I see you have in fact called setSize(...) on the JFrame. Yes, remove this as it makes no sense if you're maximizing the JFrame.
Edit
Code example of what I am suggesting:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.TitledBorder;
public class AnimationWithKeyBinding {
#SuppressWarnings("serial")
private static void createAndShowUI() {
final JPanel cardPanel = new JPanel(new CardLayout());
MenuPanel menuPanel = new MenuPanel();
AnimationPanel animationPanel = new AnimationPanel();
cardPanel.add(menuPanel, "Menu");
cardPanel.add(animationPanel, "Animation");
menuPanel.setNextBtnAction(new AbstractAction("Next") {
{
putValue(NAME, "Next");
putValue(MNEMONIC_KEY, KeyEvent.VK_N);
}
#Override
public void actionPerformed(ActionEvent arg0) {
((CardLayout)cardPanel.getLayout()).next(cardPanel);
}
});
JFrame frame = new JFrame("Animation With Key Binding");
frame.getContentPane().add(cardPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
#SuppressWarnings("serial")
class MenuPanel extends JPanel {
private JButton nextBtn = new JButton();
public MenuPanel() {
TitledBorder titledBorder = BorderFactory.createTitledBorder("Menu Panel");
titledBorder.setTitleFont(titledBorder.getTitleFont().deriveFont(Font.BOLD, 24));
setBorder(titledBorder);
setLayout(new GridBagLayout());
add(nextBtn);
}
public void setNextBtnAction(Action action) {
nextBtn.setAction(action);
}
}
#SuppressWarnings("serial")
class AnimationPanel extends JPanel {
public static final int SPRITE_WIDTH = 20;
public static final int PANEL_WIDTH = 400;
public static final int PANEL_HEIGHT = 400;
private static final int MAX_MSTATE = 25;
private static final int SPIN_TIMER_PERIOD = 16;
private static final int SPRITE_STEP = 3;
private int mState = 0;
private int mX = (PANEL_WIDTH - SPRITE_WIDTH) / 2;
private int mY = (PANEL_HEIGHT - SPRITE_WIDTH) / 2;
private int oldMX = mX;
private int oldMY = mY;
private boolean moved = false;
// an array of sprite images that are drawn sequentially
private BufferedImage[] spriteImages = new BufferedImage[MAX_MSTATE];
public AnimationPanel() {
// create and start the main animation timer
new Timer(SPIN_TIMER_PERIOD, new SpinTimerListener()).start();
setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
setBackground(Color.white);
createSprites(); // create the images
setupKeyBinding();
}
private void setupKeyBinding() {
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inMap = getInputMap(condition);
ActionMap actMap = getActionMap();
// this uses an enum of Direction that holds ints for the arrow keys
for (Direction direction : Direction.values()) {
int key = direction.getKey();
String name = direction.name();
// add the key bindings for arrow key and shift-arrow key
inMap.put(KeyStroke.getKeyStroke(key, 0), name);
inMap.put(KeyStroke.getKeyStroke(key, InputEvent.SHIFT_DOWN_MASK),
name);
actMap.put(name, new MyKeyAction(this, direction));
}
}
// create a bunch of buffered images and place into an array,
// to be displayed sequentially
private void createSprites() {
for (int i = 0; i < spriteImages.length; i++) {
spriteImages[i] = new BufferedImage(SPRITE_WIDTH, SPRITE_WIDTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = spriteImages[i].createGraphics();
g2.setColor(Color.red);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
double theta = i * Math.PI / (2 * spriteImages.length);
double x = SPRITE_WIDTH * Math.abs(Math.cos(theta)) / 2.0;
double y = SPRITE_WIDTH * Math.abs(Math.sin(theta)) / 2.0;
int x1 = (int) ((SPRITE_WIDTH / 2.0) - x);
int y1 = (int) ((SPRITE_WIDTH / 2.0) - y);
int x2 = (int) ((SPRITE_WIDTH / 2.0) + x);
int y2 = (int) ((SPRITE_WIDTH / 2.0) + y);
g2.drawLine(x1, y1, x2, y2);
g2.drawLine(y1, x2, y2, x1);
g2.dispose();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(spriteImages[mState], mX, mY, null);
}
public void incrementX(boolean right) {
oldMX = mX;
if (right) {
mX = Math.min(getWidth() - SPRITE_WIDTH, mX + SPRITE_STEP);
} else {
mX = Math.max(0, mX - SPRITE_STEP);
}
moved = true;
}
public void incrementY(boolean down) {
oldMY = mY;
if (down) {
mY = Math.min(getHeight() - SPRITE_WIDTH, mY + SPRITE_STEP);
} else {
mY = Math.max(0, mY - SPRITE_STEP);
}
moved = true;
}
public void tick() {
mState = (mState + 1) % MAX_MSTATE;
}
private class SpinTimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
tick();
int delta = 20;
int width = SPRITE_WIDTH + 2 * delta;
int height = width;
// make sure to erase the old image
if (moved) {
int x = oldMX - delta;
int y = oldMY - delta;
repaint(x, y, width, height);
}
int x = mX - delta;
int y = mY - delta;
// draw the new image
repaint(x, y, width, height);
moved = false;
}
}
}
enum Direction {
UP(KeyEvent.VK_UP), DOWN(KeyEvent.VK_DOWN), LEFT(KeyEvent.VK_LEFT), RIGHT(
KeyEvent.VK_RIGHT);
private int key;
private Direction(int key) {
this.key = key;
}
public int getKey() {
return key;
}
}
// Actions for the key binding
#SuppressWarnings("serial")
class MyKeyAction extends AbstractAction {
private AnimationPanel draw;
private Direction direction;
public MyKeyAction(AnimationPanel draw, Direction direction) {
this.draw = draw;
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
switch (direction) {
case UP:
draw.incrementY(false);
break;
case DOWN:
draw.incrementY(true);
break;
case LEFT:
draw.incrementX(false);
break;
case RIGHT:
draw.incrementX(true);
break;
default:
break;
}
}
}
I have displayed an image(ball) inside the JApplet, now I want the image to move in a vertical way (up and down). The problem is I don't know how to do it.
Could someone has an idea about this matter?
You need to set the position of that image to some calculated value (means you caculate the vertical position using time, speed and maybe other restrictions).
How you'd set that position depends on how you draw the image.
Example, based on drawing in the applet's (or a nested component's) paint(Graphics g) method:
//first calculate the y-position
int yPos += timeSinceLastPaint * speed; //increment the position
if( (speed > 0 && yPos > someMaxY) || (speed < 0 && yPos <0 ) ) {
speed *= -1; //if the position has reached the bottom (max y) or the top invert the direction
}
//in your paint(Graphics g) method:
g.drawImage(image, yPos, x, null);
Then you'd have to constantly repaint the applet.
More information on animations in applets can be found here: http://download.oracle.com/javase/tutorial/uiswing/components/applet.html
another example for javax.swing.Timer with moving Ojbects created by paintComponent(Graphics g), and I have lots of Start, not some blurred Mikado :-)
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class AnimationBackground {
private Random random = new Random();
private JFrame frame = new JFrame("Animation Background");
private final MyJPanel panel = new MyJPanel();
private JLabel label = new JLabel("This is a Starry background.", JLabel.CENTER);
private JPanel stopPanel = new JPanel();
private JPanel startPanel = new JPanel();
public AnimationBackground() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
panel.setBackground(Color.BLACK);
for (int i = 0; i < 50; i++) {
Star star = new Star(new Point(random.nextInt(490), random.nextInt(490)));
star.setColor(new Color(100 + random.nextInt(155), 100 + random.nextInt(155), 100 + random.nextInt(155)));
star.setxIncr(-3 + random.nextInt(7));
star.setyIncr(-3 + random.nextInt(7));
panel.add(star);
}
panel.setLayout(new GridLayout(10, 1));
label.setForeground(Color.WHITE);
panel.add(label);
stopPanel.setOpaque(false);
stopPanel.add(new JButton(new AbstractAction("Stop this madness!!") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
panel.stopAnimation();
}
}));
panel.add(stopPanel);
startPanel.setOpaque(false);
startPanel.add(new JButton(new AbstractAction("Start moving...") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
panel.startAnimation();
}
}));
panel.add(startPanel);
frame.add(panel);
frame.pack();
frame.setLocation(150, 150);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
AnimationBackground aBg = new AnimationBackground();
}
});
}
private class Star extends Polygon {
private static final long serialVersionUID = 1L;
private Point location = null;
private Color color = Color.YELLOW;
private int xIncr, yIncr;
static final int WIDTH = 500, HEIGHT = 500;
Star(Point location) {
int x = location.x;
int y = location.y;
this.location = location;
this.addPoint(x, y + 8);
this.addPoint(x + 8, y + 8);
this.addPoint(x + 11, y);
this.addPoint(x + 14, y + 8);
this.addPoint(x + 22, y + 8);
this.addPoint(x + 17, y + 12);
this.addPoint(x + 21, y + 20);
this.addPoint(x + 11, y + 14);
this.addPoint(x + 3, y + 20);
this.addPoint(x + 6, y + 12);
}
public void setColor(Color color) {
this.color = color;
}
public void move() {
if (location.x < 0 || location.x > WIDTH) {
xIncr = -xIncr;
}
if (location.y < 0 || location.y > WIDTH) {
yIncr = -yIncr;
}
translate(xIncr, yIncr);
location.setLocation(location.x + xIncr, location.y + yIncr);
}
public void setxIncr(int xIncr) {
this.xIncr = xIncr;
}
public void setyIncr(int yIncr) {
this.yIncr = yIncr;
}
public Color getColor() {
return color;
}
}
private class MyJPanel extends JPanel {
private static final long serialVersionUID = 1L;
private ArrayList<Star> stars = new ArrayList<Star>();
private Timer timer = new Timer(20, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Star star : stars) {
star.move();
}
repaint();
}
});
public void stopAnimation() {
if (timer.isRunning()) {
timer.stop();
}
}
public void startAnimation() {
if (!timer.isRunning()) {
timer.start();
}
}
#Override
public void addNotify() {
super.addNotify();
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
timer.stop();
}
MyJPanel() {
this.setPreferredSize(new Dimension(512, 512));
}
public void add(Star star) {
stars.add(star);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Star star : stars) {
g.setColor(star.getColor());
g.fillPolygon(star);
}
}
}
}
How to move the image inside the JApplet ..?
Pretty much exactly the same way you might do it in a JFrame, JComponent or JPanel or...
Or to put that another way, nothing to do with applets and everything to do with Graphics2D. For more details, see the 2D Graphics Trail of the Java Tutorial.
When you've figured how to move an image and paint it to a Graphics2D, implement that logic in a JComponent or JPanel's paintComponent(Graphics) method and drop the component with moving image into a JApplet or JFrame (or a JPanel etc.).
For the animation side of it, use a javax.swing.Timer as seen in this example. This example does not extend any component. Instead, it creates a BufferedImage and adds it to a JLabel that is displayed to the user. When the timer fires, the code grabs the Graphics object of the image, and proceeds from there to draw the bouncing lines.
import java.awt.image.BufferedImage;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.*;
import javax.swing.*;
import java.util.Random;
class LineAnimator {
public static void main(String[] args) {
final int w = 640;
final int h = 480;
final RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
);
hints.put(
RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY
);
final BufferedImage bi = new BufferedImage(w,h, BufferedImage.TYPE_INT_ARGB);
final JLabel l = new JLabel(new ImageIcon(bi));
final BouncingLine[] lines = new BouncingLine[100];
int factor = 1;
for (int ii=0; ii<lines.length; ii++) {
lines[ii] = new BouncingLine(w*factor,h*factor);
}
final Font font = new Font("Arial", Font.BOLD, 30);
ActionListener al = new ActionListener() {
int count = 0;
long lastTime;
String fps = "";
private final BasicStroke stroke = new BasicStroke(6);
public void actionPerformed(ActionEvent ae) {
count++;
Graphics2D g = bi.createGraphics();
g.setRenderingHints(hints);
g.setColor(new Color(55,12,59));
g.fillRect(0,0,w,h);
g.setStroke(stroke);
for (int ii=0; ii<lines.length; ii++) {
lines[ii].move();
lines[ii].paint(g);
}
if ( System.currentTimeMillis()-lastTime>1000 ) {
lastTime = System.currentTimeMillis();
fps = count + " FPS";
count = 0;
}
g.setColor(Color.YELLOW);
g.setFont(font);
g.drawString(fps,5,h-5);
l.repaint();
g.dispose();
}
};
Timer timer = new Timer(25,al);
timer.start();
JOptionPane.showMessageDialog(null, l);
//System.exit(0);
timer.stop();
}
}
class BouncingLine {
private final Color color;
private static final Random random = new Random();
Line2D line;
int w;
int h;
int x1;
int y1;
int x2;
int y2;
BouncingLine(int w, int h) {
line = new Line2D.Double(random.nextInt(w),random.nextInt(h),random.nextInt(w),random.nextInt(h));
this.w = w;
this.h = h;
this.color = new Color(
random.nextInt(255)
,random.nextInt(255)
,random.nextInt(255)
,64+random.nextInt(128)
);
x1 = (random.nextBoolean() ? 1 : -1);
y1 = (random.nextBoolean() ? 1 : -1);
x2 = -x1;
y2 = -y1;
}
public void move() {
int tx1 = 0;
if (line.getX1()+x1>0 && line.getX1()+x1<w) {
tx1 = (int)line.getX1()+x1;
} else {
x1 = -x1;
tx1 = (int)line.getX1()+x1;
}
int ty1 = 0;
if (line.getY1()+y1>0 && line.getY1()+y1<h) {
ty1 = (int)line.getY1()+y1;
} else {
y1 = -y1;
ty1 = (int)line.getY1()+y1;
}
int tx2 = 0;
if (line.getX2()+x2>0 && line.getX2()+x2<w) {
tx2 = (int)line.getX2()+x2;
} else {
x2 = -x2;
tx2 = (int)line.getX2()+x2;
}
int ty2 = 0;
if (line.getY2()+y2>0 && line.getY2()+y2<h) {
ty2 = (int)line.getY2()+y2;
} else {
y2 = -y2;
ty2 = (int)line.getY2()+y2;
}
line.setLine(tx1,ty1,tx2,ty2);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setColor(color);
//line.set
g2.draw(line);
}
}
Update 1
I want to do it in JApplet(1) using the image(2), is it possible(3)?
The examples by mKorbel and myself feature either an image in a JLabel or custom rendering in a JPanel. In our case, we added the components to a JOptionPane & a JFrame. Either example could be just as easily added to a JApplet, or a JDialog, or as part of another panel, or.. See the Laying Out Components Within a Container lesson & Using Top-Level Containers in the Java Tutorial for more details.
Instead of the stars or lines in our examples, ..paint your image. My example goes so far as to demonstrate how to get the position to bounce around within the bounds of the container.
Sure it is possible, but "Batteries not included". Our intention is to give you some ideas that you can then adapt to your bouncing ball applet. I doubt anyone is going to create an example for you, using balls, in an applet. Though if you post an SSCCE that shows your intent and what you tried, I (and others) would often run with that source. If you want more specific answers, ask a more specific SSCCE. ;)
I want to do it in JApplet.
Why not both? You can have a hybrid application/applet as shown in this animation.