Setting a timer to control when the random variable is executed - java

I am trying to set a timer class to control when the ball chooses a new color. I need to make the ball change colors at a set time and not just continuously set different colors. This is my ball class and the whole program is run through a start class.
public void go() {
if (dx >= 0) {
dx = 20;
}
}
public void update(Start sp) {
if (x + dx > sp.getWidth() - radius * 2) {
x = sp.getWidth() - radius * 2;
dx = -dx;
}
else if (x + dx < 0) {
dx = -dx;
}
else {
x += dx;
}
}
public void paint(Graphics g) {
Random set = new Random();
int num1;
num1 = set.nextInt(4);
if (num1 == 0) {
g.setColor(Color.blue);
g.fillOval(x, y, radius * 2, radius * 2);
}
if (num1 == 1) {
g.setColor(Color.green);
g.fillOval(x, y, radius * 2, radius * 2);
}
if (num1 == 2) {
g.setColor(Color.white);
g.fillOval(x, y, radius * 2, radius * 2);
}
if (num1 == 3) {
g.setColor(Color.magenta);
g.fillOval(x, y, radius * 2, radius * 2);
}
}

Declare this field in your class, outside of a method.
Timer timer = new Timer();
Declare this method.
public void setTimerToChangeColors() {
timer.schedule(new TimerTask() {
#Override
public void run() {
// Whatever you want your ball to do to changes its colors
setTimerToChangeColors();
}
}, 10*1000); // 10 seconds * 1000ms per second
}
You only need to call setTimerToChangeColors once. It will continue restarting itself every 10 seconds. You can fill in the code for what you want to do when the task is triggered. If you want random time, you will have to edit where the timer is scheduled 10*1000 to a random generator.

Related

How can I make two objects of the same class bounce when colliding?

I made a bouncing ball code, and so far as the bouncing itself the code works perfectly. I then created a second ball, and it also does what it's supposed to do. However, when I try to use an if condition to make the two balls bounce off one another as well as the edges, it doesn't work. Either they don't move or they just don't collide, and go through each other. This code was made in processing. Can anyone help me make ball1 and ball2 collide?
Moving ball1;
Moving ball2;
void setup(){
size(600,600);
ball1 = new Moving();
ball2 = new Moving();
}
void draw(){
background(255);
ball1.move();
ball1.display();
ball1.bounce();
ball2.move();
ball2.display();
ball2.bounce();
ball1.clash();
ball2.clash();
}
class Moving {
float speed = 7;
float x = random(0, width);
float y= random(0, height);
float xdirection = 1;
float ydirection = 1;
float ball_size = 50;
float radius = ball_size/2;
Moving() {
}
void move() {
x = x + (xdirection * speed);
y = y + (ydirection* speed);
}
void display() {
noStroke();
fill(50, 0, 50);
circle(x, y, ball_size);
}
void bounce() {
if ((x >= width - radius) || (x <= radius)) {
xdirection = xdirection * -1;
}
if ((y >= height - radius)|| (y<=radius)) {
ydirection = ydirection * -1;
}
}
void clash() {
if ((ball1.y+radius == ball2.y+radius) && (ball1.x+radius == ball2.x+radius)) {
ball1.ydirection = ball1.ydirection * -1;
ball2.ydirection = ball2.ydirection * -1;
ball1.xdirection = ball1.xdirection * -1;
ball2.xdirection = ball2.xdirection * -1;
x = x + (xdirection * speed);
y = y + (ydirection* speed);
if (ball1.x+radius == ball2.x+radius) {
xdirection = xdirection * -1;
}
}
}
}

java bounceBall mouse escape

I have a problem.
I am a beginner with java, and succeeded up to this point. Add bubbles with random sizes.
Now I need to make the bubbles escaping mouse when he gets near them.
Can anyone give me a hint how?
Thank you.
public class BounceBall extends JFrame {
private ShapePanel drawPanel;
private Vector<NewBall> Balls;
private JTextField message;
// set up interface
public BounceBall() {
super("MultiThreading");
drawPanel = new ShapePanel(400, 345);
message = new JTextField();
message.setEditable(false);
Balls = new Vector<NewBall>();
add(drawPanel, BorderLayout.NORTH);
add(message, BorderLayout.SOUTH);
setSize(400, 400);
setVisible(true);
}
public static void main(String args[]) {
BounceBall application = new BounceBall();
application.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
private class NewBall extends Thread {
private Ellipse2D.Double thisBall;
private boolean ballStarted;
private int size, speed; // characteristics
private int deltax, deltay; // of the ball
public NewBall() {
ballStarted = true;
size = 10 + (int) (Math.random() * 60);
speed = 10 + (int) (Math.random() * 100);
int startx = (int) (Math.random() * 300);
int starty = (int) (Math.random() * 300);
deltax = -10 + (int) (Math.random() * 21);
deltay = -10 + (int) (Math.random() * 21);
if ((deltax == 0) && (deltay == 0)) {
deltax = 1;
}
thisBall = new Ellipse2D.Double(startx, starty, size, size);
}
public void draw(Graphics2D g2d) {
if (thisBall != null) {
g2d.setColor(Color.BLUE);
g2d.fill(thisBall);
}
}
public void run() {
while (ballStarted) // Keeps ball moving
{
try {
Thread.sleep(speed);
} catch (InterruptedException e) {
System.out.println("Woke up prematurely");
}
// calculate new position and move ball
int oldx = (int) thisBall.getX();
int oldy = (int) thisBall.getY();
int newx = oldx + deltax;
if (newx + size > drawPanel.getWidth() || newx < 0) {
deltax = -deltax;
}
int newy = oldy + deltay;
if (newy + size > drawPanel.getHeight() || newy < 0) {
deltay = -deltay;
}
thisBall.setFrame(newx, newy, size, size);
drawPanel.repaint();
}
}
}
private class ShapePanel extends JPanel {
private int prefwid, prefht;
public ShapePanel(int pwid, int pht) {
prefwid = pwid;
prefht = pht;
// add ball when mouse is clicked
addMouseListener(
new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
NewBall nextBall = new NewBall();
Balls.addElement(nextBall);
nextBall.start();
message.setText("Number of Balls: " + Balls.size());
}
});
}
public Dimension getPreferredSize() {
return new Dimension(prefwid, prefht);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (int i = 0; i < Balls.size(); i++) {
(Balls.elementAt(i)).draw(g2d);
}
}
}
}
You should not have a Thread for each individual ball, this will not scale well, the more balls you add, the more threads you add. At some point, the amount of work it takes to manage the threads will exceed the benefit for using multiple threads...
Also, I doubt if your need 1000fps...something like 25fps should be more than sufficient for your simple purposes. This will give the system some breathing room and allow other threads within the system time to execute.
Lets start with a simple concept of a Ball. The Ball knows where it is and which direction it is moving it, it also knows how to paint itself, for example...
public class Ball {
private int x;
private int y;
private int deltaX;
private int deltaY;
private int dimeter;
private Ellipse2D ball;
private Color color;
public Ball(Color color, Dimension bounds) {
this.color = color;
Random rnd = new Random();
dimeter = 5 + rnd.nextInt(15);
x = rnd.nextInt(bounds.width - dimeter);
y = rnd.nextInt(bounds.height - dimeter);
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
int maxSpeed = 10;
do {
deltaX = rnd.nextInt(maxSpeed) - (maxSpeed / 2);
} while (deltaX == 0);
do {
deltaY = rnd.nextInt(maxSpeed) - (maxSpeed / 2);
} while (deltaY == 0);
ball = new Ellipse2D.Float(0, 0, dimeter, dimeter);
}
public void update(Dimension bounds) {
x += deltaX;
y += deltaY;
if (x < 0) {
x = 0;
deltaX *= -1;
} else if (x + dimeter > bounds.width) {
x = bounds.width - dimeter;
deltaX *= -1;
}
if (y < 0) {
y = 0;
deltaY *= -1;
} else if (y + dimeter > bounds.height) {
y = bounds.height - dimeter;
deltaY *= -1;
}
}
public void paint(Graphics2D g2d) {
g2d.translate(x, y);
g2d.setColor(color);
g2d.fill(ball);
g2d.translate(-x, -y);
}
}
Next, we need somewhere for the balls to move within, some kind of BallPit for example...
public class BallPit extends JPanel {
private List<Ball> balls;
public BallPit() {
balls = new ArrayList<>(25);
balls.add(new Ball(Color.RED, getPreferredSize()));
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Ball ball : balls) {
ball.update(getSize());
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
for (Ball ball : balls) {
ball.paint(g2d);
}
g2d.dispose();
}
}
This maintains a list of balls, tells them when the need to update and when the need to paint. This example uses a simple javax.swing.Timer, which acts as the central timer which updates the balls and schedules the repaints.
The reason for this is takes care of synchronisation between the updates and the paints, meaning that the balls won't be updating while they are been painted. This is achieved because javax.swing.Timer triggers it's callbacks within the context of the EDT.
See Concurrency in Swing and How to use Swing Timers for more details.
Okay, so that fixes the threading issues, but what about the mouse avoidance...
That's a little more complicated...
What we need to is add a MouseMoitionListener to the BillPit and record the last position of the mouse.
public class BallPit extends JPanel {
//...
private Point mousePoint;
//...
public BallPit() {
//...
MouseAdapter handler = new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
mousePoint = e.getPoint();
}
#Override
public void mouseExited(MouseEvent e) {
mousePoint = null;
}
};
addMouseListener(handler);
addMouseMotionListener(handler);
//...
The reason for including mouseExit is to ensure that balls don't try and move away from a phantom mouse cursor...
Next, we need to update Ball to have an "area of effect", this is the area around the ball that will trigger a change in movement if the mouse cursor moves within it's range...
public class Ball {
//...
private final Ellipse2D.Float areaOfEffect;
public Ball(Color color, Dimension bounds) {
//...
areaOfEffect = new Ellipse2D.Float(-10, -10, dimeter + 20, dimeter + 20);
}
Now, I also add some additional painting for debug reasons...
public void paint(Graphics2D g2d) {
g2d.translate(x, y);
g2d.setColor(new Color(0, 0, 192, 32));
g2d.fill(areaOfEffect);
g2d.setColor(color);
g2d.fill(ball);
g2d.translate(-x, -y);
}
Next, we need to modify the Ball's update method to accept the mousePoint value...
public void update(Dimension bounds, Point mousePoint) {
PathIterator pathIterator = areaOfEffect.getPathIterator(AffineTransform.getTranslateInstance(x, y));
GeneralPath path = new GeneralPath();
path.append(pathIterator, true);
if (mousePoint != null && path.contains(mousePoint)) {
// Determine which axis is closes to the cursor...
int xDistance = Math.abs(x + (dimeter / 2) - mousePoint.x);
int yDistance = Math.abs(y + (dimeter / 2) - mousePoint.y);
if (xDistance < yDistance) {
// If x is closer, the change the delatX
if (x + (dimeter / 2) < mousePoint.x) {
if (deltaX > 0) {
deltaX *= -1;
}
} else {
if (deltaX > 0) {
deltaX *= -1;
}
}
} else {
// If y is closer, the change the deltaY
if (y + (dimeter / 2) < mousePoint.y) {
if (deltaY > 0) {
deltaY *= -1;
}
} else {
if (deltaY > 0) {
deltaY *= -1;
}
}
}
}
//...Rest of previous method code...
}
Basically, what this is trying to do is determine which axis is closer to the mouse point and in which direction the ball should try and move...it's a little "basic", but gives the basic premise...
Lastly, we need to update the "update" loop in the javax.swing.Timer to supply the additional parameter
for (Ball ball : balls) {
ball.update(getSize(), mousePoint);
}
I'm going to answer this, but I'm very close to issuing a close vote because it doesn't show what you've done so far to attempt this. I would not be surprised if others are closer to the edge than I am on this. At the same time, you've clearly shown your progress before you reached this point, so I'll give you the benefit of the doubt. In the future, I would strongly advise making an attempt and then posting a question that pertains to the specific problem you're having while making that attempt.
You need two things:
The current location of the mouse
A range check and reversal of direction if too close.
The location of the mouse can be achieved by adding two variables (x and y) and, every time the mouse is moved (so add a mouse event listener to your JPanel or something) update those variables with the new location.
Then, you can do a range check (think Pythagorean theorem) on each bubble to make sure they're far enough away. If the bubble is too close, you'll want to check where that bubble would end up if it carried on its current course, as well as where it would end up if it changed X direction, Y direction, or both. Pick the one that ends up being furthest away and set the deltax and deltay to those, and let the calculation carry on as normal.
It sounds like a lot, but those are the two basic components you need to achieve this.

Java Animations

I've started to take interest with making animations(slideshows, backgrounds etc) in Java. I know that JavaFX is much better for doing this, but I'm just to stubborn to bother switching over.
Here is what I got so far.
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BlurredLightCells extends JPanel {
private static final long serialVersionUID = 4610174943257637060L;
private Random random = new Random();
private ArrayList<LightCell> lightcells;
private float[] blurData = new float[500];
public static void main(String[] args) {
JFrame frame = new JFrame("Swing animated bubbles");
frame.setSize(1000, 750);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BlurredLightCells(60));
frame.setVisible(true);
}
public BlurredLightCells(int amtOfBCells) {
setSize(1000, 750);
/**
* Below we initiate all the cells that are going to be drawn on screen
*/
Arrays.fill(blurData, 1f / 20f);
lightcells = new ArrayList<LightCell>(amtOfBCells);
for (int i = 0; i < amtOfBCells; i++) {
/**
* Below we generate all the values for each cell(SHOULD be random for each one)
*/
int baseSpeed = random(0, 3);
int xSpeed = (int) Math.floor((Math.random() * (baseSpeed - -baseSpeed + baseSpeed)) + -baseSpeed);
int ySpeed = (int) Math.round((Math.random() * baseSpeed) + 0.5);
int radius = random(25, 100);
int x = (int) Math.floor(Math.random() * getWidth());
int y = (int) Math.floor(Math.random() * getHeight());
int blurrAmount = (int) (Math.floor(Math.random() * 10) + 5);
int alpha = (int) ((Math.random() * 15) + 3);
/**
* Now we draw a image, and apply transparency and a slight blur to it
*/
Kernel kernel = new Kernel(blurrAmount, blurrAmount, blurData);
BufferedImageOp op = new ConvolveOp(kernel);
BufferedImage circle = new BufferedImage(150, 150, BufferedImage.TYPE_INT_ARGB);
Graphics2D circlegfx = circle.createGraphics();
circlegfx.setColor(new Color(255, 255, 255, alpha));
circlegfx.fillOval(20, 20, radius, radius);
circle = op.filter(circle, null);
LightCell bubble = new LightCell(x, y, xSpeed, ySpeed, radius, getDirection(random.nextInt(3)), circle);
lightcells.add(bubble);
}
}
public int random(int min, int max) {
final int n = Math.abs(max - min);
return Math.min(min, max) + (n == 0 ? 0 : random.nextInt(n));
}
#Override
public void paint(Graphics g) {
int w = getWidth();
int h = getHeight();
final Graphics2D g2 = (Graphics2D) g;
GradientPaint gp = new GradientPaint(-w, -h, Color.LIGHT_GRAY, w, h, Color.DARK_GRAY);
g2.setPaint(gp);
g2.fillRect(0, 0, w, h);
long start = System.currentTimeMillis();
for (int i = 0; i < lightcells.size(); i++) {
LightCell cell = lightcells.get(i);
cell.process(g2);
}
System.out.println("Took " + (System.currentTimeMillis() - start) + " milliseconds to draw ALL cells.");
repaint();
}
public String getDirection(int i) {
switch (i) {
case 0:
return "right";
case 1:
return "left";
case 2:
return "up";
case 3:
return "down";
}
return "";
}
private class LightCell {
private int x, y, xSpeed, ySpeed, radius;
private String direction;
private BufferedImage image;
public LightCell(int x, int y, int xSpeed, int ySpeed, int radius, String direction, BufferedImage image) {
this.x = x;
this.y = y;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.radius = radius;
this.direction = direction;
this.image = image;
}
public void process(Graphics g) {
switch (direction) {
case "right":
moveRight();
break;
case "left":
moveLeft();
break;
case "up":
moveUp();
break;
case "down":
moveDown();
break;
}
g.drawImage(image, x, y, null);
}
private void moveUp() {
x += xSpeed;
y -= ySpeed;
if (y + (radius / 2) < 0) {
y = getHeight() + (radius / 2);
x = (int) Math.floor(Math.random() * getWidth());
}
if ((x + radius / 2) < 0 || (x - radius / 2) > getWidth()) {
y = radius + (radius / 2);
x = (int) Math.floor(Math.random() * getWidth());
}
}
private void moveDown() {
x += xSpeed;
y += ySpeed;
if (y - (radius / 2) > getHeight()) {
y = 0 - (radius / 2);
x = (int) Math.floor(Math.random() * getWidth());
}
if ((x + radius / 2) < 0 || (x - radius / 2) > getWidth()) {
y = getHeight() + (radius / 2);
x = (int) Math.floor(Math.random() * getWidth());
}
}
private void moveRight() {
x += ySpeed;
y += xSpeed;
if (y - (radius / 2) > getHeight() || y + (radius / 2) < 0) {
x = 0 - (radius / 2);
y = (int) Math.floor(Math.random() * getHeight());
}
if ((x - radius / 2) > getWidth()) {
x = 0 - (radius / 2);
y = (int) Math.floor(Math.random() * getWidth());
}
}
private void moveLeft() {
x -= ySpeed;
y -= xSpeed;
if (y - (radius / 2) > getHeight() || y + (radius / 2) < 0) {
x = getWidth() + (radius / 2);
y = (int) Math.floor(Math.random() * getHeight());
}
if ((x + radius / 2) < 0) {
x = getWidth() + (radius / 2);
y = (int) Math.floor(Math.random() * getWidth());
}
}
}
}
If you run that, you will see the cells move at a very high speed, and if you look through the code, you see I call repaint() in the paint method in which I override. I know thats not good to do. But my question is, is their any other way in which I could draw each cell outside of the repaint() loop I have right now, because that causes other components to flash/flicker when I use this in a JFrame with other components.
FYI: Aventually I'd like to achieve something similar to this: Click Here
Thanks!
The issue of flicker is to do with the fact that top level containers are not double buffered. Instead of extending from JFrame (or other top level containers), you should consider using something more like JPanel and override it's paintComponent.
nb- Had it in my head that the OP was extending from JFrame...
Two issues could be causing the flickering. The first is overriding paint, the second is not calling super.paint(g) and the time between the updates. A better solution would be to override paintComponent and make sure you are calling super.paintComponent. Also using something like a javax.swing.Timer to schedule updates and regular intervals would also help...
Only call repaint when you want to encourage the RepaintManager to update you component. Don't call repaint from within any paintXxx method, this will cause a never ending loop of paint requests to schedule onto the event queue, eventually consuiming your CPU
I would avoid doing anything in your paintXxx methods that might take time to perform, this will slow down the rendering process. Instead, I would use a javax.swing.Timer for simple updates or for more complicated processing, a Thread which could be used to update the model before it is rendered to the screen.
This is an example of some simple optimisation process I did to take animation of 500 objects to 4500 with only a slight degration in the overall performance.
Updated
I changed you code slight and it works fine...
I changed your paint method to paintComponent and added super.paintComponent(g)
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int w = getWidth();
int h = getHeight();
final Graphics2D g2 = (Graphics2D) g;
GradientPaint gp = new GradientPaint(-w, -h, Color.LIGHT_GRAY, w, h, Color.DARK_GRAY);
g2.setPaint(gp);
g2.fillRect(0, 0, w, h);
for (int i = 0; i < lightcells.size(); i++) {
LightCell cell = lightcells.get(i);
cell.process(g2);
}
}
And at the end of your constructor I added...
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
To update the UI on a regular bases...

Why is java application running smoother when moving mouse over it? Video included

I'm working with tutorial from this site - "Fixed timestep" section.
Here's the code - http://pastebin.com/QaHgcLaR
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class GameLoopTest extends JFrame implements ActionListener
{
private GamePanel gamePanel = new GamePanel();
private JButton startButton = new JButton("Start");
private JButton quitButton = new JButton("Quit");
private JButton pauseButton = new JButton("Pause");
private boolean running = false;
private boolean paused = false;
private int fps = 60;
private int frameCount = 0;
public GameLoopTest()
{
super("Fixed Timestep Game Loop Test");
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
JPanel p = new JPanel();
p.setLayout(new GridLayout(1,2));
p.add(startButton);
p.add(pauseButton);
p.add(quitButton);
cp.add(gamePanel, BorderLayout.CENTER);
cp.add(p, BorderLayout.SOUTH);
setSize(500, 500);
startButton.addActionListener(this);
quitButton.addActionListener(this);
pauseButton.addActionListener(this);
}
public static void main(String[] args)
{
GameLoopTest glt = new GameLoopTest();
glt.setVisible(true);
}
public void actionPerformed(ActionEvent e)
{
Object s = e.getSource();
if (s == startButton)
{
running = !running;
if (running)
{
startButton.setText("Stop");
runGameLoop();
}
else
{
startButton.setText("Start");
}
}
else if (s == pauseButton)
{
paused = !paused;
if (paused)
{
pauseButton.setText("Unpause");
}
else
{
pauseButton.setText("Pause");
}
}
else if (s == quitButton)
{
System.exit(0);
}
}
//Starts a new thread and runs the game loop in it.
public void runGameLoop()
{
Thread loop = new Thread()
{
public void run()
{
gameLoop();
}
};
loop.start();
}
//Only run this in another Thread!
private void gameLoop()
{
//This value would probably be stored elsewhere.
final double GAME_HERTZ = 30.0;
//Calculate how many ns each frame should take for our target game hertz.
final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ;
//At the very most we will update the game this many times before a new render.
//If you're worried about visual hitches more than perfect timing, set this to 1.
final int MAX_UPDATES_BEFORE_RENDER = 5;
//We will need the last update time.
double lastUpdateTime = System.nanoTime();
//Store the last time we rendered.
double lastRenderTime = System.nanoTime();
//If we are able to get as high as this FPS, don't render again.
final double TARGET_FPS = 60;
final double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;
//Simple way of finding FPS.
int lastSecondTime = (int) (lastUpdateTime / 1000000000);
while (running)
{
double now = System.nanoTime();
int updateCount = 0;
if (!paused)
{
//Do as many game updates as we need to, potentially playing catchup.
while( now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER )
{
updateGame();
lastUpdateTime += TIME_BETWEEN_UPDATES;
updateCount++;
}
//If for some reason an update takes forever, we don't want to do an insane number of catchups.
//If you were doing some sort of game that needed to keep EXACT time, you would get rid of this.
if ( now - lastUpdateTime > TIME_BETWEEN_UPDATES)
{
lastUpdateTime = now - TIME_BETWEEN_UPDATES;
}
//Render. To do so, we need to calculate interpolation for a smooth render.
float interpolation = Math.min(1.0f, (float) ((now - lastUpdateTime) / TIME_BETWEEN_UPDATES) );
drawGame(interpolation);
lastRenderTime = now;
//Update the frames we got.
int thisSecond = (int) (lastUpdateTime / 1000000000);
if (thisSecond > lastSecondTime)
{
System.out.println("NEW SECOND " + thisSecond + " " + frameCount);
fps = frameCount;
frameCount = 0;
lastSecondTime = thisSecond;
}
//Yield until it has been at least the target time between renders. This saves the CPU from hogging.
while ( now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES)
{
Thread.yield();
//This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it.
//You can remove this line and it will still work (better), your CPU just climbs on certain OSes.
//FYI on some OS's this can cause pretty bad stuttering. Scroll down and have a look at different peoples' solutions to this.
try {Thread.sleep(1);} catch(Exception e) {}
now = System.nanoTime();
}
}
}
}
private void updateGame()
{
gamePanel.update();
}
private void drawGame(float interpolation)
{
gamePanel.setInterpolation(interpolation);
gamePanel.repaint();
}
private class GamePanel extends JPanel
{
float interpolation;
float ballX, ballY, lastBallX, lastBallY;
int ballWidth, ballHeight;
float ballXVel, ballYVel;
float ballSpeed;
int lastDrawX, lastDrawY;
public GamePanel()
{
ballX = lastBallX = 100;
ballY = lastBallY = 100;
ballWidth = 25;
ballHeight = 25;
ballSpeed = 25;
ballXVel = (float) Math.random() * ballSpeed*2 - ballSpeed;
ballYVel = (float) Math.random() * ballSpeed*2 - ballSpeed;
}
public void setInterpolation(float interp)
{
interpolation = interp;
}
public void update()
{
lastBallX = ballX;
lastBallY = ballY;
ballX += ballXVel;
ballY += ballYVel;
if (ballX + ballWidth/2 >= getWidth())
{
ballXVel *= -1;
ballX = getWidth() - ballWidth/2;
ballYVel = (float) Math.random() * ballSpeed*2 - ballSpeed;
}
else if (ballX - ballWidth/2 <= 0)
{
ballXVel *= -1;
ballX = ballWidth/2;
}
if (ballY + ballHeight/2 >= getHeight())
{
ballYVel *= -1;
ballY = getHeight() - ballHeight/2;
ballXVel = (float) Math.random() * ballSpeed*2 - ballSpeed;
}
else if (ballY - ballHeight/2 <= 0)
{
ballYVel *= -1;
ballY = ballHeight/2;
}
}
public void paintComponent(Graphics g)
{
//BS way of clearing out the old rectangle to save CPU.
g.setColor(getBackground());
g.fillRect(lastDrawX-1, lastDrawY-1, ballWidth+2, ballHeight+2);
g.fillRect(5, 0, 75, 30);
g.setColor(Color.RED);
int drawX = (int) ((ballX - lastBallX) * interpolation + lastBallX - ballWidth/2);
int drawY = (int) ((ballY - lastBallY) * interpolation + lastBallY - ballHeight/2);
g.fillOval(drawX, drawY, ballWidth, ballHeight);
lastDrawX = drawX;
lastDrawY = drawY;
g.setColor(Color.BLACK);
g.drawString("FPS: " + fps, 5, 10);
frameCount++;
}
}
private class Ball
{
float x, y, lastX, lastY;
int width, height;
float xVelocity, yVelocity;
float speed;
public Ball()
{
width = (int) (Math.random() * 50 + 10);
height = (int) (Math.random() * 50 + 10);
x = (float) (Math.random() * (gamePanel.getWidth() - width) + width/2);
y = (float) (Math.random() * (gamePanel.getHeight() - height) + height/2);
lastX = x;
lastY = y;
xVelocity = (float) Math.random() * speed*2 - speed;
yVelocity = (float) Math.random() * speed*2 - speed;
}
public void update()
{
lastX = x;
lastY = y;
x += xVelocity;
y += yVelocity;
if (x + width/2 >= gamePanel.getWidth())
{
xVelocity *= -1;
x = gamePanel.getWidth() - width/2;
yVelocity = (float) Math.random() * speed*2 - speed;
}
else if (x - width/2 <= 0)
{
xVelocity *= -1;
x = width/2;
}
if (y + height/2 >= gamePanel.getHeight())
{
yVelocity *= -1;
y = gamePanel.getHeight() - height/2;
xVelocity = (float) Math.random() * speed*2 - speed;
}
else if (y - height/2 <= 0)
{
yVelocity *= -1;
y = height/2;
}
}
public void draw(Graphics g)
{
}
}
}
After run this code, the ball has kind of lag, but there is still 60 FPS. After I move mouse over application's window and move it in random directions, the ball is moving smoothly. It happens even if window application isn't focused! What's wrong? Can it be fixed?
I'm using Ubuntu 13.04 with Oracle JDK7.
I've found that it happens with every application. Similar things happens even in LWJGL application, but effect of the "lag" is much less than in swing application.
17 sec video showing my problem
http://www.youtube.com/watch?v=J8SBjKncgRw
I had same problem under Kubuntu 13.04
I googled something which works for me: http://www.java-gaming.org/index.php?topic=19224.0
The basic idea is to put Toolkit.getDefaultToolkit().sync(); after drawing something. In your code it should be after drawGame(interpolation);.
The explanation seems to be that the window system manages the update intervals, so it is not Java's fault and only occures with some window mangers.
The repaints should be triggered from a Swing based Timer, which ensures that GUI updates are called on the EDT. See Concurrency in Swing for more details.
This appears to be a bug in the VM since Java 6. I had the same problem and found an ugly, but simple workaround: Create a Robot object and let it press a key or position the mouse in each cycle. The animation will then run smoothly. Example:
final Robot robot = new Robot();
javax.swing.Timer timer = new javax.swing.Timer(initialDelay, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
// update your image...
robot.keyPress(62);
}
});
See also: Java animation stutters when not moving mouse cursor
You could invoke setIgnoreRepaint(true) and use a BufferStrategy for drawing that particular component instead. A BufferStrategy allows you to perform the drawing whenever you want. You can also invoke paintComponent methods inside your drawing method.

Java Thread handling

I am trying to make a stoplight that performs certain tasks after a button is clicked. What this stoplight is supposed to do is change from green to yellow after 50 secs, from yellow to red after 10 secs, and from red to green after 60 secs (this part I have working fine), and if the button is pressed when it is green it should change to yellow, this should only work after 10 secs have at least passed while green. What I have a problem is how do I check if 10 secs have passed or not?
public class Stoplight extends Applet
{
Button cross;
public void init(){
cross = new Button("Cross");
add(cross);
StoplightCanvas stoplightCanvas = new StoplightCanvas(cross);
add(stoplightCanvas);
new StoplightThread(stoplightCanvas).start();
}
}
class StoplightCanvas extends Canvas implements ActionListener
{
int Xpos;
int Ypos;
int diameter;
Button cross;
int x = 1;
StoplightCanvas(Button cross)
{
this.cross = cross;
cross.addActionListener(this);
setSize(300, 600);
}
public void paint(Graphics g)
{
diameter = 70;
Xpos = 70;
Ypos = 50;
g.setColor(Color.BLUE);
g.fillRect(70, 50, 74, 220);
g.setColor(Color.WHITE);
if (x == 1)
g.setColor(Color.RED);
drawCircles(g, Xpos, Ypos);
g.setColor(Color.WHITE);
if (x == 2)
g.setColor(Color.YELLOW);
drawCircles(g, Xpos, Ypos + diameter);
g.setColor(Color.WHITE);
if (x == 3)
g.setColor(Color.GREEN);
drawCircles(g, Xpos, Ypos + diameter * 2);
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == cross) {
}
repaint();
}
void drawCircles(Graphics g, int x, int y)
{
g.fillOval(x, y, diameter, diameter);
}
public void toggleColor() {
if (x == 1)
x = 3;
else if (x == 2)
x = 1;
else if (x == 3)
x = 2;
}
}
class StoplightThread extends Thread
{
StoplightCanvas stoplightCanvas;
StoplightThread(StoplightCanvas stoplightCanvas) {
this.stoplightCanvas = stoplightCanvas;
}
public void run()
{
while (true) {
try {
if (stoplightCanvas.x == 3){
Thread.sleep(50000);
} else if (stoplightCanvas.x == 2) {
Thread.sleep(10000);
} else if (stoplightCanvas.x == 1) {
Thread.sleep(60000);
}
} catch (InterruptedException e){}
stoplightCanvas.toggleColor();
stoplightCanvas.repaint();
}
}
}
You can set a timer when they press the button for 10 seconds. When that time expires, then change the color to yellow via the callback. It is much better than dealing with exceptions, because they should be for exceptional circumstances.
See this thread on how to set a timer for later.
Edit
The poster wishes to not use timers. One way would be to store the time when the button is pressed in a variable, then access that variable and compare against the current time within the while loop of the run method.

Categories

Resources