I am currently using a JPanel to draw the elements from my game, however, there is occasional stuttering with the graphics, after some research I learnt about screen tearing and double buffering, which I believe is the solution to my problem, however, currently with my code I am finding it extremely difficult to implement without tearing out a lot of code.
I am wondering if anyone could possibly provide either a simpler way to fix my screen tearing on a small 2D tile based game, or how to do double buffering with my current code, thank you very much!
Frame.java
public class Frame extends JPanel implements KeyListener {
public static Game game;
public static boolean[] mouseDown = new boolean[4];
public static int width, height;
public static Font font = new Font("Arial", Font.BOLD, 16);
public static JFrame frame;
public Frame()
{
setFocusable(true);
requestFocus();
setOpaque(true);
addKeyListener((KeyListener) this);
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
});
addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseMoved(MouseEvent e) {
}
#Override
public void mouseDragged(MouseEvent e) {
}
});
}
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setBackground(Color.BLACK);
g2d.setFont(font);
super.setBackground(Color.BLACK);
super.paintComponent(g2d);
//This is where I use g2d to draw onto the JPanel
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
frame = new JFrame("Game");
frame.add(new Frame());
frame.setSize(1000, 750);
frame.setExtendedState(frame.getExtendedState() | JFrame.MAXIMIZED_BOTH);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}});
}
Game.java
public void gameStart() {
Thread gameThread = new Thread() {
// Override run() to provide the running behavior of this thread.
#Override
public void run() {
gameLoop();
}
};
// Start the thread. start() calls run(), which in turn calls gameLoop().
gameThread.start();
}
public void gameLoop() {
while (!isGameFinished) {
beginTime = System.nanoTime();
gameUpdate();
frame.repaint();
// Delay timer to provide the necessary delay to meet the target rate
timeTaken = System.nanoTime() - beginTime;
timeLeft = (UPDATE_PERIOD_NSEC - timeTaken) / 1000000L; // in milliseconds
if (timeLeft < 10) timeLeft = 10; // set a minimum
try { // Provides the necessary delay and also yields control so that other thread can do work.
Thread.sleep(timeLeft);
} catch (InterruptedException ex) { }
}
}
public void gameStart() {
Thread gameThread = new Thread() {
// Override run() to provide the running behavior of this thread.
#Override
public void run() {
gameLoop();
}
};
// Start the thread. start() calls run(), which in turn calls gameLoop().
gameThread.start();
}
public void gameLoop() {
while (!isGameFinished) {
beginTime = System.nanoTime();
gameUpdate();
frame.repaint();
timeTaken = System.nanoTime() - beginTime;
timeLeft = (UPDATE_PERIOD_NSEC - timeTaken) / 1000000L;
if (timeLeft < 10) timeLeft = 10; // set a minimum
try {
Thread.sleep(timeLeft);
} catch (InterruptedException ex) { }
}
}
As shown in the code, the Game class contains the Game loop, which will constantly repaint the Frame class. How can I tweak this code to allow for double buffering to remove screen tearing? Thanks!
Double buffering is the best way to fix it. Try calling Toolkit#sync (Toolkit.getDefaultToolkit().sync()) , which sometimes removes the issue. You won't be tearing down much code if you switch to double buffering, just move the code in paint to a separate method.
Related
I'm trying to make an animation of a red oval that will move to the right of the screen. But it just draws the oval. I don't know what I'm doing wrong and I literally can't find anything about how to do this. Any help would be awesome, thanks.
import java.awt.*;
public class mainClass
{
public mainClass()
{
Frame f = new Frame("Canvas Example");
f.add(new MyCanvas());
f.setLayout(null);
f.setSize(400, 400);
f.setVisible(true);
}
public static void main(String args[])
{
new mainClass();
}
}
class MyCanvas extends Canvas
{
int x = 75;
public MyCanvas() {
setBackground (Color.BLACK);
setSize(400, 400);
}
public void paint(Graphics g)
{
g.setColor(Color.red);
g.fillOval(x, 75, 150, 75);
}
public void update(Graphics g)
{
x++;
}
}
Theory
Animation is hard, I mean, really good animation is hard. There is a lot of theory which goes into creating good animation, things like easement, anticipation, squish ... I could go on, but I'm boring myself.
The point is, simply incrementing a value (AKA linear progression) is a poor approach to animation. If the system is slow, busy or for some other reason isn't keeping up, the animation will suffer because of it (stuttering, pauses, etc).
A "better" solution is to use a time based progression. That is, you specify the amount of time it will take to move from the current state to it's new state and then continuously loop and update the state until you run out of time.
The "main loop"
If you do any research into game development, they always talk about this thing called the "main loop".
The "main loop" is responsible for updating the game state and scheduling paint passes.
In terms to your question, you need a "main loop" which can update the position of the oval until it reaches it's target position.
Because most GUI frameworks are already running within their own thread context, you need to setup your "main loop" in another thread
AWT
Some theory
AWT is the original GUI framework, so it's "old". While Swing does sit on top of it, you'll find more people have experience with Swing then they do AWT.
One of the important things to keep in mind is, Canvas is not double buffered, so, if you're updating the component fast enough, it will flash.
To overcome this, you need to implement some kind of double buffering workflow.
Runnable example
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.time.Duration;
import java.time.Instant;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Frame frame = new Frame();
frame.add(new TestCanvas());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Ticker implements Runnable {
public interface Callbck {
public void didTick(Ticker ticker);
}
private boolean isRunning = false;
private Thread thread;
private Callbck callback;
public void setCallback(Callbck tick) {
this.callback = tick;
}
public void start() {
if (isRunning) {
return;
}
isRunning = true;
thread = new Thread(this);
thread.setDaemon(false);
thread.start();
}
public void stop() {
if (!isRunning) {
return;
}
isRunning = false;
thread.interrupt();
thread = null;
}
#Override
public void run() {
while (isRunning) {
try {
Thread.sleep(5);
if (callback != null) {
callback.didTick(this);
}
} catch (InterruptedException ex) {
isRunning = false;
}
}
}
}
public class TestCanvas extends Canvas {
private BufferedImage buffer;
int posX;
private Ticker ticker;
private Instant startedAt;
private Duration duration = Duration.ofSeconds(5);
public TestCanvas() {
ticker = new Ticker();
ticker.setCallback(new Ticker.Callbck() {
#Override
public void didTick(Ticker ticker) {
if (startedAt == null) {
startedAt = Instant.now();
}
Duration runtime = Duration.between(startedAt, Instant.now());
double progress = runtime.toMillis() / (double)duration.toMillis();
if (progress >= 1.0) {
stopAnimation();
}
posX = (int)(getWidth() * progress);
repaint();
}
});
}
protected void startAnimtion() {
ticker.start();
}
protected void stopAnimation() {
ticker.stop();
}
#Override
public void setBounds(int x, int y, int width, int height) {
buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
super.setBounds(x, y, width, height);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void addNotify() {
super.addNotify();
startAnimtion();
}
#Override
public void removeNotify() {
super.removeNotify();
buffer = null;
}
#Override
public void paint(Graphics g) {
super.paint(g);
if (buffer == null) {
return;
}
Graphics2D g2d = buffer.createGraphics();
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.setColor(Color.RED);
int midY = getHeight() / 2;
g2d.fillOval(posX, midY - 5, 10, 10);
g2d.dispose();
g.drawImage(buffer, 0, 0, this);
}
}
}
What is Canvas good for ...?
In most cases, you should avoid using Canvas, for many of the reasons mentioned above, but one of the reasons you might consider using Canvas is if you want to take full control over the painting process. You might do this if you want to create a complex game which and you want to get the best possible performance out of the rendering pipeline.
See BufferStrategy and BufferCapabilities and the JavaDocs for more detail
A Swing based implementation
Hopefully I've convinced you that a Swing implementation might be a better solution, which in that case you should make use of a Swing Timer instead of Thread, as Swing is not thread safe
See Concurrency in Swing and How to Use Swing Timers
for more details
Runnable 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.time.Duration;
import java.time.Instant;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Ticker {
public interface Callbck {
public void didTick(Ticker ticker);
}
private Timer timer;
private Callbck callback;
public void setCallback(Callbck tick) {
this.callback = tick;
}
public void start() {
if (timer != null) {
return;
}
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (callback == null) {
return;
}
callback.didTick(Ticker.this);
}
});
timer.start();
}
public void stop() {
if (timer == null) {
return;
}
timer.stop();
timer = null;
}
}
public class TestPane extends JPanel {
int posX;
private Ticker ticker;
private Instant startedAt;
private Duration duration = Duration.ofSeconds(5);
public TestPane() {
ticker = new Ticker();
ticker.setCallback(new Ticker.Callbck() {
#Override
public void didTick(Ticker ticker) {
if (startedAt == null) {
startedAt = Instant.now();
}
Duration runtime = Duration.between(startedAt, Instant.now());
double progress = runtime.toMillis() / (double) duration.toMillis();
if (progress >= 1.0) {
stopAnimation();
}
posX = (int) (getWidth() * progress);
repaint();
}
});
}
protected void startAnimtion() {
ticker.start();
}
protected void stopAnimation() {
ticker.stop();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void addNotify() {
super.addNotify();
startAnimtion();
}
#Override
public void removeNotify() {
super.removeNotify();
stopAnimation();
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
int midY = getHeight() / 2;
g2d.fillOval(posX, midY - 5, 10, 10);
g2d.dispose();
}
}
}
The reason this doesn't animate is that nothing triggers the component to update and repaint itself. There are a few things that need to be considered:
Something needs to call the update method. Ordinarily, this is triggered by a call to repaint() on the component, but nothing in this code calls that method.
It's important for an overridden update method to call super.update(g) to ensure the default behavior is invoked (clearing the canvas and painting it again).
Animation has a time component: the oval should move over some period of time. This needs to be incorporated into the logic. AWT has no built-in mechanism for timed behavior.
If you're able to use classes from Swing, the javax.swing.Timer class is very useful for animation. It executes your callback on the AWT thread, and therefore means that you don't have to take special measures to ensure thread safety.
If you can't use Swing, it can use java.util.Timer or a custom thread, but will need to manage thread synchronization directly.
You'll probably also want the animation to stop once the oval reaches the edge of the canvas.
Here's an example using javax.swing.Timer (assuming Java 8 or later). Note that all of the animation logic is in the ActionListener attached to the Timer, so the overridden update method has been removed:
import javax.swing.*;
import java.awt.*;
public class MainClass {
public static final int CANVAS_SIZE = 400;
public MainClass() {
Frame f = new Frame("Canvas Example");
f.add(new MyCanvas(CANVAS_SIZE));
f.setLayout(null);
f.setSize(CANVAS_SIZE, CANVAS_SIZE);
f.setVisible(true);
}
public static void main(String[] args) {
new MainClass();
}
}
class MyCanvas extends Canvas {
public static final int INITIAL_POSITION = 75;
public static final int HEIGHT = 75;
public static final int WIDTH = 150;
private static final int TIMER_DELAY_MILLIS = 1000 / 30; // 30 FPS
private int x = INITIAL_POSITION;
private final Timer timer;
public MyCanvas(int canvasSize) {
setBackground(Color.BLACK);
setSize(canvasSize, canvasSize);
timer = new Timer(TIMER_DELAY_MILLIS, (event) -> {
// ensure the oval stays on the canvas
if (x + WIDTH < getWidth()) {
x++;
repaint();
} else {
stopAnimation();
}
});
timer.start();
}
public void paint(Graphics g) {
g.setColor(Color.red);
g.fillOval(x, INITIAL_POSITION, WIDTH, HEIGHT);
}
private void stopAnimation() {
timer.stop();
}
}
This code has a few additional incidental changes.
Updated the name of mainClass to MainClass (leading capital "M") to comply with standard Java naming conventions.
Changed String args[] to String[] args for the same reason.
Extracted numeric constants to named static final fields.
Made the canvas size a constructor parameter, controlled by the caller.
Made x private.
Minor formatting changes to ensure a consistent style.
One option that doesn't use javax.swing.Timer (with unchanged code omitted):
private final AtomicInteger x = new AtomicInteger(INITIAL_POSITION);
public MyCanvas(int canvasSize) {
setBackground(Color.BLACK);
setSize(canvasSize, canvasSize);
new Thread(() -> {
try {
// ensure the oval stays on the canvas
while (x.incrementAndGet() + WIDTH < getWidth()) {
Thread.sleep(TIMER_DELAY_MILLIS);
repaint();
}
} catch (InterruptedException e) {
// Just let the thread exit
Thread.currentThread().interrupt();
}
}).start();
}
I am totally a newbie in Java. And I want to create an animation. But I did not succeed. mouseClicked() will be executed in the event dispatching thread. Why doesn't it work? Will the event dispatching be occupied by other threads, what other threads?
public class DemoThreadGUI {
public static void main(String [] args) {
DemoThreadGUI app = new DemoThreadGUI();
app.go();
}
public void go() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
generateGUI();
}
});
}
public void generateGUI() {
JFrame frame = new JFrame("Demo");
frame.add(new MyPanel());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
class MyPanel extends JPanel implements MouseListener {
private int x,y;
private int r;
public MyPanel() {
this.setPreferredSize(new Dimension(100,100));
this.addMouseListener(this);
x = 50;
y = 50;
r = 25;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(x-r,y-r,r*2,r*2);
}
public void mouseClicked(MouseEvent event) {
int targetX = event.getX();
int targetY = event.getY();
for(int i=0;i<10;++i) {
x = (x+targetX)/2;
y = (y+targetY)/2;
repaint();
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
}
}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
public void mousePressed(MouseEvent event) {}
public void mouseReleased(MouseEvent event) {}
}
}
In your mouseClicked() start a new Thread and place the code in the thread
for(int i=0;i<10;++i) {
x = (x+targetX)/2;
y = (y+targetY)/2;
repaint();
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
}
The repaint() call should be wrapped into SwingUtilities.invokeAndWait() to pass control to EDT
As Holger said, You can’t expect a thread to paint your UI when you told it to sleep.
Use this instead:
Make targetX and targetY global variables, and make a new java.util.Thread object t.
private int x,y;
private int r;
Thread t;
int targetX, targetY;
modify your mouseClicked method as:
public void mouseClicked(final MouseEvent event) {
targetX = event.getX();
targetY = event.getY();
t = new Thread(new Runnable(){public void run(){anim();}});
t.start();
}
Put your code of the mouseClicked method into the anim method as:
public void anim()
{
for(int i=0;i<10;++i) {
try{
x = (x+targetX)/2;
y = (y+targetY)/2;
repaint();
Thread.sleep(100);
}catch(Exception e){}
}
t.stop();
}
This works perfectly.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I'm trying to learn Java and game programming by myself by just playing around with some different things. But now I have come across this problem, when my java app goes fullscreen via GraphicsDevice, the KeyListeners don't work. It's like it doesn't register anything when I press the buttons on my keyboard. When the app isn't fullscreen, everything works as it is supposed to.
I am using Mac.
The code is a bit messy, but it should be somewhat easy to navigate etc.
Game.class
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
// Render Vars
public static final int WIDTH = 1280;
public static final int HEIGHT = 800; //WIDTH / 16 *
public static final int SCALE = 1;
public final static String TITLE = "Test Game - inDev 1.0.0";
public static boolean fullscreen = false;
public static JFrame window = new JFrame(TITLE);
public static Font defaultFont = new Font("Dialog", Font.PLAIN, 12);
public static Color defaultColor = Color.gray;
// Thread Vars
public static boolean running = false;
private static Thread thread;
private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
// Mechanic Vars
public static boolean mouseDown;
public static int mouseX;
public static int mouseY;
// Game Vars
public static GameState gameState = new Play();
public static boolean keyPressed;
public static Game game = new Game();
public static void main(String arghs[]) {
game.setSize(new Dimension(WIDTH, HEIGHT));
game.setPreferredSize(new Dimension(WIDTH, HEIGHT));
fullscreen = true;
window.add(game);
window.setUndecorated(true);
window.pack();
window.setLocationRelativeTo(null);
window.setResizable(false);
window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
window.setVisible(true);
// HERE I GO FULLSCREEN
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(window);
game.addKeyListener(new KeyEventListener());
game.addMouseListener(new MouseEventListener());
game.start();
}
public void run() {
init();
long lastTime = System.nanoTime();
final double numTicks = 100.0;
double ns = 1000000000 / numTicks;
double delta = 0;
int updates = 0;
int frames = 0;
long timer = System.currentTimeMillis();
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
if (delta >= 1) {
update();
updates++;
delta--;
}
render();
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println(updates + " Ticks, FPS: " + frames);
updates = 0;
frames = 0;
}
}
stop();
}
private synchronized void start() {
if (running) {
return;
}
running = true;
thread = new Thread(this);
thread.setName("My Game");
thread.start();
}
public synchronized static void stop() {
if (!running) {
return;
}
running = false;
thread = null;
System.exit(1);
}
private void init() {
gameState.init();
}
private void update() {
gameState.update();
}
private void render() {
BufferStrategy bs = this.getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setColor(defaultColor);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
// Draw Content
gameState.render(g);
// Draw to Screen;
g.dispose();
bs.show();
}
public static void setGameState(GameState state) {
gameState = state;
gameState.init();
}
}
KeyEventListener.class
public class KeyEventListener extends KeyAdapter {
public void keyPressed(KeyEvent e) {
if (!Game.keyPressed) {
Game.gameState.keyPressed(e);
}
Game.keyPressed = true;
}
public void keyReleased(KeyEvent e) {
Game.gameState.keyReleased(e);
Game.keyPressed = false;
}
}
MouseEventListener.class (Just for recording the mouse position)
public class MouseEventListener extends MouseAdapter {
public void mousePressed(MouseEvent e) {
Game.mouseDown = true;
}
public void mouseReleased(MouseEvent e) {
Game.mouseDown = false;
}
public void mouseMoved(MouseEvent e) {
Game.mouseX = e.getX();
Game.mouseY = e.getY();
}
public void mouseDragged(MouseEvent e) {
Game.mouseX = e.getX();
Game.mouseY = e.getY();
}
}
GameState.class
public abstract class GameState {
public abstract void init();
public abstract void update();
public abstract void render(Graphics g);
public void keyPressed(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
And my Play.class which is the current gameState
public class Play extends GameState {
public static int key;
public void init() {
}
public void update() {
}
public void render(Graphics g) {
g.drawString("Key: " + key, 100, 100);
}
public void keyPressed(KeyEvent e) {
key = e.getKeyCode();
}
public void keyReleased(KeyEvent e) {
}
}
KeyListener will only respond to key events when the component is registered to is focusable and has focus.
After you have set the window to full screen, try adding...
game.requestFocusInWindow();
You may also need to use game.setFocusable(true)
If it wasn't for the fact that you're using a Canvas, I'd suggest using the key bindings API to over all these focus issues
Updated
I added...
window.addWindowFocusListener(new WindowAdapter() {
#Override
public void windowGainedFocus(WindowEvent e) {
System.out.println("gainedFocus");
if (!game.requestFocusInWindow()) {
System.out.println("Could not request focus");
}
}
});
To the window after it was visible but before it was made full screen
Updated
Oh, you're going to love this...
Based on this question: FullScreen Swing Components Fail to Receive Keyboard Input on Java 7 on Mac OS X Mountain Lion
I added setVisible(false) followed by setVisible(true) after setting the window to full screen mode and it seems to have fixed it...
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(window);
window.setVisible(false);
window.setVisible(true);
Verified running on Mac, using Java 7
After execution, the loop completes its circles and then the repaint method is being called,
Is there an alternative way of drawing the image?
public class GameCanvas extends JPanel implements KeyListener {
private Tank tank1;
private Tank tank2;
private Rocket rocket;
public Graphics2D g2;
private Image img;
public boolean fire;
public GameCanvas() {
tank1 = new Tank(0, 0);
tank2 = new Tank(500, 500);
rocket = new Rocket(50, tank1);
init();
fire = false;
}
public void init() {
JFrame frame = new JFrame("Topak Tank");
frame.setLayout(new FlowLayout());
frame.setSize(1300, 740);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
this.setPreferredSize(new Dimension(1300, 740));
frame.add(this);
frame.addKeyListener(tank1);
frame.addKeyListener(this);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
tank1.draw(g);
// rocket.draw(g);
g2 = (Graphics2D) g;
if (fire == true) {
try {
img = ImageIO.read(getClass().getResource("rocket.png"));
System.out.println(rocket.getYPos());
g2.drawImage(img, rocket.getXPos(), rocket.getYPos(), null);
} catch (Exception e) {
}
}
repaint();
}
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_F: {
try {
int xPos = tank1.getXAxis();
int yPos = tank1.getYAxis();
fire = true;
int delay = 10000;
// img = ImageIO.read(getClass().getResource("rocket.png"));
int count = 0;
while (true) {
System.out.println(xPos);
System.out.println(yPos);
if (count == 5) {
break;
}
count++;
// g2.drawImage(img,xPos,yPos,null);
rocket.setYPos(yPos);
System.out.println("loop");
Timer t = new Timer(delay, null);
t.start();
yPos++;
this.repaint();// here i want to excute and draw image after every lop cycle
Thread.sleep(100);
}
System.out.println("Fired");
fire = false;
} catch (Exception io) {
JOptionPane.showMessageDialog(null, "Could not found Rocket image");
}
}
break;
}
}
public void keyReleased(KeyEvent ke) {
}
public void keyTyped(KeyEvent ke) {
}
public static void main(String[] arg) {
GameCanvas gc = new GameCanvas();
}
}
Calling repaint() just tells the component to repaint itself whenever the EDT is available again. Since that entire loop is on the EDT, the component can't repaint itself until it's done.
You should probably be using a Timer or a Thread instead of tying up the EDT with that loop. You should NOT be calling sleep() on the EDT.
More info here: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/
how i can separate the logic from the EDT,because as you can see logic and render are related into loop
and how i can separate code in a pretty way, i' m getting in trouble with this,trying to find out how to do.
Any suggestion/helps/advices?
main class
public class MainFrame {
private static MainFrame mainFrame = null;
private final JFrame frame;
private MainFrame() {
frame = new JFrame();
frame.setUndecorated(true);
frame.add(new GamePanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static final MainFrame getMainFrameInstance() {
if (mainFrame == null) {
mainFrame = new MainFrame();
}
return mainFrame;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {// set look and feel to nimbus
for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
MainFrame.getMainFrameInstance();
}
});
}
}
gamePanel class on which i start the game loop
public class MyPanel extends JPanel implements Runnable {
private static final long serialVersionUID = 1L;
// thread and loop
private Thread thread;
private boolean running;
private int FPS = 60;
private long targetTime = 1000 / FPS;
private long start;
private long elapsed;
private long wait;
// image
public BufferedImage image;
// foo
private Foo foo;
private Render render = Render.getRenderManagerInstance();
public MyPanel() {
setPreferredSize(new Dimension(700, 700));
setFocusable(true);
requestFocus();
}
public void addNotify() {
super.addNotify();
if (thread == null) {
thread = new Thread(this);
thread.start();
}
}
private void initGraphic() {
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
foo = new Foo();
running = true;
}
public void run() {
initGraphic();
// loop
while (running) {
start = System.nanoTime();
foo.update();
repaint();
elapsed = System.nanoTime() - start;
wait = (targetTime - elapsed / 1000000) - 8;
if (wait <= 0)
wait = 6;
try {
Thread.sleep(wait);
} catch (Exception e) {
e.printStackTrace();
}
}
}
#Override
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
graphics = (Graphics2D) image.getGraphics();
((Graphics2D) graphics).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
((Graphics2D) graphics).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
render.setRenderState((Graphics2D) graphics);
}
Your gamePanel should not enter an infinite loop. It should initialize itself and define a method to be called periodically by a Swing timer. More generally, whenever you do something periodically, you should not try to keep track of time, but instead define what should be done during a single step and let a timer call you. This makes a much better use of system resources and is a good engineering practice.