Repaint() working only half times - java

i've got a problem with what should be a simple excercise.
I've been asked to make an applet that prints a green oval (filled) that becomes larger until it hits the borders, than start becoming smaller.
This should go on until you close the applet.
Well, i came out with this code
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JApplet;
public class Disco extends JApplet{
private int x;
private int y;
private int r;
private boolean enlarge;
MakeLarger makeLarger;
MakeSmaller makeSmaller;
public void init() {}
public void start() {
x = getWidth()/2;
y = getHeight()/2;
r = 50;
enlarge = true;
makeLarger = new MakeLarger();
makeLarger.start();
}
public void paint(Graphics g) {
g.setColor(Color.GREEN);
g.fillOval(x - r, y- r, r*2, r*2);
}
public void update() {
if(enlarge) {
makeLarger = new MakeLarger();
makeLarger.start();
} else {
makeSmaller = new MakeSmaller();
makeSmaller.start();
}
}
private class MakeLarger extends Thread {
public void run() {
while(true) {
x = getWidth()/2;
y = getHeight()/2;
if(getWidth() > getHeight()) {
if(r < getHeight()/2) {
r++;
repaint();
try {
sleep(25);
} catch(InterruptedException e) {
e.printStackTrace();
}
} else {
enlarge = false;
update();
Thread.currentThread().interrupt();
return;
}
} else {
if(r < getWidth()/2) {
r++;
repaint();
try {
sleep(25);
} catch(InterruptedException e) {
e.printStackTrace();
}
} else {
enlarge = false;
update();
Thread.currentThread().interrupt();
return;
}
}
}
}
}
private class MakeSmaller extends Thread {
public void run() {
while(true) {
x = getWidth()/2;
y = getHeight()/2;
if(r > 50) {
r--;
repaint();
revalidate();
try {
sleep(25);
} catch(InterruptedException e) {
e.printStackTrace();
}
} else {
enlarge = true;
update();
Thread.currentThread().interrupt();
return;
}
}
}
}
}
When I start my applet the oval start growing correctly until it hits the border and then suddently stop.
The first thing i thought was that it wasn't getting smaller correctly. But a little System.out.println work showed me that all computation was going on correctly, the problem is that the applet repaint only while the makeLarger thread is active, when the makeSmaller thread is at work the call to repaint() doesn't work!
If i resize the applet window while the makeSmaller Thread is at work it repaints correctly showing me the oval getting smaller.
Can, please, someone enlight me on this odd behavior?
What am I missing?
thank you everyone for your most appreciated help and sorry if my english is so poor!

I can't say that I've looked at all the code, but a couple of suggestions:
Paint in a JPanel's paintComponent override.
This is key: call the super.paintComponent method in the override, first line. This gets rid of prior images so that the image can get smaller.
Display the JPanel within your JApplet by adding it to the applet.
Myself, I'd use a single Swing Timer for the animation loop, and would use a boolean to decide which direction the sizing should go.
But regardless of whether I were using a Swing Timer or a Runnable placed in a Thread, I'd try to keep things a simple as possible, and that would mean using a single Timer that changes direction of resizing or a single Runnable/Thread that changes size of resizing based on a boolean. This swapping of threads that you're doing just serves to overly and unnecessarily complicate things, and could quite possibly be the source of your error.
As a side recommendation, you almost never want to extend Thread. Much better is to create classes that implement Runnable, and then when you need to run them in a background thread, create a Thread, passing in your Runnable into the Thread's constructor, and then calling start() on the Thread.

First things first, thank you all for helping me!
The problem I was trying to solve was this:
Make an applet which shows a green oval getting larger until it hits the borders, then it should start getting smaller up to a fixed radius (50), then it should start over (and over) again. Solve the problem by making two threads (by extending Thread).
Just to explain why the algorithm was so odd!
Well, reading your suggestions I fixed my code, now it's running properly. Here is the code if anyone will need this.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.JApplet;
import javax.swing.Timer;
public class Disco extends JApplet{
private int x;
private int y;
private int r;
private boolean enlarge;
MakeLarger makeLarger;
MakeSmaller makeSmaller;
public void init() {}
public void start() {
x = getWidth()/2;
y = getHeight()/2;
r = 50;
enlarge = true;
makeLarger = new MakeLarger();
makeLarger.start();
Timer timer = new Timer(1000/60, new ActionListener() {
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
public void paint(Graphics g) {
BufferedImage offScreenImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_4BYTE_ABGR_PRE);
Graphics2D g2D = (Graphics2D) offScreenImage.getGraphics();
g2D.clearRect(0, 0, getWidth(), getHeight());
g2D.setColor(Color.GREEN);
g2D.fillOval(x - r, y- r, r*2, r*2);
g.drawImage(offScreenImage, 0, 0, this);
}
public void update() {
if(enlarge) {
makeLarger = new MakeLarger();
makeLarger.start();
} else {
makeSmaller = new MakeSmaller();
makeSmaller.start();
}
}
private class MakeLarger extends Thread {
public void run() {
while(true) {
x = getWidth()/2;
y = getHeight()/2;
if(getWidth() > getHeight()) {
if(r < getHeight()/2) {
r++;
try {
sleep(25);
} catch(InterruptedException e) {
e.printStackTrace();
}
} else {
enlarge = false;
update();
Thread.currentThread().interrupt();
return;
}
} else {
if(r < getWidth()/2) {
r++;
try {
sleep(25);
} catch(InterruptedException e) {
e.printStackTrace();
}
} else {
enlarge = false;
update();
Thread.currentThread().interrupt();
return;
}
}
}
}
}
private class MakeSmaller extends Thread {
public void run() {
while(true) {
x = getWidth()/2;
y = getHeight()/2;
if(r > 50) {
r--;
try {
sleep(25);
} catch(InterruptedException e) {
e.printStackTrace();
}
} else {
enlarge = true;
update();
Thread.currentThread().interrupt();
return;
}
}
}
}
}

Related

Swing requires createBufferStrategy(2) to properly paint

I have some grids that is painted to screen one by one. I use arrow keys to move grids around as a group. Swing is said to be doubleBuffered by default so I believe frame.createBufferStrategy(2) is a bad practice but the problem is when I don't use manual double buffering, the grids are misaligned and some holes are appearing between them. Using manual double buffering fixes it.
I'm also experiencing some graphical problems(such as a dialog's buttons not displaying properly) in the actual program(not in SSCCE) so I thought it might be caused by the incorrect implementation of the double buffering.
Here is the SSCCE of the program, that causes grids to misalign when not manually double buffered:
package SSCCE;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LayoutManager;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
boolean manuallyDoubleBuffered = false; //change this
static Main main;
public final JFrame frame = new JFrame();
public final Keys keys = new Keys();
private JPanel panel;
private BufferStrategy bufferStrategy;
public static void main(String[] args) {
main = new Main();
main.initiate();
// --START LOOP--
Thread loop = new Thread(main.new Looper());
loop.start();
}
public void initiate() {
frameInit();
keys.start();
}
private void frameInit() {
frame.setSize(1200, 750);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
setUpGUI();
if (manuallyDoubleBuffered)
frame.createBufferStrategy(2); // manual double buffering
bufferStrategy = frame.getBufferStrategy();
}
private void setUpGUI() {
panel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Main.main.rendering(g2d);
super.paintComponent(g);
}
};
LayoutManager layout = new FlowLayout();
frame.getContentPane().setBackground(Color.black);
panel.setLayout(layout);
panel.setOpaque(false);//
JButton but1 = new JButton("but1");
panel.add(but1);
frame.add(panel);
}
class Looper implements Runnable {
#Override
public void run() {
Main.main.gameLoop();
}
}
private void gameLoop() {
// variables are declared at start
while (true) {
if (manuallyDoubleBuffered)
paint(); // MANUAL double buffering
else
frame.repaint();// no manual double buffering
update();
try {
Thread.sleep(1000 / 60);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}// loop end
private void update() {
move();
}
private void rendering(Graphics2D g2d) {
// // testing
paintGrids(g2d);
}
private void move() {
x += sx;
y += sy;
}
int sx = 0; //speedX
int sy = 0; //speedY
//top left corner of the grid
int x = 0;
int y = 0;
private void paintGrids(Graphics2D g) {
for (int i = 0; i < 100; i++) {
for (int t = 0; t < 100; t++) {
g.setColor(Color.GRAY);
g.fillRect(i * 50 + x, t * 50 + y, 50, 50);
g.setColor(Color.BLACK);
g.drawString(i + "," + t, i * 50 + x, t * 50 + y + 10);
}
}
}
public void paint() {
// uses double buffering system.
do {
do {
Graphics2D g2d = (Graphics2D) bufferStrategy.getDrawGraphics();
g2d.fillRect(0, 0, frame.getWidth(), frame.getHeight());
try {
frame.paint(g2d);
} catch (NullPointerException e) {
e.printStackTrace();
}
g2d.dispose();
} while (bufferStrategy.contentsRestored());
bufferStrategy.show();
} while (bufferStrategy.contentsLost());
}
}
class Keys implements KeyListener {// Trimmed down to shorten SSCCE
private final int leftKey = 37; // left b.
private final int rightKey = 39; // Right b.
private final int upKey = 38;// up k.
private final int downKey = 40;// down k.
public void start() {
Main.main.frame.addKeyListener(this);
Main.main.frame.setFocusable(true);
}
private void left() {
Main.main.sx -= 10;
}
private void right() {
Main.main.sx += 10;
}
private void up() {
Main.main.sy -= 10;
}
private void down() {
Main.main.sy += 10;
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
System.out.println(e.getKeyCode());
switch (e.getKeyCode()) {
case leftKey:
left();
break;
case rightKey:
right();
break;
case downKey:
down();
break;
case upKey:
up();
break;
}
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
}// END OF THE KEYS CLASS
Oracle tutorials of swing does not explain the usage with a game loop. What is the best way to do it? Am I doing anything wrong?
In case the visual error is not reproduced on other computers, I'm uploading a screenshot:
Black lines are caused by the misalinging of the rectangles. They don't exist when manual double buffering is set to true.
Thanks in advance.
Edit: I've forgot to mention that the black lines occur when grids are moving.
I' have also found out, manual double buffering drastically reduces performance.
Edit 2 : I've fixed the problem and posted it as an answer but feel free to comment on my code. Main class(except the gameLoop) is similar to the actual main class I use in my program.
I couldn't see any change in the background. Here's the code change I made.
public static void main(String[] args) {
main = new Main();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
main.initiate();
}
});
// --START LOOP--
Thread loop = new Thread(main.new Looper());
loop.start();
}
You must always start a Swing application with a call to SwingUtilities.invokeLater.
I've found the problem and writing here in case something like that ever happens to anyone else.
The problem was caused due to program being multi-threaded. Top left coordinates of the grids(x and y) were updated by the other thread in the middle of the paintGrids() method. Manual double buffering was slowing the program down (by hundreds of times) and that was allowing the paintGrids method to finish painting before x and y was updated by the keys.
To fix it I've added the following to the start of the paintGrids method:
int x = this.x;
int y = this.y;

Eclipse ADT Applet blank screen

I'm trying to show some images on my applet and have them move around but whenever I run my project through the applet all I get is a blank/black screen. I can change the color of the background, but I can't see any of my images. Also, the applet isn't initialized when I try to start the applet. Here's the code:
package test;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.URL;
public class MCTaRE extends Applet implements Runnable, KeyListener {
private Character character;
private Image image, robot;
private Graphics graphics;
private URL base;
#Override
public void init() {
setSize(800, 480);
setBackground(Color.BLACK);
setFocusable(true);
Frame frame = (Frame) this.getParent().getParent();
frame.setTitle("Test");
robot = getImage(base, "data/character.png");
}
#Override
public void start() {
Thread thread = new Thread(this);
thread.start();
character = new Character();
}
#Override
public void stop() {
}
#Override
public void run() {
while (true) {
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void update(Graphics g) {
if (image == null) {
image = createImage(this.getWidth(), this.getHeight());
graphics = image.getGraphics();
}
// graphics.setColor(getBackground());
graphics.fillRect(0, 0, getWidth(), getHeight());
// graphics.setColor(getForeground());
paint(graphics);
g.drawImage(image, 0, 0, this);
}
#Override
public void paint(Graphics g) {
g.drawImage(robot, character.getInitX() - 61,
character.getInitY() - 63, this);
}
public void KeyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_SPACE:
System.out.println("Space");
break;
}
}
public void KeyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
}
package test;
public class Character {
private int initX = 100;
private int initY;
private int speedX = 0;
public void update(){
//if (key listener is at the top){
moveLeft();
//}
//else {
stop();
//}
initY = 200;
}
public int getInitX() {
return initX;
}
public int getInitY() {
return initY;
}
public int getSpeedX() {
return speedX;
}
public void setInitX(int initX) {
this.initX = initX;
}
public void setInitY(int initY) {
this.initY = initY;
}
public void setSpeedX(int speedX) {
this.speedX = speedX;
}
public void moveLeft(){
speedX = -5;
}
public void stop(){
speedX = 0;
}
}
Any help would be greatly appreciated I need to make this work. I can try to use an AVD but I think that would complicate things even more. And a JApplet isn't an option.
"but I can't see any of my images."
You have a few options loading the images.
I like to use getClass().getResource("/path/to/image") as it load from the class path, which is used from embedded resources.
try {
robot = ImageIO.read(getClass().getResource("/data/character.png"));
} catch (IOException ex) {
ex. printStackTrace();
}
You could do the same thing using ImageIcon.getImage where you can avoid the try/catch but I prefer the former, so you can get exception if path is incorrect
robot = new ImageIcon(getClass().getResource("/data/character.png")).getImage();
For Applet, you can use getCodeBase() as seen here
try {
URL url = new URL(getCodeBase(), "data/character.png");
robot = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
NOTE: All three options will work with your code (just tested all), given your file structure is as follows, with data in the src
ProjectRoot
src
data
character.png
test
MCTaRE.java
Side Note
You are not calling super.paint in your paint method, which will leave you with paint artifacts. So call it
#Override
public void paint(Graphics g) {
super.paint(g);
Why code an Applet in the first place. If it must be an Applet, why AWT Applet and not Swing JApplet. If this is a class assignment, have your professor read Why CS teachers should stop teaching Java applets
If you want to ditch the zero (AWT) and get with the hero (Swing), read more at Creating GUI with Swing

How to put two different tasks in Threads

I have following code. in this code i have an image which moves from left to right and a button which has an event. but i want to put these both tasks in Threads. so that it can work properly. the problem with this code is that button event does not work until it reaches to the right most point.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class MyImage extends JFrame implements ActionListener
{
static int xPixel = 20;
Image myImage, offScreenImage;
Graphics offScreenGraphics;
JPanel p = new JPanel();
Button btn = new Button("bun");
JFrame f = new JFrame();
public MyImage()
{
myImage = Toolkit.getDefaultToolkit().getImage("mywineshoplogo.jpg");
setExtendedState(JFrame.MAXIMIZED_BOTH);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
add(p);
p.add(btn);
moveImage();
btn.addActionListener(this);
}
public void update(Graphics g)
{
paint(g);
}
public void paint(Graphics g)
{
int width = getWidth();
int height = getHeight();
if (offScreenImage == null)
{
offScreenImage = createImage(width, height);
offScreenGraphics = offScreenImage.getGraphics();
}
// clear the off screen image
offScreenGraphics.clearRect(0, 0, width + 1, height + 1);
// draw your image off screen
offScreenGraphics.drawImage(myImage, xPixel, 10, this);
// draw your image off screen
// show the off screen image
g.drawImage(offScreenImage, 0, 0, this);
// show the off screen image
}
void moveImage() //left to right move
{
Thread hilo = new Thread() {
public void run() {
try {
for (int i = 0; i < 530; i++)
{
xPixel += 1;
repaint();
// then sleep for a bit for your animation
try
{
Thread.sleep(4);
} /* this will pause for 50 milliseconds */
catch (InterruptedException e)
{
System.err.println("sleep exception");
}
}
} //try
catch (Exception ex) {
// do something...
}
}
};
hilo.start();
}
/* void moveimg() // right to left move
{
for (int i = 529; i > 0; i--)
{
if (i == 1)
{
moveImage();
}
xPixel -= 1;
repaint();
// then sleep for a bit for your animation
try
{
Thread.sleep(40);
} // this will pause for 50 milliseconds
catch (InterruptedException e)
{
System.err.println("sleep exception");
}
}
} */
public void actionPerformed(ActionEvent ae)
{
try
{
if (ae.getSource() == btn)
{
p.setBackground(Color.RED);
}
}
catch (Exception e)
{
System.out.println("error");
}
}
public static void main(String args[])
{
MyImage me = new MyImage();
}
}
Whenever you are about to write Thread.sleep in your GUI code, stop yourself and introduce a task scheduled on Swing's Timer. This is exactly what you need with your code: schedule each update as a separate scheduled task on Timer. Timer is quite simple and straightforward to use, see for example this official Oracle tutorial.

How to get the color of a pixel

In my code below....i wish to add a yellow bar at the bottom if upon firing a bullet, the bullet reaches the moving bar at the top(which is a trail of alternative yellow and cyan boxes) and touches it at a yellow box.For that what I am planning is to first detect the color of the pixel where the bullet touches the moving bar and then add yellow bar below if the pixel color was yellow............Here is my code:
//
import java.awt.*;
import java.applet.Applet;
import java.awt.event.*;
public class abyss extends Applet implements Runnable,KeyListener{
int lim=446,l,src,i=-40,n,c,ct=450,cl=225,y,f,bl,bw,fr,mud=0;
Thread v=null;
public void init() {
setBackground(Color.black);
addKeyListener(this);
}
public void start() {
v=new Thread(this);
v.start();
}
public void run() {
try {
int trainDelay = 0;
while (true) {
if(ct<=200)
{repaint();
}
if (y == 1) {
if (f<=41) {
bl = cl + 25;
bw = 10;
f = lim;
}
if (f > 41) {
repaint(bl, f, bw, bw + 1);
if (--f<=41) {
if(l%80==0)
{mud=1;
ct=ct-20;
System.out.println("decreased");
lim=lim-20;
repaint();}
else if(l%80!=0 && (ct+20<=420))
{ct=ct+20;
lim=lim+20;
}
y = 0;
bw = 0;
}
}
}
if (trainDelay <= 0) {
repaint();
i = i + 40;
c = 1;
n = i / 40;
trainDelay = 200;
}
Thread.sleep(5);
trainDelay--;
}
} catch (Exception e) {
}
}
public void paint(Graphics g) {
if(ct>=200){
if(mud==1)
{g.setColor(Color.orange);
g.fill3DRect(0,ct+20,500,450-ct,true);}
g.setColor(Color.darkGray);
g.fill3DRect(0,200,30,300,true);
g.fill3DRect(470,200,30,300,true);
g.fill3DRect(0,470,500,30,true);
g.setColor(Color.blue);
g.fill3DRect(cl,ct,50,20,true);
setBackground(Color.black);
for(int j=n-1;j>=0;j--)
{ l=j*40;
if((c%2)==0)
{g.setColor(Color.cyan);
g.fill3DRect(l,0,50,40,true);}
else
{g.setColor(Color.orange);
g.fill3DRect(l,0,50,40,true);}
c++;
}
}
{g.setColor(Color.yellow);
g.fillOval(bl,f,bw,bw);
if(ct<=200)
{setBackground(Color.red);
g.setColor(Color.yellow);
g.drawString("You win!!",215,210);
}
} }
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_LEFT && cl>=38){
cl=cl-10;}
if(e.getKeyCode()==KeyEvent.VK_RIGHT && cl<=412){
cl=cl+10;}
if(e.getKeyCode()==KeyEvent.VK_UP){
y=1;}
}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
public void stop() {
try{wait();}
catch(Exception e) {}
}
public void destroy() {}
}
Please tell me how to detect the color.........can getColor() be used??
It looks like you are off to a good start.
I would recommend that you have your cyan and yellow boxes be objects with a color and location attributes.
Create a getColor method for your box objects, and create a getLocation method for your box objects and bullet object.
when bullet.getLocation = box.getLocation
then box.getColor
Take a look at this Java/Processing App that sets the color of a circle when clicked on. It uses principles similar to what I just described.
https://sourceforge.net/projects/all-spark-cube/
https://github.com/spudstud/All-Spark-Cube/blob/master/Sandbox/Panel_2d/LedObject.pde

Problems with Java's Paint method, ridiculous refresh velocity

I'm developing a very simple version of R-Type as work for the university, but despite it works, the craft velocity is a lot of slow, so the movement is ugly and clumsy.
I use the method repaint for refresh the screen, there are others methods or ways best than it?
Video of Movement
Paint method at main Panel
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawImage(fondo, 0, 0,1200,600,this);
pj.paint(g2);
g2D=g2;
}
PJ's paint method
public void paint(Graphics2D g) {
g.drawImage(imagen,x,y,this);
}
PJ's move method
public void move (KeyEvent e) {
int dx = 0; int dy = 0;
int code = e.getKeyCode();
switch (code) {
case KeyEvent.VK_Q: dy-=1; break;
case KeyEvent.VK_A: dy+=1; break;
case KeyEvent.VK_P: dx+=1; break;
case KeyEvent.VK_O: dx-=1; break;
}
int x = (getX()<maxX&&getX()!=0) ? getX()+dx : getX();
int y = (getY()<maxY&&getY()!=0) ? getY()+dy : getY();
if (getY()>=maxY||getY()==0) {
if (dy==+1) y=y+1;
}
setPosicion(x, y);
}
The image fondo should already be scaled to 1200x600.
I am not sure, but is super.paint(g) needed? You might also use paintComponent.
The event handling (you seem to be moving by 1 pixel on key down), must be done correctly. I would have set the direction and speed (1px), and leave it to a swing timer to do the continuous moving.
Repainting best is done resilient/flexible: repaint(20L) (50 frames per second);
events like key-down maybe with EventQueue.invokeLater(new Runnable() { ... });.
Especially you might use repaint with the changed area.
You can find a great example of a similar program here. The example demonstrates creating a new thread and having that thread sleep every iteration through the main loop.
Here is another question about loading images for games in Java.
It looks like swing itself is pretty crummy for using images in games. You may want to consider using a more suitable library.
Below is simple example using a background as simple game loop. It updates the state of the game objects and calculates the required delay in order to maintain the required fps.
The game object (Ship) has the ability to accelerate/decelerate over a short period of time
import java.awt.BorderLayout;
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.KeyEvent;
import java.awt.geom.Path2D;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class AnimationTest {
public static void main(String[] args) {
new AnimationTest();
}
public AnimationTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new GamePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GamePane extends JPanel {
private Ship ship;
public GamePane() {
ship = new Ship();
Thread thread = new Thread(new MainLoop(this));
thread.setDaemon(true);
thread.start();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
// Key controls...
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "upPressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "downPressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "upReleased");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "downReleased");
am.put("upPressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
// Change the direction...
ship.setDirection(-1);
// Accelerate by 1 per frame
ship.setVelocity(1);
}
});
am.put("downPressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
// Change direction
ship.setDirection(1);
// Accelerate by 1 per frame
ship.setVelocity(1);
}
});
am.put("upReleased", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
// Deccelerate by 1 per frame
ship.setVelocity(-1);
}
});
am.put("downReleased", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
// Deccelerate by 1 per frame
ship.setVelocity(-1);
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void updateState() {
// Update the state of the game objects.
// This would typically be better done in
// some kind of model
ship.update(getWidth(), getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Paint the game state...
Graphics2D g2d = (Graphics2D) g.create();
ship.paint(g2d);
g2d.dispose();
}
}
public class MainLoop implements Runnable {
private GamePane pane;
private int fps = 25;
public MainLoop(GamePane pane) {
this.pane = pane;
}
#Override
public void run() {
// Wait until the screen is ready
while (pane.getHeight() <= 0) {
try {
Thread.sleep(125);
} catch (InterruptedException ex) {
}
}
// Main loop
while (true) {
// Start time loop
long startTime = System.currentTimeMillis();
// Update the game state
pane.updateState();
// Calculate the amount of time it took to update
long elasped = System.currentTimeMillis() - startTime;
// Calculate the number of milliseconds we need to sleep
long sleep = Math.round((1000f / fps) - elasped);
pane.repaint();
if (sleep > 0) {
try {
Thread.sleep(sleep);
} catch (InterruptedException ex) {
}
}
}
}
}
public static class Ship {
public static int MAX_SPEED = 8;
private int direction = 0;
private int velocity = 0;
private int x;
private int y;
private int speed = 0;
private Path2D shape;
private boolean initState;
public Ship() {
shape = new Path2D.Float();
shape.moveTo(0, 0);
shape.lineTo(5, 5);
shape.lineTo(0, 10);
shape.lineTo(0, 0);
shape.closePath();
initState = true;
}
public void setDirection(int direction) {
this.direction = direction;
}
public void setVelocity(int velocity) {
this.velocity = velocity;
}
public void update(int width, int height) {
if (initState) {
y = (height - 10) / 2;
initState = false;
} else {
// Add the velocity to the speed
speed += velocity;
// Don't over accelerate
if (speed > MAX_SPEED) {
speed = MAX_SPEED;
} else if (speed < 0) {
speed = 0;
}
// Adjust out position if we're moving
if (speed > 0) {
y += (direction * speed);
}
// Bounds check...
if (y - 5 < 0) {
y = 5;
} else if (y + 5 > height) {
y = height - 5;
}
}
}
public void paint(Graphics2D g2d) {
int yPos = y - 5;
g2d.translate(10, yPos);
g2d.fill(shape);
g2d.translate(-10, -yPos);
}
}
}

Categories

Resources