So I want to move a circle according to a certain velocity, like pong. But I'm having trouble updating the the circle's position. Here is my code that will just draw the circle and rectangle on the side.
import java.awt.*;
import javax.swing.*;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Game extends JFrame{
public Game(){
GameScreen p1 = new GameScreen();
add(p1);
Timer t = new Timer(1000, new ReDraw());
t.start();
}
public static void main(String[] args){
Game g = new Game();
g.setLocation(400, 200);
g.setSize(700, 600);
g.setVisible(true);
}
}
class ReDraw implements ActionListener{
static int count = 0;
static int posX = 603;
static int posY = 210;
static int velX = 50;
static int velY = 50;
public void actionPerformed(ActionEvent e){
count++;
posX -= velX;
posY -= velY;
System.out.println("Flag 1: " + posX + " " + posY);
if (count == 2)
((Timer)e.getSource()).stop();
}
}
class GameScreen extends JPanel{
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.orange);
g.fillRect(654, 200, 30, 100);
g.setColor(Color.red);
g.fillOval(ReDraw.posX, ReDraw.posY, 50, 50);
}
}
I would like to use the Timer class in swing but if you have another way I would love to hear it.
EDIT: I added an attempt at updating the position of the circle.
It is necessary to repaint() the component in question, which will result in the paintComponent(Graphics) method being called again. To do that, the listener will need to have a reference to the animation panel. This is one way to do it, that includes no changes to other parts of the code.
import java.awt.*;
import javax.swing.*;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Game001 extends JFrame {
public Game001() {
GameScreen p1 = new GameScreen();
add(p1);
Timer t = new Timer(1000, new ReDraw(p1));
t.start();
}
public static void main(String[] args) {
Game001 g = new Game001();
g.setLocation(400, 200);
g.setSize(700, 600);
g.setVisible(true);
}
}
class ReDraw implements ActionListener {
static int count = 0;
static int posX = 603;
static int posY = 210;
static int velX = 50;
static int velY = 50;
GameScreen gameScreen;
ReDraw(GameScreen gameScreen) {
this.gameScreen = gameScreen;
}
public void actionPerformed(ActionEvent e) {
count++;
posX -= velX;
posY -= velY;
System.out.println("Flag 1: " + posX + " " + posY);
gameScreen.repaint();
if (count == 4) {
((Timer) e.getSource()).stop();
}
}
}
class GameScreen extends JPanel {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.orange);
g.fillRect(654, 200, 30, 100);
g.setColor(Color.red);
g.fillOval(ReDraw.posX, ReDraw.posY, 50, 50);
}
}
Related
Thanks in advance for help
I created a program that makes multiple bouncing balls When user clicks on the screen a new ball should appear and move around screen. But when i click on the screen a ball appears and doesn't moving at all. When another click happens, the ball created previously jumped to another position instantly.
this is the ball class: used to create balls
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.util.Random;
import javax.swing.JComponent;
public class Ball extends JComponent implements Runnable{
private final int DIAMETER = 25;
private final int v1 = 5;
private final int v2 = -5;
private final Random rnd = new Random();
private int posX;
private int posY;
private Color color;
private int xVelocity;
private int yVelocity;
public Ball(int posX, int posY) {
this.posX = posX;
this.posY = posY;
this.color = randomColor();
this.xVelocity = rnd.nextBoolean()?v1:v2;
this.yVelocity = rnd.nextBoolean()?v1:v2;
}
public void move() {
if (posX < 15) {
xVelocity = -xVelocity;
} else if(posX > 475) {
xVelocity = -xVelocity;
}
if (posY < 0) {
yVelocity = -yVelocity;
} else if(posY > 475) {
yVelocity = -yVelocity;
}
posX +=xVelocity;
posY +=yVelocity;
}
public void draw(Graphics2D g2) {
g2.setColor(color);
g2.fill(new Ellipse2D.Double(posX,posY,DIAMETER,DIAMETER));
}
private static Color randomColor() {
int r = (int)(Math.random()*255);
int g = (int)(Math.random()*255);
int b = (int)(Math.random()*255);
Color rColor = new Color(r,g,b);
return rColor;
}
#Override
public void run() {
while(!Thread.interrupted()) {
move();
repaint();
try {
Thread.sleep(60);
} catch (InterruptedException ex) {}
}
}
}
this is the ballcomponent class: used to create the panel & display the balls
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BallComponent extends JPanel{
private ArrayList<Ball> bList;
public BallComponent() {
bList = new ArrayList<Ball>();
this.setPreferredSize(new Dimension(500,500));
this.setBackground(Color.BLACK);
this.addMouseListener(new ClickListener());
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
super.paintComponent(g2);
for (Ball a : bList) {
a.draw(g2);
}
}
private class ClickListener extends MouseAdapter{
public void mouseClicked(MouseEvent e) {
Ball a = new Ball(e.getX(),e.getY());
bList.add(a);
repaint();
Thread gameThread = new Thread(a);
gameThread.start();
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setTitle("Bouncing Balls");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BallComponent());
frame.pack();
frame.setVisible(true);
}
}
Since you have a bunch of threads doing random stuff. You might as well have your JPanel update on it's own.
At the end of your main method.
new Thread( ()->{
while(true){
frame.repaint();
try{
Thread.sleep(60);
} catch(Exception e){
break;
}
}
}).start();
Usually you would do this with a timer task, but since you said you couldn't use a Timer, I just wrote a thread version.
I think repaint() is safe to call from off of the EDT since it just schedules a repaint. The paintComponent method and click method will only be called on the EDT. So you shouldn't get CCME. There are a bunch of race conditions with the multiple threads, but it seems like the only problem would be balls drawn out of position.
So I'm new at java and need some help with my breakout game. My JFrame is just blank and i don't know how to fix it?
So I have a ball class, paddle class, canvas class and a brick class as well as a main class. In my canvas class I set all functions the ball, paddle and bricks has etc. In brick class I draw the bricks. And in my main I do the JFrame but it's blank
Main class :
public class Main {
public static void main(String[] args){
JFrame frame = new JFrame();
Canvas c = new Canvas();
frame.add(c);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
I expect the JFrame to show the game instead of just blank window
package breakout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.KeyEvent;
import breakout.Bricks.Type;
public class Canvas extends JPanel implements ActionListener, MouseMotionListener, MouseListener, KeyListener {
/**
*
*/
private static final long serialVersionUID = 1L;
private static final int HEIGHT = 600;
public static final int WIDTH = 720;
private int horizontalCount;
private BufferedImage image;
private Graphics2D bufferedGraphics;
private Timer time;
private static final Font endFont = new Font(Font.SANS_SERIF, Font.BOLD, 20);
private static final Font scoreFont = new Font(Font.SANS_SERIF, Font.BOLD, 15);
private Paddle player;
private Ball ball;
ArrayList<ArrayList<Bricks>> bricks;
public Canvas() {
super();
setPreferredSize( new Dimension(WIDTH, HEIGHT));
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
bufferedGraphics = image.createGraphics();
time = new Timer (15, this);
player = new Paddle((WIDTH/2)-(Paddle.PADDLE_WIDTH/2));
ball = new Ball (((player.getX() + (Paddle.PADDLE_WIDTH / 2 )) - (Ball.DIAMETER / 2)), (Paddle.Y_POS - (Ball.DIAMETER + 10 )), -5, -5);
bricks = new ArrayList<ArrayList<Bricks>>();
horizontalCount = WIDTH / Bricks.BRICK_WIDTH;
for(int i = 0; i < 8; ++i) {
ArrayList<Bricks> temp = new ArrayList<Bricks>();
#SuppressWarnings("unused")
Type rowColor = null;
switch(i) {
case 0 :
case 2:
rowColor = Type.LOW;
break;
case 1 :
case 3 :
case 5 :
rowColor = Type.MEDIUM;
break;
case 4 :
case 6 :
rowColor = Type.HIGH;
break;
case 7 :
default :
rowColor = Type.ULTRA;
break;
}
for(int j = 0; j < horizontalCount; ++j) {
Bricks tempBrick = new Bricks();
temp.add(tempBrick);
}
bricks.add(temp);
addMouseMotionListener(this);
addMouseListener(this);
addKeyListener(this);
requestFocus();
}
}
public void actionPerformed(ActionEvent e) {
checkCollisions();
ball.Move();
for(int i = 0; i < bricks.size(); ++i) {
ArrayList<Bricks> al = bricks.get(i);
for(int j = 0; j < al.size(); ++j) {
Bricks b = al.get(j);
if(b.dead()) {
al.remove(b);
}
}
}
repaint();
}
private void checkCollisions() {
if(player.hitPaddle(ball)) {
ball.setDY(ball.getDY() * -1);
return;
}
if(ball.getX() >= (WIDTH - Ball.DIAMETER) || ball.getX() <= 0) {
ball.setDX(ball.getDX() * -1);
}
if(ball.getY() > (Paddle.Y_POS + Paddle.PADDLE_HEIGHT + 10)) {
resetBall();
}
if(ball.getY() <= 0) {
ball.setDY(ball.getDY() * -1);
}
int brickRowActive = 0;
for(ArrayList<Bricks> alb : bricks) {
if(alb.size() == horizontalCount) {
++brickRowActive;
}
}
for(int i = (brickRowActive==0) ? 0 : (brickRowActive - 1); i < bricks.size(); ++i) {
for(Bricks b : bricks.get(i)) {
if(b.hitBy(ball)) {
player.setScore(player.getScore() + b.getBrickType().getPoints());
b.decrementType();
}
}
}
}
private void resetBall() {
if(gameOver()) {
time.stop();
return;
}
ball.setX(WIDTH/2);
ball.setDY((HEIGHT/2) + 80);
player.setLives(player.getLives() -1);
player.setScore(player.getScore() <= 1);
}
private boolean gameOver() {
if(player.getLives() <= 1) {
return true;
}
return false;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
bufferedGraphics.clearRect(0, 0, WIDTH, HEIGHT);
player.drawPaddle(bufferedGraphics);
player.drawBall(bufferedGraphics);
for(ArrayList<Bricks> row : bricks) {
for(Bricks b : row) {
b.drawBrick(bufferedGraphics);
}
}
bufferedGraphics.setFont(scoreFont);
bufferedGraphics.drawString("Score: " + player.getScore(), 10, 25);
if(gameOver() && ball.getY() >= HEIGHT) {
bufferedGraphics.setColor(Color.black);
bufferedGraphics.setFont(endFont);
bufferedGraphics.drawString("Game Over Score: " + player.getScore(), (WIDTH /2) -85, (HEIGHT/2));
}
if(empty()) {
bufferedGraphics.setColor(Color.black);
bufferedGraphics.setFont(endFont);
bufferedGraphics.drawString("You won. Score: " + player.getScore(), (WIDTH /2) -85, (HEIGHT /2));
time.stop();
}
g.drawImage(image, 0, 0, this);
Toolkit.getDefaultToolkit().sync();
}
private boolean empty() {
for(ArrayList<Bricks> al : bricks) {
if(al.size() != 0) {
return false;
}
}
return true;
}
#Override
public void mouseMoved(MouseEvent e) {
player.setX(e.getX() - (Paddle.PADDLE_WIDTH / 2));
}
#Override
public void mouseClicked(MouseEvent e) {
if(time.isRunning()) {
return;
}
time.start();
}
#Override
public void mouseDragged(MouseEvent e) { }
#Override
public void mouseEntered(MouseEvent arg0) {}
#Override
public void mouseExited(MouseEvent arg0) {}
#Override
public void mousePressed(MouseEvent arg0) {}
#Override
public void mouseReleased(MouseEvent arg0) {}
#Override
public void keyPressed(KeyEvent arg0) {}
#Override
public void keyReleased(KeyEvent arg0) {}
#Override
public void keyTyped(KeyEvent arg0) {}
}
Preparing an MCVE, as required in SO, not only it makes helping much easier.
In many case, while preparing one, you are likely to find the problem, so it is a good debugging tool.
To answer "why is my JFrame blank ?" you could create the minimal code example like the following (copy-paste the entire code into GameBoard.java and run):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameBoard extends JPanel {
static final int HEIGHT = 600, WIDTH = 720, BRICK_ROWS = 8;
private final int horizontalCount;
private static final Font scoreFont = new Font(Font.SANS_SERIF, Font.BOLD, 15);
private final Paddle player;
private final Ball ball;
ArrayList<ArrayList<Brick>> bricks;
public GameBoard() {
super();
setPreferredSize( new Dimension(WIDTH, HEIGHT));
player = new Paddle(WIDTH/2-Paddle.PADDLE_WIDTH/2);
ball = new Ball (player.getX() + Paddle.PADDLE_WIDTH / 2 - Ball.DIAMETER / 2,
Paddle.Y_POS - (Ball.DIAMETER + 10 ));
bricks = new ArrayList<>();
horizontalCount = WIDTH / Brick.BRICK_WIDTH;
for(int i = 0; i < BRICK_ROWS; ++i) {
ArrayList<Brick> temp = new ArrayList<>();
for(int j = 0; j < horizontalCount; ++j) {
Brick tempBrick = new Brick(j*Brick.BRICK_WIDTH , Brick.BRICK_YPOS + i*Brick.BRICK_HEIGHT);
temp.add(tempBrick);
}
bricks.add(temp);
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D)g;
g2D.clearRect(0, 0, WIDTH, HEIGHT);
player.drawPaddle(g2D);
ball.drawBall(g2D);
for(ArrayList<Brick> row : bricks) {
for(Brick b : row) {
b.drawBrick(g2D);
}
}
g2D.setFont(scoreFont);
g2D.drawString("Score: " + player.getScore(), 10, 25);
}
}
class Paddle{
public final static int PADDLE_WIDTH = 100, PADDLE_HEIGHT= 30, Y_POS = GameBoard.HEIGHT - 2* PADDLE_HEIGHT;
private int xPos, score;
Paddle(int xPos) {
this.xPos = xPos;
}
void setX(int xPos) {this.xPos = xPos;}
int getX() {return xPos;}
String getScore() {
return String.valueOf(score);
}
void drawPaddle(Graphics2D g2D) {
g2D.setColor(Color.GREEN);
g2D.fillRect(xPos, Y_POS, PADDLE_WIDTH, PADDLE_HEIGHT);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(400,250);
frame.add(new GameBoard());
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
}
}
class Brick{
final static int BRICK_WIDTH = 80, BRICK_HEIGHT = 15, BRICK_YPOS = 50;
int xPos, yPos;
Brick(int xPos, int yPos) {
this.xPos = xPos;
this.yPos = yPos;
}
void drawBrick(Graphics2D g2D) {
g2D.setColor(Color.RED);
g2D.fillRect(xPos, yPos, BRICK_WIDTH, BRICK_HEIGHT);
g2D.setColor(Color.BLACK);
g2D.drawRect(xPos, yPos, BRICK_WIDTH, BRICK_HEIGHT);
}
}
class Ball{
final static int DIAMETER = 40;
int xPos, yPos;
Ball(int xPos, int yPos) {
this.xPos = xPos;
this.yPos = yPos;
}
void drawBall(Graphics2D g2D) {
g2D.setColor(Color.BLUE);
g2D.fillOval(xPos, yPos, DIAMETER, DIAMETER);
}
}
This produces the following result, which I believe can serve as the basis of what you wanted to achieve:
Now start adding the missing functionality and see what breaks it.
OK so i'm working on a school project (little animation) and I am currently trying to make rain. I'm not sure how I would go about drawing individual "drops" using JPanel. My Code so far:
Main Class:
public class RainPanel extends JPanel {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
new RainPanel();
}
private final int WIDTH = 800, HEIGHT = 800;
Drop drop;
public RainPanel() {
init();
}
public void init() {
JFrame frame = new JFrame("Rain");
JPanel drop = new Drop();
frame.setVisible(true);
frame.setSize(WIDTH, HEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.add(drop);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
drop.paint(g);
}
Drop class:
public class Drop extends JPanel implements ActionListener{
private static final long serialVersionUID = 1L;
int x,y;
int yVel = 2;
Timer t = new Timer(5, this);
Random r = new Random();
ArrayList<Drop> DropArray;
public Drop() {
x = r.nextInt(800);
y = r.nextInt(800);
t.start();
}
public void paint(Graphics g) {
super.paintComponent(g);
DropArray = new ArrayList<>(100);
for (int i = 0; i < DropArray.size(); i++) {
DropArray.add(new Drop());
}
g.setColor(Color.BLUE);
g.fillRect(x, y, 3, 15);
}
public void update() {
y += yVel;
if (y > 800)
y = r.nextInt(800);
}
#Override
public void actionPerformed(ActionEvent e) {
update();
repaint();
}
I understand if you might be cringing hard right now (I'm fairly new to graphics coding and mostly familiar with Java itself). All i'm getting drawn currently is a single rain drop. Any suggestions are appreciated.
Don't call super.paintComponent from within paint, you're breaking the paint chain which could cause no end of issues. Override paintComponent directly instead
You shouldn't be modifying the state of a component or anything the component relies on from within any paint method, paint can be called a number of times in quick succession and this can cause no end of issues
Component based animation is not a simple task and unless you really, really need it, you should try and avoid it. Instead, write a class which is "paintable", which you can call from your paintComponent method
For example..
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class RainDropsKeepFalling {
public static void main(String[] args) {
new RainDropsKeepFalling();
}
public RainDropsKeepFalling() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new RainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class RainPane extends JPanel {
private List<Drop> drops = new ArrayList<>(100);
public RainPane() {
for (int index = 0; index < 100; index++) {
drops.add(new Drop(getPreferredSize()));
}
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Drop drop : drops) {
drop.update(getSize());
repaint();
}
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Drop drop : drops) {
Graphics2D g2d = (Graphics2D) g.create();
drop.paint(g2d);
g2d.dispose();
}
}
}
protected static final Random random = new Random();
public static class Drop {
private double vDelta = random.nextDouble() + 0.5;
private int height = 15;
private int width = 3;
private double x;
private double y = -height;
private Rectangle2D shape;
public Drop(Dimension size) {
x = random.nextInt(size.width - width) + width;
y = random.nextInt(size.height - height) + height;
shape = new Rectangle2D.Double(x, y, width, height);
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.BLUE);
g2d.fill(shape);
}
public void update(Dimension size) {
y += vDelta;
if (y > size.height) {
y = -height;
x = random.nextInt(size.width - width) + width;
}
shape.setRect(x, y, width, height);
}
}
}
I'm trying to create a simple panel where a 2-dimensional ball is bouncing up and down. I can't get it to work because for some reason I can't call the repaint method more than once a second. The design is basically that there is an object that can be given "an impulse" with the method move(). Everytime the evaluatePosition method is called, the current position will be calculated through the time that has passed, the velocity and the acceleration. The code for the panel is:
public class Display extends JPanel {
private MovableObject object = new MovableObject(new Ellipse2D.Double(5,5,50,50));
private static final int DELAY = 1000;
public Display(){
object.move(50,50);
Timer timer = new Timer(DELAY, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
object.evaluatePosition();
repaint();
}
});
timer.start();
}
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawOval((int)object.getPosition().getX(), (int)object.getPosition.getY()
(int)object.getShape().getWidth(), object.getShape().getHeight());
}
This code works for DELAY=1000 but not for DELAY=100 or DELAY=10 and so on. I read some example code here on SO but they all seem to me like what I already did. So why is my code not working?
EDIT (2016-01-30):
Since it really seems to be a performance issue, here's the code for the MovableObject (I just thought it would be irrelevant and you will probably see why):
public class MovableObject {
// I would really like to use Shape instead of Ellipse2D so that
// objects of any shape can be created
private Ellipse2D.Double shape;
private Point position;
// Vector is my own class. I want to have some easy vector addition and
// multiplication and magnitude methods
private Vector velocity = new Vector(0, 0);
private Vector acceleration = new Vector(0, 0);
private Date lastEvaluation = new Date();
public MovableObject(Ellipse2D.Double objectShape){
shape = objectShape;
}
public void evaluatePosition(){
Date currentTime = new Date();
long deltaTInS = (currentTime.getTime()-lastEvaluation.getTime())/1000;
// s = s_0 + v*t + 0.5*a*t^2
position = new Point((int)position.getX()+ (int)(velocity.getX()*deltaTInS) + (int)(0.5*acceleration.getX()*deltaTInS*deltaTInS),
(int)position.getY()+ (int)(velocity.getY()*deltaTInS) + (int)(0.5*acceleration.getY()*deltaTInS*deltaTInS));
lastEvaluation = currentTime;
}
}
public void move(Vector vector){
velocity = velocity.add(vector);
evaluatePosition();
}
public Point getPosition(){
return position;
}
public Ellipse2D.Double getShape(){
return shape;
}
My move method does not change position but velocity. Please notice that I just changed the shape Object from Shape to Ellipse2D for testing if my code has a performance issue because of the additional code. So if you think this is more complex than it needs to be: I actually want to add some complexity so that the MovableObject can have the shape of any subclass of shape. I've seen a lot of code that seemed more complex to me and rendered fast. So I'd like to know what's wrong with this (besides the fact that it's a bit too complex for just rendering an ellipse).
Okay, so this is a simple example, based on the out-of-context code snippet you left which doesn't seem to have any problems. It has variable speed controlled by a simple slider...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Display extends JPanel {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Display());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
private MovableObject object = new MovableObject(new Ellipse2D.Double(5, 5, 50, 50));
private int delay = 40;
private Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
object.evaluatePosition(getSize());
repaint();
}
});
private JSlider slider = new JSlider(5, 1000);
public Display() {
object.move(50, 50);
slider = new JSlider(5, 1000);
slider.setSnapToTicks(true);
slider.setMajorTickSpacing(10);
slider.setMinorTickSpacing(5);
setLayout(new BorderLayout());
add(slider, BorderLayout.SOUTH);
// This is simply designed to put an artificate delay between the
// change listener and the time the update takes place, the intention
// is to stop it from pausing the "main" timer...
Timer delay = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (timer != null) {
timer.stop();
}
timer.setDelay(slider.getValue());
timer.start();
}
});
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
delay.restart();
repaint();
}
});
slider.setValue(40);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.draw(object.getTranslatedShape());
FontMetrics fm = g2.getFontMetrics();
String text = Integer.toString(slider.getValue());
g2.drawString(text, 0, fm.getAscent());
g2.dispose();
}
public class MovableObject {
private Shape shape;
private Point location;
private int xDelta, yDelta;
public MovableObject(Shape shape) {
this.shape = shape;
location = shape.getBounds().getLocation();
Random rnd = new Random();
xDelta = rnd.nextInt(8);
yDelta = rnd.nextInt(8);
if (rnd.nextBoolean()) {
xDelta *= -1;
}
if (rnd.nextBoolean()) {
yDelta *= -1;
}
}
public void move(int x, int y) {
location.setLocation(x, y);
}
public void evaluatePosition(Dimension bounds) {
int x = location.x + xDelta;
int y = location.y + yDelta;
if (x < 0) {
x = 0;
xDelta *= -1;
} else if (x + shape.getBounds().width > bounds.width) {
x = bounds.width - shape.getBounds().width;
xDelta *= -1;
}
if (y < 0) {
y = 0;
yDelta *= -1;
} else if (y + shape.getBounds().height > bounds.height) {
y = bounds.height - shape.getBounds().height;
yDelta *= -1;
}
location.setLocation(x, y);
}
public Shape getTranslatedShape() {
PathIterator pi = shape.getPathIterator(AffineTransform.getTranslateInstance(location.x, location.y));
GeneralPath path = new GeneralPath();
path.append(pi, true);
return path;
}
}
}
You could also have a look at
Swing animation running extremely slow
Rotating multiple images causing flickering. Java Graphics2D
Java Bouncing Ball
for some more examples...
I am creating a moving ball program that features a ball moving and bouncing off the walls of a rectangle with go and stop buttons at the bottom.
What I am having problems with is I want the ball to start off moving when the program is run and bouncing off the lines inside the rectangle which is my main problem. Below is my code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
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.Timer;
//Does the drawing
class MyDrawing extends JPanel {
private int xpos;
private int ypos;
public void setXPos(final int x) {
this.xpos = x;
}
public void setYPos(final int y) {
this.ypos = y;
}
public int getXpos() {
return xpos;
}
public int getYpos() {
return ypos;
}
#Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g;
g.setColor(Color.red);
final Ellipse2D.Double circle = new Ellipse2D.Double(xpos, ypos, 50, 50);
g2.draw(circle);
g2.fill(circle);
final Rectangle box1 = new Rectangle(10, 10, 380, 300);
g.setColor(Color.BLACK);
g2.draw(box1);
}
}
public class ControlledBall extends JFrame {
private final JButton flash = new JButton("Go");
private final JButton steady = new JButton("Stop");
private final JPanel panel = new JPanel(new GridBagLayout());
private final MyDrawing drawing = new MyDrawing();
private final Timer timer;
//direction
private int dx = 3;
private int dy = 2;
public ControlledBall() {
panel.add(flash);
panel.add(steady);
this.add(panel, BorderLayout.SOUTH);
this.add(drawing, BorderLayout.CENTER);
drawing.setXPos(300);
drawing.setYPos(150);
steady.addActionListener(new SteadyListener());
final MoveListener ml = new MoveListener();
flash.addActionListener(ml);
timer = new Timer(15, ml);
}
class MoveListener implements ActionListener {
#Override
public void actionPerformed(final ActionEvent event) {
if (!timer.isRunning()){
timer.start();
}
move();
}
}
class SteadyListener implements ActionListener {
#Override
public void actionPerformed(final ActionEvent event) {
if (timer.isRunning()){
timer.stop();
}
}
}
private void move() {
int x = drawing.getXpos();
int y = drawing.getYpos();
final int dia = 30;
if (x + dx < 0 || x + dia + dx > getWidth()) {
dx *= -1;
}
if (y + dy < 0 || y + dia + dy > getHeight()) {
dy *= -1;
}
x += dx;
y += dy;
drawing.setXPos(x);
drawing.setYPos(y);
repaint();
}
public static void main(final String[] args) {
final JFrame window = new ControlledBall();
window.setSize(400, 400);
window.setTitle("Controlled Ball");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
}
The problem is the boundaries you check against. If you want to check against the Rectangle you have to implement the size as variables and enter them in your "bounce"-check or you add it manually like (take the size of the rectangle from your code):
if (x + dx < 10 || x + dia + dx > 380) {
dx *= -1;
}
if (y + dy < 10 || y + dia + dy > 300) {
dy *= -1;
}
If you like to take the real distanz use an offset of the size of hallf of the ball and add it to this code. i think this is better than the speed-vector dxand dyonly.