I am rendering a model on canvas with opengl, therefore using a while loop and I run it under a thread not to disturb the main thread. I also have a text Field where I want to write some text. The text field focuses to my action and takes input but when hovering through the canvas, the text field focuses but I am not able to write something on it. This may be because of the the while loop but I do not know how to overcome this. I want the ui to remain responsive.
Here is my code:
import java.awt.Canvas;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JTextField;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.PixelFormat;
public final class Loader extends JFrame {
public final int FPS = 120; // better cause of animations
public static Loader loader;
public Canvas canvas;
private Viewport viewport;
private JTextField textField;
private Loader() {
setTitle("Test");
setResizable(true);
setSize(320, 285);
setLocationRelativeTo(null);
getContentPane().setLayout(null);
setVisible(true);
canvas = new Canvas();
getContentPane().add(canvas);
canvas.setBounds(10, 24, 280, 163);
textField = new JTextField();
textField.setColumns(10);
textField.setBounds(91, 193, 116, 22);
getContentPane().add(textField);
}
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
// Initialize the frame
loader = new Loader();
new Thread(new Runnable() {
#Override
public void run() {
try {
// render the model
loader.render();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
private static void init(Canvas canvas) throws Exception {
Display.setParent(canvas);
try {
Display.create(new PixelFormat(8, 24, 8, 8));
} catch (Exception ex) {
Display.create(new PixelFormat(8, 24, 8, 0));
}
Display.makeCurrent();
Display.setVSyncEnabled(false);
Display.setSwapInterval(0);
}
private void render() throws Exception {
init(canvas);
loader.viewport = new Viewport(canvas);
long[] timerCache = new long[10];
long timerCur = 0L;
long timerDst = 0L;
long timerLast = 0L;
long timerRate = 1000000L * 1000L / FPS;
int timerCacheIndex = 0;
int timerCacheSize = 0;
boolean minSleep = Runtime.getRuntime().availableProcessors() <= 1;
long[] clockCache = new long[32];
int clockIndex = 0;
int fps = 0;
while (isVisible()) {
long clock = System.nanoTime();
long lastClock = clockCache[clockIndex];
clockCache[clockIndex] = clock;
if (++clockIndex == 32)
clockIndex = 0;
if (lastClock != 0L && clock > lastClock) {
fps = (int) (1000000L * 1000L * 32L / (clock - lastClock));
}
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GL11.glClearColor(.5f, .6f, .9f, 1f);
loader.viewport.render();
if (minSleep)
try {
Thread.sleep(1L);
} catch (Exception ex) {
}
timerCache[timerCacheIndex++] = System.nanoTime();
if (timerCacheSize < timerCacheIndex)
timerCacheSize = timerCacheIndex;
if (timerCacheIndex == 10)
timerCacheIndex = 0;
long time = 0L;
for (int i = 0; i != timerCacheSize; time += timerCache[i++])
;
time /= (long) timerCacheSize;
if (timerCacheSize == 1)
timerLast = time;
timerCur += time - timerLast;
timerLast = time;
timerDst += timerRate;
if (timerDst > timerCur) {
long sleep = timerDst - timerCur;
try {
Thread.sleep(sleep / 1000000L, (int) (sleep % 1000000L));
} catch (Exception ex) {
}
timerCur = timerDst;
for (int i = 0; i != timerCacheSize; ++i)
timerCache[i] += sleep;
timerLast += sleep;
}
}
}
}
This gif may help know how it currently looks.
https://gyazo.com/e6950fd8dd01306c704857e94f214f93.gif
I don't see where is the mistake in here, I already use a thread for the render method.
Edit:
Viewport.java
public Viewport(Canvas canvas) throws LWJGLException {
this.canvas = canvas;
this.scale = 1.0F;
this.pitch = 180.0F;
this.yaw = 0.0F;
this.roll = 0.0F;
}
public void render() {
try {
Dimension size = canvas.getSize();
if (models != null && size.width > 0 && size.height > 0) {
if (width != size.width || height != size.height) {
width = size.width;
height = size.height;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float c = (float) Math.sqrt((double) (width * width) + (double) (height * height));
glOrtho(0.0F, (float) width, 0.0F, (float) height, -c, c);
glMatrixMode(GL_MODELVIEW);
}
if (Mouse.isButtonDown(0) && !Mouse.isButtonDown(1)) {
yaw -= (float) Mouse.getDX() / 2;
pitch -= (float) Mouse.getDY() / 2;
}
if (Mouse.isButtonDown(0) && Mouse.isButtonDown(1)) {
offset_z += (float) Mouse.getDY();
}
float wheel = (float) Mouse.getDWheel() / 1800.0F;
if (wheel > 1.0F)
wheel = 1.0F;
else if (wheel < -1.0F)
wheel = -1.0F;
scale += scale * wheel;
if (scale < 0.01F)
scale = 0.01F;
for (Model3D model : models) {
float x = (float) width / 2.0F;
float y = ((float) -((100.0F * (scale))) + offset_z) + (float) (height - model.height()) / 2.0F;
float z = 0.0F;
model.render(model, x, y, z, pitch, yaw, roll, scale, scale, scale);
}
Display.update();
}
} catch (Throwable t) {
}
}
Instead of:
new Thread(new Runnable() {
#Override
public void run() {
try {
// render the model
loader.render();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
maybe:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
// render the model
loader.render();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
It will run at AWT dispatching thread, maybe the issue will fade. Swing not being thread safe sometimes makes life hard.
Can you try adding requestFocus() to the end of your render() method of your Loader class and report back what happens?
Related
Is there any way to efficiently move an object from point A to point B without having to use an inefficient While(true) loop? I tried to repaint Draw class, but it did not help with the performance issues. I am unable to trace the root of the huge performance issue. I tried to research moving entities on Google, GitHub, and others and found nothing useful. Any help is appreciated!
Code:
class Draw extends JPanel {
public static int x;
public static int y;
public static int randx;
public static int randy;
public void paintComponent(Graphics g) {
super.paintComponent(g);
BufferedImage image = null;
BufferedImage image1 = null;
try {
image = ImageIO.read(new File("Small Sheep Icon.png"));
image1 = ImageIO.read(new File("Small Sheep Icon.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
g.drawImage(image, x, y, null);
g.drawImage(image1, randx, randy, null);
// g.drawString("Hello, World", x, y);
}
}
public static void sheepmove() {
double starttime = System.nanoTime();
double previoustime = 0;
double waittime = 0;
boolean xright = true;
boolean waitswitch = false;
int counter = 0;
while(true) {
double elapsedtime = (System.nanoTime() - starttime)/(1000000000);
if (elapsedtime-waittime >= 1 || waitswitch == false) {
if (waitswitch == true) {
waitswitch = false;
}
waittime = elapsedtime;
if (elapsedtime >= previoustime + 0.1) {
previoustime = elapsedtime;
if (xright == true) {
Draw.randx += 1;
}
if (Draw.randx - 200 >= 50) {
if (counter == 0) {
waitswitch = true;
xright = false;
}
// System.out.println("Moving south...");
Draw.randy += 1;
if (Draw.randy >= 300) {
Draw.randy -= 1;
break;
}
counter++;
}
frame.repaint();
// System.out.print("=");
// System.out.println("Previous Time: " + previoustime);
}
}
// System.out.println(elapsedtime);
// Thread.sleep(1000);
}
}
public static void main(String[] args) {
new Demo();
}
#Override
public void mouseDragged(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseMoved(MouseEvent e) {
// System.out.println("Mouse Moved!");
Draw.x = e.getX();
Draw.y = e.getY();
frame.repaint();
}
I'm currently working on an application which uses the draw function to animate "bouncing images" in a JPanel. To accomplish it I had to learn to use threads. When I used them in my code someone recommended that instead of using threads directly I can use stuff like Executor framework and ExecutorService.
Right now my problem is that when adding new images I need to make sure that they don't get created inside each other. When the program detects that they would be intersecting it should wait some amount of time, while all the other threads keep running, so images are still getting moved and drawn in current positions.
What happens however is that when I make one of the threads sleep to wait for a spot to be empty the whole program seems to freeze. The only thing that seems to be running is the image moving function.
Code in a Github Gist
Here is the code:
This is the BouncingImages class
/* NOTE: requires MyImage.java */
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.swing.*;
public class BouncingImages extends JFrame implements ActionListener {
public static void main(String[] args) {
new BouncingImages();
}
static boolean imagesLoaded = true;
JPanel resetPanel = new JPanel();
JPanel runningPanel = new JPanel();
JPanel pausedPanel = new JPanel();
JPanel btnPanel = new JPanel();
public static AnimationPanel animationPanel;
ArrayList <MyImage> imageList = new ArrayList <MyImage>();
private volatile boolean stopRequested = false; //maybe should use AtomicBoolean
boolean isRunning = false;
boolean isReset = true;
ExecutorService service = Executors.newCachedThreadPool();
Future f;
//here is the part of the code responsible for creating a JFrame
BouncingImages() {
//set up button panel
JButton btnStart = new JButton("Start");
JButton btnResume = new JButton("Resume");
JButton btnAdd = new JButton("Add");
JButton btnAdd10 = new JButton("Add 10");
JButton btnStop = new JButton("Stop");
JButton btnReset = new JButton("Reset");
JButton btnExit = new JButton("Exit");
btnStart.addActionListener(this);
btnResume.addActionListener(this);
btnAdd.addActionListener(this);
btnAdd10.addActionListener(this);
btnStop.addActionListener(this);
btnReset.addActionListener(this);
btnExit.addActionListener(this);
resetPanel.add(btnStart);
runningPanel.add(btnAdd);
runningPanel.add(btnAdd10);
runningPanel.add(btnStop);
pausedPanel.add(btnResume);
pausedPanel.add(btnReset);
animationPanel = new AnimationPanel();
resetButtons();
this.add(btnPanel, BorderLayout.SOUTH);
this.add(animationPanel);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack(); //since the JPanel is controlling the size, we need pack() here.
this.setLocationRelativeTo(null); //after pack();
this.setVisible(true);
}
//I use different JPanels with designated buttons to make them display different buttons when the program is running, paused or completely restarted.
public void resetButtons() {
btnPanel.updateUI();
if (isReset) {
btnPanel.removeAll();
btnPanel.add(resetPanel, BorderLayout.SOUTH);
} else {
if (isRunning) {
btnPanel.removeAll();
btnPanel.add(runningPanel, BorderLayout.SOUTH);
}
if (!isRunning) {
btnPanel.removeAll();
btnPanel.add(pausedPanel, BorderLayout.SOUTH);
}
}
}
//ActionListener for Buttons
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("Start")) {
startAnimation();
isRunning = true;
isReset = false;
resetButtons();
}
if (e.getActionCommand().equals("Resume")) {
startAnimation();
isRunning = true;
resetButtons();
}
if (e.getActionCommand().equals("Add")) {
addImage();
}
if (e.getActionCommand().equals("Add 10")) {
for(int i = 0; i <10; i++) {
addImage();
startAnimation();
}
}
if (e.getActionCommand().equals("Stop")) {
pauseAnimation();
isRunning = false;
resetButtons();
}
if (e.getActionCommand().equals("Reset")) {
imageList.clear();
repaint();
isReset = true;
resetButtons();
}
if (e.getActionCommand().equals("Exit")) {
System.exit(0);
}
}
//This function starts all the animations using the Runnable AnimationThread
void startAnimation() {
//this starts the program
if (f == null) {
f = service.submit(new AnimationThread());
}
//this starts the program after it got paused
else if (f.isCancelled()) {
f = service.submit(new AnimationThread());
}
}
//this pauses all the animations
void pauseAnimation() {
f.cancel(true);
}
//here is the part of the code that I have problems with. I'm not sure how to make the program wait for a spot to be empty while all the other threads are running rather than pausing them all.
void addImage(){
int i = 0;
MyImage image;
while (true) {
image= new MyImage("image.png");
if (checkCollision(image)) {
if(i > 100){
System.out.println("Something went wrong");
System.exit(0);
}
try {
i++;
Thread.sleep(50);
} catch (InterruptedException e) {}
} else {
System.out.println("image added");
break;
}
}
imageList.add(image);
}
//this part of the program does all the image moving. It uses the function move image from the MyImage class and a checkCollision function from this class.
void moveAllImages() {
for (MyImage image : imageList) {
image.moveImage(animationPanel);
image.calculatePoints();
checkCollision(image);
}
}
//this checks all the collisions with other images. It can be used for checking if a image can be created in some spot and also for all the bouncing callculations
boolean checkCollision(MyImage currentImage){
if(imageList.isEmpty()) return false;
for(MyImage im : imageList){
if(currentImage == im) continue;
if(currentImage.intersects(im)){
if(im.contains(currentImage.ml) || im.contains(currentImage.mr)){
currentImage.undoMove();
currentImage.vx = -currentImage.vx;
return true;
}
}
if(im.contains(currentImage.mt) || im.contains(currentImage.mb)){
currentImage.undoMove();
currentImage.vy = - currentImage.vy;
return true;
}
if(currentImage.contains(im.ml) || currentImage.contains(im.mr)){
currentImage.undoMove();
currentImage.vx = -currentImage.vx;
return true;
}
if (im.contains(currentImage.tl) || im.contains(currentImage.tr) || im.contains(currentImage.bl) || im.contains(currentImage.br)) {
currentImage.undoMove();
currentImage.vx *= -1;
currentImage.vy *= -1;
return true;
}
}
return false;
}
//This class does drawing graphics and nothing else
public class AnimationPanel extends JPanel {
AnimationPanel() {
this.setBackground(Color.BLACK);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setPreferredSize(new Dimension((int) screenSize.getWidth() / 2, (int) screenSize.getHeight() / 2));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (MyImage im : imageList) {
g.drawImage(im.image, im.x, im.y, im.width, im.height, null);
}
}
}
//this is the Runnable AnimationThread which does all the image moving and repainting.
private class AnimationThread implements Runnable {
#Override
public void run() {
while (!f.isCancelled()) {
moveAllImages();
animationPanel.repaint();
try {
Thread.sleep(5);
}
catch (InterruptedException e) {
System.out.println(e.getMessage());
f.cancel(true);
}
}
}
}
}
This is the MyImage class
package bouncer;
import java.awt.Color;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
//NOTE: This class requires BouncingImages.java
/* This class combines an image with a rectangle
* which allows for easy movement and collision detection
*/
class MyImage extends Rectangle {
//this gives it an x,y,width,height
static int biggestDim = 0;
BufferedImage image;
Color color = Color.RED;
//these are the speeds of movement
int vx = 1;
int vy = 1;
int lastx = -1;
int lasty = -1;
Point ctr, tl, tr, bl, br, ml, mr, mt, mb;
MyImage(String filename) {
vx = (int) (Math.random() * 5 + 1);
vy = (int) (Math.random() * 5 + 1);
//load the image
try {
image = ImageIO.read(new File(filename));
width = image.getWidth(null);
height = image.getHeight(null);
}
catch (IOException e) {
System.out.println("ERROR: image file \"" + filename + "\" not found");
//e.printStackTrace();
BouncingImages.imagesLoaded = false;
width = 100 + (int) (Math.random() * 100);
height = width - (int) (Math.random() * 70);
color = Color.getHSBColor((float) Math.random(), 1.0f, 1.0f); // a quick way to get random colours
//System.exit(0);
}
//update the variable containing the biggest dimension
if (width > biggestDim) biggestDim = width;
if (height > biggestDim) biggestDim = height;
calculatePoints();
}
void calculatePoints() {
//Calculate points
//corners
tl = new Point(x, y);
tr = new Point(x + width, y);
bl = new Point(x, y + height);
br = new Point(x + width, y + height);
//center
ctr = new Point(x + width / 2, y + height / 2);
//mid points of sides
ml = new Point(x, y + height / 2);
mr = new Point(x + width, y + height / 2);
mt = new Point(x + width / 2, y);
mb = new Point(x + width / 2, y + height);
}
void moveImage(BouncingImages.AnimationPanel panel) {
lastx = x;
lasty = y;
x += vx;
y += vy;
if (x < 0 && vx < 0) {
x = 0;
vx = -vx;
}
if (y < 0 && vy < 0) {
y = 0;
vy = -vy;
}
if (x + width > panel.getWidth() && vx > 0) {
x = panel.getWidth() - width;
vx = -vx;
}
if (y + height > panel.getHeight() && vy > 0) {
y = panel.getHeight() - height;
vy = -vy;
}
}
void undoMove() {
if (lastx > 0) {
x = lastx;
y = lasty;
}
lastx = lasty = -1;
}
}
You are blocking the main thread in method addImage in Thread.sleep(50). Instead, you could ensure that you call method addImage asynchronously, e.g.
if (e.getActionCommand().equals("Add")) {
CompletableFuture.runAsync(this::addImage);
}
if (e.getActionCommand().equals("Add 10")) {
CompletableFuture.runAsync(() -> addImages(10));
}
with
private void addImages(int numberOfImages) {
for(int i = 0; i < numberOfImages; i++) {
addImage();
startAnimation();
}
}
If you want to experiment a bit more with threads, then you can think about how to do it "by hand".
im following a tutorial and im coming across this is the error, i cannot seem to work out what the problem is. All im trying to do is load a Sprite image. Here is the code:
Here is the error:
Exception in thread "Thread-0" java.lang.NullPointerException
at com.mainpkg.game.Handler.gg(Handler.java:27)
at com.mainpkg.game.Game.render(Game.java:107)
at com.mainpkg.game.Game.run(Game.java:83)
at java.base/java.lang.Thread.run(Thread.java:844)
Main Game Class:
public class Game extends Canvas implements Runnable {
public static final int WIDTH = 800, HEIGHT = WIDTH / 12 * 9;
private Thread thread;
private boolean running = false;
private Handler handler;
private BufferedImage grassTile;
public Game() {
new Window(WIDTH, HEIGHT, "MOON EXPOLATION", this);
handler = new Handler(getWidth(), getHeight());
testImage = loadImage("Resources/GrassTile.png");
}
private BufferedImage loadImage(String path) {
try {
BufferedImage loadedImage = ImageIO.read(new FileInputStream(path));
BufferedImage formattedImage = new BufferedImage(loadedImage.getWidth(), loadedImage.getHeight(),
BufferedImage.TYPE_INT_RGB);
formattedImage.getGraphics().drawImage(loadedImage, 0, 0, null);
return formattedImage;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public synchronized void start() {
thread = new Thread(this);
thread.start();
running = true;
}
public synchronized void stop() {
try {
thread.join();
running = false;
} catch (Exception e) {
e.printStackTrace();
}
}
public void run() {
int FRAMES = 0;
int TICKS = 0;
long lastTime = System.nanoTime();
double unprocessed = 0;
double nsPerSecs = 1000000000 / 60.0;
long Timer = System.currentTimeMillis();
while (running) {
long now = System.nanoTime();
unprocessed += (now - lastTime) / nsPerSecs;
lastTime = now;
if (unprocessed >= 1) {
TICKS++;
ticks();
unprocessed -= 1;
}
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
FRAMES++;
render();
if (System.currentTimeMillis() - Timer > 1000) {
System.out.println("Ticks: " + TICKS + " FPS: " + FRAMES);
TICKS = 0;
FRAMES = 0;
Timer += 1000;
}
}
stop();
}
private void ticks() {
}
void render() {
BufferStrategy bs = this.getBufferStrategy();
if (bs == null) {
super.paint(getGraphics());
this.createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
handler.renderImage(testImage, 0, 0);
handler.render(g);
// g.setColor(Color.BLACK);
// g.fillRect(0,0,WIDTH,HEIGHT);
g.dispose();
bs.show();
}
public static void main(String[] args) {
new Game();
}
Handler Class:
public class Handler {
private BufferedImage view;
private int pixels[];
public Handler(int width, int height) {
view = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
pixels = ((DataBufferInt) view.getRaster().getDataBuffer()).getData();
}
public void render(Graphics g) {
g.drawImage(view, 0, 0, view.getWidth(), view.getHeight(), null);
}
public void renderImage(BufferedImage image, int xPosition,int yPosition) {
int[] imagepixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
for(int y = 0; y < image.getHeight(); y++) {
for(int x = 0; x < image.getWidth(); x++) {
pixels[(x + xPosition) + (y + yPosition ) * view.getWidth()] = imagepixels[x + y * image.getWidth()];
}
}
}
The problem is here:
BufferedImage loadedImage = ImageIO.read(Game.class.getResource(path));
getResource(path) is returning null and that is causing the exception.
Try changing the image path to "Assets/GrassTile.png"
One tip:
You should avoid using absolute paths to locate your resources. This is a really bad idea because if you change the location of your project it will stop working, try using relative paths.
I'm trying to make an image move randomly but it's not working for some reason. The image appears in the panel but it's not moving. I'm using tow classes for this: a main class and a sub class. This is the code for my sub class.
public Image()
{
randomPosition();
try {
image = ImageIO.read(new File("image.png"));
} catch (IOException e) {}
}
public void paint(Graphics g)
{
g.drawImage(image, x, y, this);
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
public void move()
{
x += dx;
y += dy;
if (x >= 550) {
x = 550;
randomDirection();
}
if (x <= 1) {
x = 1;
randomDirection();
}
if (y >= 350) {
y = 350;
randomDirection();
}
if (y <= 1) {
y = 1;
randomDirection();
}
}
public void randomDirection() {
double speed = 2.0;
double direction = Math.random()*2*Math.PI;
dx = (int) (speed * Math.cos(direction));
dy = (int) (speed * Math.sin(direction));
}
public void randomPosition()
{
x = LEFT_WALL + (int) (Math.random() * (RIGHT_WALL - LEFT_WALL));
y = UP_WALL + (int) (Math.random() * (DOWN_WALL - UP_WALL));
}
public void run()
{
long beforeTime, timeDiff, sleep;
beforeTime = System.currentTimeMillis();
while (true)
{
move();
repaint();
timeDiff = System.currentTimeMillis() - beforeTime;
sleep = DELAY - timeDiff;
if (sleep > 2)
{
sleep = 1;
}
try
{
Thread.sleep(sleep);
}
catch (InterruptedException e)
{
System.out.println("interrupted");
}
beforeTime = System.currentTimeMillis();
}
}
and this is my main class where I start the thread:
public void addImage(){
Image I = new Image();
x = panel.getGraphics();
I.paint(x);
Thread thr=new Thread(I);
thr.start();
}
paint() should be
Like this:
public void paintComponent(Graphics g) {
super.paintComponent(g);
...
}
Never call paint()/paintComponent() directly. Instead call repaint(), this adds it to the event queue. The graphics object is passed by swing.
I'm not sure g.dispose() is necessary, and might cause problems.
Check the panel your painting into is sized correctly - a quick way to debug this is panel.setBorder(BorderFactory.createLineBorder(Color.RED));
Consider using a javax.swing.Timer instead of Thread. This isn't the cause of problem, but would be cleaner.
i'm trying to create a game. And almost everytime I move, it's leaving a trail
Code:
package lt.mchackers.gametest.main;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import lt.mchackers.gametest.handlers.InputHandler;
/**
* Main class for the game
*/
public class Main extends JFrame
{
private static final long serialVersionUID = -828018325337767157L;
boolean isRunning = true;
int fps = 30;
int windowWidth = 320;
int windowHeight = 320;
int speed;
BufferedImage backBuffer;
Insets insets;
InputHandler input;
int x = 0;
int y = 0;
int xa = 0;
int ya = 0;
Coordinates coords = new Coordinates(0, 0);
public static void main(String[] args)
{ Main game = new Main();
game.run();
System.exit(0);
}
/**
* This method starts the game and runs it in a loop
*/
public void run()
{
initialize();
while(isRunning)
{
long time = System.currentTimeMillis();
update();
draw();
// delay for each frame - time it took for one frame
time = (1000 / fps) - (System.currentTimeMillis() - time);
if (time > 0)
{
try
{
Thread.sleep(time);
}
catch(Exception e){}
}
}
setVisible(false);
}
/**
* This method will set up everything need for the game to run
*/
void initialize()
{
setTitle("Game Tutorial");
setSize(windowWidth, windowHeight);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
insets = getInsets();
setSize(insets.left + windowWidth + insets.right,
insets.top + windowHeight + insets.bottom);
backBuffer = new BufferedImage(windowWidth, windowHeight, BufferedImage.TYPE_INT_RGB);
input = new InputHandler(this);
Graphics bbg = backBuffer.getGraphics();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("map"));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
String line = null;
try {
BufferedImage gray = ImageIO.read(new File("gray.png"));
BufferedImage black = ImageIO.read(new File("black.png"));
while ((line = reader.readLine()) != null) {
for(String s : line.split(""))
{
if (s.contains("*"))
{
bbg.drawImage(gray, xa-32, ya, null);
}
else if (s.contains("#"))
{
bbg.drawImage(black, xa-32, ya, null);
}
if (xa < 320)
{
xa += 32;
}
else
{
ya += 32;
xa = 0;
}
System.out.println(xa);
System.out.println(ya);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* This method will check for input, move things
* around and check for win conditions, etc
*/
void update()
{
if (input.isKeyDown(KeyEvent.VK_NUMPAD0))
{
speed -= 1;
}
if (input.isKeyDown(KeyEvent.VK_NUMPAD1))
{
speed += 1;
}
if (input.isKeyDown(KeyEvent.VK_RIGHT))
{
coords.setCoords(coords.getX() + 32, coords.getY());
}
if (input.isKeyDown(KeyEvent.VK_LEFT))
{
coords.setCoords(coords.getX() - 32, coords.getY());
}
if (input.isKeyDown(KeyEvent.VK_UP))
{
coords.setCoords(coords.getX(), coords.getY() - 32);
}
if (input.isKeyDown(KeyEvent.VK_DOWN))
{
coords.setCoords(coords.getX(), coords.getY() + 32);
}
//System.out.println(x);
//System.out.println(y);
//System.out.println(speed);
if (coords.getY() < 0)
{
coords.setCoords(coords.getX(), 0);
}
if (coords.getX() < 0)
{
coords.setCoords(0, coords.getY());
}
if (coords.getX() > windowWidth - 32)
{
coords.setCoords(windowWidth - 32, coords.getY());
}
if (coords.getY() > windowHeight - 32)
{
coords.setCoords(coords.getX(), windowHeight - 32);
// y = windowHeight - 32;
}
}
/**
* This method will draw everything
*/
void draw()
{
Graphics g = getGraphics();
//this.setBackground(Color.BLACK);
//super.paintComponents(g);
backBuffer.setRGB(x, y, Color.BLACK.getRGB());
Graphics bbg = backBuffer.getGraphics();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("map"));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
String line = null;
try {
BufferedImage gray = ImageIO.read(new File("gray.png"));
BufferedImage black = ImageIO.read(new File("black.png"));
while ((line = reader.readLine()) != null) {
for(String s : line.split(""))
{
if (s.contains("*") && xa + 32!= coords.getX() && ya - 32 != coords.getY())
{
bbg.drawImage(gray, xa-32, ya, null);
}
else if (s.contains("#") && xa + 32 != coords.getX() && ya - 32 != coords.getY())
{
bbg.drawImage(black, xa-32, ya, null);
}
if (xa < 320)
{
xa += 32;
}
else
{
ya += 32;
xa = 0;
}
//System.out.println(xa);
//System.out.println(ya);
}
}
} catch (IOException e) {
e.printStackTrace();
}
bbg.setColor(Color.WHITE);
xa = 0;
ya = 0;
System.out.println(coords.getX());
bbg.setColor(Color.WHITE);
bbg.fillRect(coords.getX(),coords.getY(), 32,32);
System.out.println(coords.getY());
//bbg.setColor(Color.BLACK);
//bbg.drawOval(x, y, 20, 20);
g.drawImage(backBuffer, insets.left, insets.top, this);
}
}
Thanks for help.
Check out my code from here, to get a hint as to how to paint stuff in a game loop properly.
Basically what you need to take care of is double buffering to prevent any flickering and also repainting the background so that you don't leave out any trail of rectangles.
You can also check out the Killer Game Programming in Java online book, which can help you learn the game programming concepts and their implementation.
The concept of double buffering is simple. Since painting on the screen takes more time than updating the states of the objects in the gameplay, we use two canvas to prevent any flickering issues which arise when objects are painted directly on the screen.
When the object states are updated in the game loop, it is rendered in a background canvas. The background canvas is then copied to the screen which takes less time compared to painting directly on the screen. And while this copying is happening, the object states are updated again and they are rendered on the background canvas again, which is then copied to the screen. Repeat this over and over, and you get the double buffering.
Basically you keep a buffer of screen which is to be painted and your game renders objects in the buffer which is then copied to the screen.
Here's a code which I think might help you understand the concept:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class GamePanel extends JPanel implements Runnable
{
private static final long serialVersionUID = 6892533030374996243L;
public static final int WIDTH = 800;
public static final int HEIGHT = 600;
private Thread animator;
private volatile boolean running = false;
private volatile boolean isGameOver = false;
private volatile boolean isUserPaused = false;
private volatile boolean isWindowPaused = false;
private Graphics dbg;
private Image dbImage = null;
private static final int NO_DELAYS_PER_YIELD = 16;
private static final int MAX_FRAME_SKIPS = 5;
private static final Color backgroundColor = new Color(245, 245, 245);
private static long fps = 30;
private static long period = 1000000L * (long) 1000.0 / fps;
private static volatile boolean isPainted = false;
public GamePanel()
{
setBackground(backgroundColor);
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setFocusable(true);
requestFocus();
readyForPause();
// Add key listeners here...
}
public void addNotify()
{
super.addNotify();
startGame();
}
void startGame()
{
if (animator == null || !running)
{
animator = new Thread(this);
animator.start();
}
}
void stopGame()
{
running = false;
}
private void readyForPause()
{
addKeyListener(new KeyAdapter()
{
public void keyPressed(KeyEvent e)
{
int keyCode = e.getKeyCode();
if ((keyCode == KeyEvent.VK_ESCAPE) || (keyCode == KeyEvent.VK_Q)
|| (keyCode == KeyEvent.VK_END) || (keyCode == KeyEvent.VK_P)
|| ((keyCode == KeyEvent.VK_C) && e.isControlDown()))
{
if (!isUserPaused)
setUserPaused(true);
else
setUserPaused(false);
}
}
});
}
// This is the game loop. You can copy-paste it even in your own code if you want to.
public void run()
{
long beforeTime, afterTime, timeDiff, sleepTime;
long overSleepTime = 0L;
int noDelays = 0;
long excess = 0L;
beforeTime = System.nanoTime();
running = true;
while (running)
{
requestFocus();
gameUpdate();
gameRender();
paintScreen();
afterTime = System.nanoTime();
timeDiff = afterTime - beforeTime;
sleepTime = (period - timeDiff) - overSleepTime;
if (sleepTime > 0)
{
try
{
Thread.sleep(sleepTime / 1000000L);
}
catch (InterruptedException e)
{
}
overSleepTime = (System.nanoTime() - afterTime - sleepTime);
}
else
{
excess -= sleepTime;
overSleepTime = 0L;
if (++noDelays >= NO_DELAYS_PER_YIELD)
{
Thread.yield();
noDelays = 0;
}
}
beforeTime = System.nanoTime();
int skips = 0;
while ((excess > period) && (skips < MAX_FRAME_SKIPS))
{
excess -= period;
gameUpdate();
skips++;
}
isPainted = true;
}
System.exit(0);
}
private void gameUpdate()
{
if (!isUserPaused && !isWindowPaused && !isGameOver)
{
// Update the state of your game objects here...
}
}
private void gameRender()
{
if (dbImage == null)
{
dbImage = createImage(WIDTH, HEIGHT);
if (dbImage == null)
{
System.out.println("Image is null.");
return;
}
else
dbg = dbImage.getGraphics();
}
dbg.setColor(backgroundColor);
dbg.fillRect(0, 0, WIDTH, HEIGHT);
// Render your game objects here....
// like: xyzObject.draw(dbg);
// or dbg.drawOval(...);
if (isGameOver)
gameOverMessage(dbg);
}
private void gameOverMessage(Graphics g)
{
// Paint a game over message here..
}
private void paintScreen()
{
Graphics g;
try
{
g = this.getGraphics();
if ((g != null) && (dbImage != null))
g.drawImage(dbImage, 0, 0, null);
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
catch (Exception e)
{
System.out.println("Graphics context error : " + e);
}
}
public void setWindowPaused(boolean isPaused)
{
isWindowPaused = isPaused;
}
public void setUserPaused(boolean isPaused)
{
isUserPaused = isPaused;
}
}
Then you can simply add your game panel to your JFrame like following:
import java.awt.GridBagLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
public class GameFrame extends JFrame
{
private static final long serialVersionUID = -1624735497099558420L;
private GameFrame gamePanel = new GamePanel();
public GameFrame()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Game");
addWindowListener(new FrameListener());
getContentPane().setLayout(new GridBagLayout());
getContentPane().add(gamePanel);
pack();
setLocationRelativeTo(null);
setResizable(false);
setVisible(true);
}
public class FrameListener extends WindowAdapter
{
public void windowActivated(WindowEvent we)
{
gamePanel.setWindowPaused(false);
}
public void windowDeactivated(WindowEvent we)
{
gamePanel.setWindowPaused(true);
}
public void windowDeiconified(WindowEvent we)
{
gamePanel.setWindowPaused(false);
}
public void windowIconified(WindowEvent we)
{
gamePanel.setWindowPaused(true);
}
public void windowClosing(WindowEvent we)
{
gamePanel.stopGame();
}
}
public static void main(String args[])
{
new GameFrame();
}
}