This is my Game Class:
package Game;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Game extends JPanel implements Runnable {
Thread thread;
private boolean running = true;
private double FPS = 1.0/60.0;
private Level level;
public Game(){
level = new LevelOne();
level.setBackground(Color.BLACK);
add(level);
}
public synchronized void start() {
thread = new Thread(this, "Game");
thread.start();
}
public synchronized void stop() {
running = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void run() {
double firstTime = 0;
double lastTime = System.nanoTime() / 1000000000.0;
double passedTime = 0;
double updateTime = 0;
double timer = System.nanoTime() / 1000000000.0;
int rendered = 0;
int updates = 0;
while (running) {
firstTime = System.nanoTime() / 1000000000.0;
passedTime = firstTime - lastTime;
lastTime = firstTime;
updateTime += passedTime;
while(updateTime > FPS){
updates++;
update();
updateTime -= FPS;
}
render();
rendered++;
if((System.nanoTime() / 1000000000) - timer > 1){
timer += 1;
System.out.println("FPS:"+rendered+" Updates:"+updates);
updates = 0;
rendered = 0;
}
}
}
private void update() {
}
private void render() {
this.repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
level.repaint();
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setLocationRelativeTo(null);
frame.setSize(300, 300);
frame.setVisible(true);
frame.setTitle("Game");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Game game = new Game();
frame.add(game);
game.start();
}
}
My LevelOne class:
package Game;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class LevelOne extends Level {
private BufferedImage spriteSheet;
private BufferedImage[] player = new BufferedImage[4]; //0 = down, 1 = right, 2 = up, 3 = left
private int dir = 0;
private String UP = "W";
private String DOWN = "S";
private String LEFT = "A";
private String RIGHT = "D";
private int speedX = 0;
private int speedY = 0;
public LevelOne(){
try {
spriteSheet = ImageIO.read(new File("images/sprites.png"));
} catch (IOException e) {
e.printStackTrace();
}
for (int i = 0; i < 4; i++) {
player[i] = spriteSheet.getSubimage((i * 18), 0, 18, 28);
}
this.addKeyListener(new KeyListener() {
#Override
public void keyPressed(KeyEvent e) {
String key = e.getKeyText(e.getKeyCode());
if (key.equals(UP)) {
dir = 2;
speedY = -5;
} else if (key.equals(LEFT)) {
dir = 3;
speedX = -5;
} else if (key.equals(RIGHT)) {
dir = 1;
speedX = 5;
} else if (key.equals(DOWN)) {
dir = 0;
speedY = 5;
}
}
#Override
public void keyReleased(KeyEvent e) {
String key = e.getKeyText(e.getKeyCode());
if (key.equals(UP)) {
speedY = 0;
} else if (key.equals(LEFT)) {
speedX = 0;
} else if (key.equals(RIGHT)) {
speedX = 0;
} else if (key.equals(DOWN)) {
speedY = 0;
}
}
#Override
public void keyTyped(KeyEvent e) {
}
});
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(player[dir], 0, 0, 18, 28, null);
}
}
LevelOne extends Level, which is currently empty(And extends JPanel), I'm not sure if I need to add anything to Level. I just dont get what I need to do to make the player image show up...
And sorry if my code is sloppy, I'm trying to get into higher level Java, and im not sure if I am just approaching it wrong.
Thanks.
Six things...
Don't call level.repaint from within the paintComponent method of Game, this could cause an infinite loop of repaint requests which is going to screw with your frame rate. Consider calling it within your render method
paintComponent should never public, there's no reason for anybody to ever call it directly.
Use key bindings over KeyListener, they will solve focus related issues. How to Use Key Bindings
Move frame.setVisible AFTER frame.add(game);, in fact, it really should the last thing you do
Make sure your images are loading properly
Set the layout manager for Game to BorderLayout
Related
I am writing a space invaders clone for a school project, I am in the process of writing the aliens and their algorithm for movement with the use of an array.
My issue is a bunch of errors occur when I run the code but I can`t find why?
any help would be appreciated and bare in mind I am very inexperienced with game development and java
bellow is my GamePanel class, and then my Alien class
package Main;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
import entity.Alien;
import entity.Controller;
import entity.Player;
import entity.playerBullet;
public class GamePanel extends JPanel implements Runnable{
public final int screenHeight = 600;
public final int screenWidth = 800;
public final int playerSize = 48;
int fps = 60;
KeyHandler keyH = new KeyHandler();
Thread gameThread;
public Player player = new Player(this,keyH);
public CollisionChecker cChecker = new CollisionChecker(this);
private Controller c = new Controller(null);
Alien alien;
//player default position
int playerX = 100;
int playerY = 100;
int playerSpeed = 4;
public GamePanel() {
this.setPreferredSize(new Dimension (screenWidth, screenHeight));
this.setBackground(Color.black);
this.setDoubleBuffered(true);
this.addKeyListener(keyH);
this.setFocusable(true);
}
public void startGameThread() {
gameThread = new Thread (this);
gameThread.start();
alien.initAlien();
}
public void run() {
double drawInterval = 1000000000/fps; // 0.01666 seconds = 60 times per seconds
double nextDrawTime = System.nanoTime() + drawInterval;
while(gameThread != null) {
System.out.println("this game is runing");
// update information such as character positions
// draw the screen with the updated information
update();
repaint();
try {
double remainingTime = nextDrawTime - System.nanoTime();
remainingTime = remainingTime/1000000;
if(remainingTime<0) {
remainingTime = 0;
}
Thread.sleep ((long) remainingTime);
nextDrawTime += drawInterval;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void update() {
player.update();
c.tick();
alien.moveAliens();
}
public void paintComponent(Graphics g) {
// to draw something
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
player.draw(g2);
c.render(g );
g2.dispose();
alien.render(g );
}
}
Alien class
package entity;
import java.awt.Color;
import java.awt.Graphics;
public class Alien {
boolean isVisible;
boolean moveLeft;
boolean moveRight;
Alien[] a = new Alien[10];
private int x;
private int y;
int ax = 10;
int ay = 10;
public Alien(int x, int y) {
}
public void initAlien() {
for(int i=0;i<a.length; i++) {
a[i] = new Alien(ax,ay);
ax += 40;
if(i==4) {
ax=10;
ay+=40;
}
}
}
public void moveAliens() {
for(int i=0;i<a.length; i++) {
if (a[i].moveLeft ==true) {
a[i].x -=2;
}
if (a[i].moveRight ==true) {
a[i].x+=2;
}
for(int i1 = 0; i<a.length; ) {
if(a[i].x>600) {
for(int j =0;j<a.length; j++) {
a[j].moveLeft = true;
a[j].moveRight = false;
a[j].y += 5;
}
}
if(a[i].x<0) {
for(int j = 0; j< a.length; j++) {
a[j].moveLeft = false;
a[j].moveRight = true;
a[j].y += 5;
}
}
}
}
}
Before I start, here is my UML diagram:
I'm trying to animate my player's character, of a gif but split into 5 separate png files. I'd rather work when the 5 frames directly because it's not a detailed image, just an 8-bit character sprite. Also, don't know how to use many Photo editing software, so I'm using the separate files themselves.
I've tried quite a few examples off Stack Overflow and other tutorials but nothing seems to match what I'm trying to do on the framework I have to build for my game. It's for an end of semester project but I'm planning to build upon in after the due date and keep it open source.
Here is some of my code.
Driver. Loads the game loop and other functions.
package cactus;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
public class Driver extends Canvas implements Runnable{
private static final long serialVersionUID = 1L;
private Thread t;
private boolean running;
private Controller controller;
public Driver() {
controller = new Controller();
/*--- Add Game Objects ---------------------------------------*/
controller.addObject(new Travis(500, 675, ID.Travis));
/*------------------------------------------------------------*/
}
public synchronized void start() {
t = new Thread(this);
t.start();
running = true;
}
public synchronized void stop() {
try {
t.join();
running = false;
} catch (Exception e) {
e.printStackTrace();
}
}
public void run() {
this.requestFocus();
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
int frames = 0;
while(running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while(delta >= 1) {
tick();
delta--;
}
if(running) {
render();
}
frames++;
if(System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS:" + frames);
frames = 0;
}
}
stop();
}
private void tick() {
controller.tick();
}
private void render() {
BufferStrategy buffer = this.getBufferStrategy();
if (buffer == null) {
this.createBufferStrategy(3);
return;
}
Graphics g = buffer.getDrawGraphics();
g.drawImage(background.getCurrentBG(), 0, 0, Frame.getWidth(), Frame.getHeight(), null);
controller.render(g);
g.dispose();
buffer.show();
}
public static void main(String[] args) {
new Driver();
}
}
Controller. Loops through game objects to execute some of their render functions, which displays what they look like, and their tick functions which controls their locations and some future actions.
package cactus;
import java.awt.Graphics;
import java.util.LinkedList;
public class Controller {
LinkedList<Objects> gameObj = new LinkedList<Objects>();
public void tick() {
for (int i = 0; i < gameObj.size(); i++) {
Objects currentObject = gameObj.get(i);
if (currentObject.getId() == ID.ShootFire && currentObject.getX() > Frame.getWidth() + 30) {
removeObject(currentObject);
System.out.println("Fire Object Removed");
}
currentObject.tick();
}
}
public void render(Graphics g) {
for (int i = 0; i < gameObj.size(); i++) {
Objects currentObject = gameObj.get(i);
currentObject.render(g);
}
}
public void addObject(Objects o) {
this.gameObj.add(o);
}
public void removeObject(Objects o) {
this.gameObj.remove(o);
}
}
Player class. Gets methods from Abstract Objects class
package cactus;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
// import javax.swing.Timer;
public class Travis extends Objects implements ActionListener{
ArrayList<BufferedImage> animation;
// Timer animationTimer;
Graphics g;
// int i = 0;
public Travis(int x, int y, ID id) {
super(x, y, id);
this.animation = createAnimation();
// this.animationTimer = createTimer(animationTimer);
// animationTimer.start();
}
private synchronized ArrayList<BufferedImage> createAnimation() {
animation = new ArrayList<BufferedImage>();
for (int i = 0; i < 6; i++) {
try {
animation.add(ImageIO.read(new File("./src/resources/image/travis/frame_" + i + ".png")));
} catch (IOException e) {e.printStackTrace(); System.exit(0);}
}
return animation;
}
// private synchronized Timer createTimer(Timer animationTimer) {
// animationTimer = new Timer(15, this);
// animationTimer.setDelay(15*1000);
// return animationTimer;
// }
#Override
public void tick() {
x += velocityX;
}
/*
* drawImage(img, posX, posY, observe [null])
*
* (non-Javadoc)
* #see cactus.GameObjects#render(java.awt.Graphics)
*/
#Override
public void render(Graphics g) {
for (int i = 0; i < 6; i++) {
g.drawImage(animation.get(i), x, y, null);
}
if (i == 6)
i = 0;
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
I want each object of the ArrayList (although likely will change it to Map), to play with different delays between frames.
* frame_0: 1s
* frame_1: 2s
* frame_2: 2s
* frame_3: 1s
* frame_4: 2s
* frame_5: 5s
I've tried using Timer and some other things and I'm not able to animate it, it just displays the last frame.
I don't have enough rep to post photographs.
The simplest approach is to yield the rendering thread by sleeping it for a few milliseconds between frames:
private static final long PERIOD = 1000 / FRAMES_PER_SECOND;
...
long now = System.currentTimeMillis();
while(running) {
// Perform frame
...
// Wait for next frame
now += PERIOD;
final long duration = now - System.currentTimeMillis();
if(duration > 0) {
Thread.sleep(duration);
}
}
where FRAMES_PER_SECOND is the desired number of frames-per-second.
You could also add a counter to calculate the frame-rate if required.
There are lots of other (and better) ways of creating a render loop but this should do as a starter.
I'm pretty new to java coding, but i know the basic structures of how to code. I am not familiar with awt/swing, and don't know what my error is.
Background: I searched up ways to animate with java, found zetcode tutorials.
I copy/pasted their code, and tested the program, to make sure it works. the JFrame opened, but nothing drew. Maybe this is a system compatibility error? if so, how would I fix it?
these are the two separate classes:
package com.zetcode;
import java.awt.EventQueue;
import javax.swing.JFrame;
public class ThreadAnimationExample extends JFrame {
public ThreadAnimationExample() {
initUI();
}
private void initUI() {
add(new Board());
setResizable(false);
pack();
setTitle("Star");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame ex = new ThreadAnimationExample();
ex.setVisible(true);
}
});
}
}
This is the main class.
Board.java
package com.zetcode;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
public class Board extends JPanel
implements Runnable {
private final int B_WIDTH = 350;
private final int B_HEIGHT = 350;
private final int INITIAL_X = -40;
private final int INITIAL_Y = -40;
private final int DELAY = 25;
private Image star;
private Thread animator;
private int x, y;
public Board() {
initBoard();
}
private void loadImage() {
ImageIcon ii = new ImageIcon("star.png");
star = ii.getImage();
}
private void initBoard() {
setBackground(Color.BLACK);
setPreferredSize(new Dimension(B_WIDTH, B_HEIGHT));
setDoubleBuffered(true);
loadImage();
x = INITIAL_X;
y = INITIAL_Y;
}
#Override
public void addNotify() {
super.addNotify();
animator = new Thread(this);
animator.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawStar(g);
}
private void drawStar(Graphics g) {
g.drawImage(star, x, y, this);
Toolkit.getDefaultToolkit().sync();
}
private void cycle() {
x += 1;
y += 1;
if (y > B_HEIGHT) {
y = INITIAL_Y;
x = INITIAL_X;
}
}
#Override
public void run() {
long beforeTime, timeDiff, sleep;
beforeTime = System.currentTimeMillis();
while (true) {
cycle();
repaint();
timeDiff = System.currentTimeMillis() - beforeTime;
sleep = DELAY - timeDiff;
if (sleep < 0) {
sleep = 2;
}
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
System.out.println("Interrupted: " + e.getMessage());
}
beforeTime = System.currentTimeMillis();
}
}
}
When I run the program, I get a blank black window. The program is supposed to draw a star. Here is what it is supposed to look like.
I was trying to follow these tutorials http://zetcode.com/tutorials/javagamestutorial/animation/ and none of the three examples on that page seem to be working for me. One of them uses a swing timer, one uses the utility timer, and the last and supposedly most effective and accurate according to the page uses a thread to animate.
I will show you the one using the thread, since it is the way that I think I will be doing thing's when using animation for making games.
ThreadAnimationExample.java (in the tutorial it is called star.java but obviously that wont work)
import java.awt.EventQueue;
import javax.swing.JFrame;
public class ThreadAnimationExample extends JFrame {
public ThreadAnimationExample() {
add(new Board());
setTitle("Star");
pack();
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame ex = new ThreadAnimationExample();
ex.setVisible(true);
}
});
}
}
Board.java (the main class)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
public class Board extends JPanel
implements Runnable {
private final int B_WIDTH = 350;
private final int B_HEIGHT = 350;
private final int INITIAL_X = -40;
private final int INITIAL_Y = -40;
private final int DELAY = 25;
private Image star;
private Thread animator;
private int x, y;
public Board() {
loadImage();
initBoard();
}
private void loadImage() {
ImageIcon ii = new ImageIcon("star.png");
star = ii.getImage();
}
private void initBoard() {
setBackground(Color.BLACK);
setPreferredSize(new Dimension(B_WIDTH, B_HEIGHT));
setDoubleBuffered(true);
x = INITIAL_X;
y = INITIAL_Y;
}
#Override
public void addNotify() {
super.addNotify();
animator = new Thread(this);
animator.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawStar(g);
}
private void drawStar(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(star, x, y, this);
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
private void cycle() {
x += 1;
y += 1;
if (y > B_HEIGHT) {
y = INITIAL_Y;
x = INITIAL_X;
}
}
#Override
public void run() {
long beforeTime, timeDiff, sleep;
beforeTime = System.currentTimeMillis();
while (true) {
cycle();
repaint();
timeDiff = System.currentTimeMillis() - beforeTime;
sleep = DELAY - timeDiff;
if (sleep < 0) {
sleep = 2;
}
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
System.out.println("Interrupted: " + e.getMessage());
}
beforeTime = System.currentTimeMillis();
}
}
}
If you are using Eclipse you should create a source folder and add that image to the source folder. Then you could use this:
ImageIcon ii = new ImageIcon( getClass().getResource("/imageName.png") );
Consider the following Swing timer :
timer = new Timer (ballSpeed, tc);
ballSpeed is initially at 10. tc is an Action Listener class that increments the x value of an object painted on the screen. The variable ballSpeed is kind of a misnomer because the lower the value, the faster the object moves.
Now I want the object's movement to look as smooth as possible. Therefore I will only increment the x value one by one in the ActionListener. That is the object should only move move pixel by pixel. I use x++ instead of x+=10. Therefore I will not modify the ball's speed this way.
Now since the first argument of Timer will only accept an integer, it doesn't give me a great deal of control over the object's speed. I can only use 10,9,8,etc. The object either moves too fast or too slow.
To summarize, millisecond precision is not sufficient.
Is there a way around this? Or is there an overall better way to implement object movement on the screen?
A javax.swing.Timer does not have sufficient accuracy or precision to generate the smooth graphics you are trying to achieve. Furthermore, the swing timer is affected by anything else in the swing event queue:
Second, its automatic thread sharing means that you don't have to take special steps to avoid spawning too many threads. Instead, your timer uses the same thread used to make cursors blink, tool tips appear, and so on.
If you don't want to use some media framework or use APIs that describe the motion of objects (rather than actually moving the objects), you should use the swing timer as a way to schedule the next computation, but determine the time elapsed since last computation by looking at the difference between System.nanoTime() now and the nanoTime during the last computation.
Using this approach, you will have more jaggered but more correct animation on underpowered machines.
OK, so if you really want to go for the nanosecond, at the end of this answer is a way to do it using a ScheduledThreadPool. But this is just pure madness, this may lead to tons of problems and the result is disappointing. I would really not go down that road.
With 50Hz (ie, 50 refresh per second) you should be able to achieve a decent result. The thing is that I would rather drop the assumption that you can move only pixel per pixel and link your ball speed to your move increases.
Here is an example (just drag the slider to see the result):
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TestAnimation2 {
private static final int NB_OF_IMAGES_PER_SECOND = 50;
private static final int WIDTH = 800;
private static final int HEIGHT = 600;
private static final int MIN = 0;
private static final int MAX = 100;
private double speed = convert(50);
private double dx;
private double dy;
private double x = WIDTH / 2;
private double y = HEIGHT / 2;
private JFrame frame;
private CirclePanel circle;
private Runnable job;
private long lastMove = System.currentTimeMillis();
protected void initUI() throws MalformedURLException {
frame = new JFrame(TestAnimation2.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
circle = new CirclePanel();
circle.setSize(20, 20);
frame.add(circle);
frame.setSize(WIDTH, HEIGHT);
dx = speed;
dy = speed;
final JSlider slider = new JSlider(MIN, MAX);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
speed = convert(slider.getValue());
if (dx > 0) {
dx = speed;
} else {
dx = -speed;
}
if (dy > 0) {
dy = speed;
} else {
dy = -speed;
}
}
});
slider.setValue(50);
slider.setLocation(0, 0);
slider.setSize(slider.getPreferredSize());
frame.add(slider);
frame.setVisible(true);
Timer t = new Timer(1000 / NB_OF_IMAGES_PER_SECOND, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
move();
}
});
t.start();
}
protected double convert(double sliderValue) {
return sliderValue + 1;
}
protected void move() {
x += dx;
y += dy;
if (x + circle.getWidth() > frame.getContentPane().getWidth()) {
x = frame.getContentPane().getWidth() - circle.getWidth();
dx = -speed;
} else if (x < 0) {
x = 0;
dx = speed;
}
if (y + circle.getHeight() > frame.getContentPane().getHeight()) {
y = frame.getContentPane().getHeight() - circle.getHeight();
dy = -speed;
} else if (y < 0) {
y = 0;
dy = speed;
}
circle.setLocation((int) x, (int) y);
circle.repaint();
}
public static class CirclePanel extends JPanel {
public CirclePanel() {
super();
setOpaque(false);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillOval(0, 0, getWidth(), getHeight());
}
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
UnsupportedLookAndFeelException {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
new TestAnimation2().initUI();
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
});
}
}
Here is an example using a scheduled thread pool (very dangerous and disappointing)
import java.awt.Color;
import java.awt.Graphics;
import java.net.MalformedURLException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TestAnimation2 {
private static final int WIDTH = 800;
private static final int HEIGHT = 600;
private static final int SLOWEST_RATE = 10000000;
private static final int FASTEST_RATE = 1000;
private static final int RANGE = SLOWEST_RATE - FASTEST_RATE;
private static final int MIN = 0;
private static final int MAX = 100;
private double dx;
private double dy;
private double x = WIDTH / 2;
private double y = HEIGHT / 2;
private volatile long delay = convert(50);
private JFrame frame;
private CirclePanel circle;
private Runnable job;
private long lastMove = System.currentTimeMillis();
protected void initUI() throws MalformedURLException {
frame = new JFrame(TestAnimation2.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
circle = new CirclePanel();
circle.setSize(20, 20);
frame.add(circle);
frame.setSize(WIDTH, HEIGHT);
dx = 1;
dy = 1;
final ScheduledExecutorService sheduledThreadPool = Executors.newScheduledThreadPool(1);
final JSlider slider = new JSlider(MIN, MAX);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
delay = convert(slider.getValue());
}
});
slider.setValue(50);
slider.setLocation(0, 0);
slider.setSize(slider.getPreferredSize());
frame.add(slider);
frame.setVisible(true);
job = new Runnable() {
#Override
public void run() {
move();
sheduledThreadPool.schedule(job, delay, TimeUnit.NANOSECONDS);
}
};
sheduledThreadPool.schedule(job, delay, TimeUnit.NANOSECONDS);
}
protected long convert(float sliderValue) {
return (long) (SLOWEST_RATE - sliderValue / (MAX - MIN) * RANGE);
}
protected void move() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
System.err.println("Ellapsed " + (System.currentTimeMillis() - lastMove) + " delay is " + (double) delay / 1000000 + " ms");
x += dx;
y += dy;
if (x + circle.getWidth() > frame.getContentPane().getWidth()) {
x = frame.getContentPane().getWidth() - circle.getWidth();
dx = -1;
} else if (x < 0) {
x = 0;
dx = 1;
}
if (y + circle.getHeight() > frame.getContentPane().getHeight()) {
y = frame.getContentPane().getHeight() - circle.getHeight();
dy = -1;
} else if (y < 0) {
y = 0;
dy = 1;
}
circle.setLocation((int) x, (int) y);
frame.repaint();
lastMove = System.currentTimeMillis();
}
});
}
public static class CirclePanel extends JPanel {
public CirclePanel() {
super();
setOpaque(false);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillOval(0, 0, getWidth(), getHeight());
}
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
UnsupportedLookAndFeelException {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
new TestAnimation2().initUI();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}