I have a problem in regards to parallelism in javafx. More specifically, dividing the canvas of my program to run with multiple threads. When compiled, the program glitches out and doesn't work as supposed to. The code in the thread class is supposed to print out the Mandelbrot set, and the code in the application class is dividing the canvas into different parts that each should run in parallel. Here is the code:
public void MandelbrotSet(int n) {
int widthh = (int)canvas.getWidth();
int portion = widthh / n;
for (int i = 0; i < n; i++) {
int startX = i * portion;
int endX = startX + portion;
myMandelbrotParallelTEST2 thread = new myMandelbrotParallelTEST2(startX, endX, image, width, height, maximumIterations, canvas, zoom, xPos, yPos, hue, brightness, saturation, R, G, B);
thread.start();
//would add thread.join() here with try and catch
}
canvas.getGraphicsContext2D().drawImage(image, 0, 0);
}
this is how the code in the Thread class looks:
#Override
public void run(){
double centerY = canvas.getWidth() / 2.0;
double centerX = canvas.getHeight() / 2.0;
for (int x = start; x < end; x++) {
for (int y = 0; y < canvas.getHeight(); y++) {
double cr = xPos / width + (x - centerY) / zoom;
double ci = yPos / height + (y - centerX) / zoom; //getting position of the points on the canvas
int iterationsOfZ = 0;
double zr = 0.0;
double zi = 0.0;
while (iterationsOfZ < maximumIterations && (zr * zr) + (zi * zi) < 4) {
double oldZr = zr;
zr = (zr * zr) - (zi * zi) + cr;
zi = 2 * (oldZr * zi) + ci;
iterationsOfZ++;
}
int iterations = iterationsOfZ;
if (iterations == maximumIterations) { //inside the set
imagee.getPixelWriter().setColor(x, y, Color.rgb(R, G, B));
} else if (brightness == 0.9) { //white background
imagee.getPixelWriter().setColor(x, y, Color.hsb(hue, iterations / maximumIterations, brightness));
} else if (hue == 300) { //colorful background
imagee.getPixelWriter().setColor(x, y, Color.hsb(hue * iterations / maximumIterations, saturation, brightness));
} else if (hue == 0 && saturation == 0 && brightness == 1) {
imagee.getPixelWriter().setColor(x, y, Color.hsb(hue, saturation, brightness));
} else { //black background
imagee.getPixelWriter().setColor(x, y, Color.hsb(hue, saturation, iterations / brightness));
}
}
}
//canvas.getGraphicsContext2D().drawImage(imagee, 0, 0);
}
[this is how the program looks, and while panning around it glitches even more, but the execution time is really fast, which means that it is ran concurrently.]enter code here(https://i.stack.imgur.com/IZUfY.png)
I found that adding thread.join() fixes the problem, but then the program is ran on a single thread. Could anyone help me out resolve this issue?
What you are trying to do cannot be done with a Canvas because of the fx application thread restiction which has already been mentioned.
But it can be done via the WritableImage of JavaFX 13+ with support for Buffers. Writing into the buffer is not restricted in the above way and can thus be done in parallel with high performance.
I have compiled some examples (not any parallel ones) of how to use WritableImages with Buffers here: https://github.com/mipastgt/JFXToolsAndDemos
Related
Let's first start off with what I am trying to do. I would like to be able to take PNG file with a transparent background and find anywhere from 90 to 360 points along the edge of the subject of the image. Here is a rough example of what I mean. Given this image of Mario and Yoshi:
I want to make a circle that is centered at the center of the image with a diameter slightly larger than the largest side of the image to serve as a reference. Then, I want to go around the circle at set intervals, and trace a line towards the center until it hits a non-transparent pixel. Here is what that would look like:
I have attempted to implement this a few different times, all of which failed, and I was hoping to get some guidance or insight as to what I am doing wrong. Here is an image of the math I am using behind the code (sorry if the quality is not great, I don't have a scanner):
The Line 1 is either the top, bottom, left or right edge of the image, and Line 2 goes through the center of the circle at the given angle. The point where lines 1 and 2 intersect should be on the edge of the image, and is where we should start looking for the edge of the image's subject.
Here is the code that I came up with from this idea. I did it in Java because BufferedImage is really easy to use, but I am going to translate this over to C# (XNA) for the final product.
public class Mesh {
private int angleA, angleB, angleC, angleD;
private BufferedImage image;
private Point center;
public ArrayList<Point> points = new ArrayList<>();
public Mesh(BufferedImage image) {
center = new Point(image.getWidth() / 2, image.getHeight() / 2);
angleA = (int) (Math.atan(center.y / center.x) * (180 / Math.PI));
angleB = 180 - angleA;
angleC = 180 + angleA;
angleD = 360 - angleA;
this.image = image;
for(int angle = 0; angle <= 360; angle+=4){
Point point = getNext(angle);
if(point != null) points.add(point);
}
}
private Point getNext(int angle) {
double radians = angle * Math.PI / 180;
double xStep = Math.cos(radians);
double yStep = Math.sin(radians);
int addX = angle >= 90 && angle <= 270 ? 1 : -1;
int addY = angle >= 0 && angle <= 180 ? 1 : -1;
double x, y;
if (xStep != 0) {
double slope = yStep / xStep;
double intercept = center.y - (slope * center.x);
if (angle >= angleA && angle <= angleB) {
y = 0;
x = -intercept / slope;
} else if (angle > angleB && angle < angleC) {
x = 0;
y = intercept;
} else if (angle >= angleC && angle <= angleD) {
y = image.getHeight() - 1;
x = (y - intercept) / slope;
} else {
x = image.getWidth() - 1;
y = slope * x + intercept;
}
} else {
x = center.x;
y = angle <= angleB ? 0 : image.getHeight();
}
if (x < 0) x = 0;
if (x > image.getWidth() - 1) x = image.getWidth() - 1;
if (y < 0) y = 0;
if (y > image.getHeight() - 1) y = image.getHeight() - 1;
double distance = Math.sqrt(Math.pow(x - center.x, 2) + Math.pow(y - center.y, 2));
double stepSize = Math.sqrt(Math.pow(xStep, 2) + Math.pow(yStep, 2));
int totalSteps = (int) Math.floor(distance / stepSize);
for (int step = 0; step < totalSteps; step++) {
int xVal = (int) x;
int yVal = (int) y;
if(xVal < 0) xVal = 0;
if(xVal > image.getWidth() -1) xVal = image.getWidth() -1;
if(yVal < 0) yVal = 0;
if(yVal > image.getHeight()-1) yVal = image.getHeight() -1;
int pixel = image.getRGB(xVal, yVal);
if ((pixel >> 24) == 0x00) {
x += (Math.abs(xStep) * addX);
y += (Math.abs(yStep) * addY);
} else {
return new Point(xVal, yVal);
}
}
return null;
}
}
The algorithm should be returning all positive points that are all ordered in counterclockwise rotation (and non-overlapping) but I have failed to get the desired output (this being my most recent attempt) so just to restate the question, is there a formalized way of doing this, or can someone find the mistake I made in my logic. For visual reference, the Mario and Yoshi Traced image is sort of what the final output should look like, but with many more points (which would give more detail to the mesh).
I am attempting to make a rendering system with a depth map involved with the usual pixels for dealing with alpha. My problem is that no color is being set correctly! I have tried to debug using System.out.println and testing various components, but to no avail I have not found a solution.
The Variables
The variables that are involved with dealing with drawing, setting, and clearing of pixels are: private int[][] node, private int[] pixels, and private ArrayList<Integer> changedPixels.
private int[][] node deals with storing pixels and dealing with depth [depth][x + y * width] before transferring over to the BufferedImage pixels. The data is set to a clear black and the lowest depth it is a fully visible black.
private int[] pixels is the data from a BufferedImage to change it up, it is the only image every used! All data is by default fully visible black
private ArrayList<Integer> changedPixels deals with pixels that are there from the last frame so as to help boost FPS by not clearing the entire screen if not needed. Empty by default since not pixels were changed from a previous frame.
The Methods
I have several methods for the rendering system: setNode(int x, int y, int z, int color, int alpha, drawScreen(), and clearScreen(). I also have a drawing rectangle and sprite function which deals with adding pixels by calling the setNode() method to add in colors.
private void setNode(int x, int y, int z, int color, float alpha)
{
color = Pixel.getColor(alpha, color);
if (translate) // Move the pixel to the correct location
{
x -= transX;
y -= transY;
}
if (x < 0 || x >= width || y < 0 || y >= height || alpha <= 0.0f || nodeMap[z][x + y * width] == color) // Check if we need to draw the pixel
return;
for (int zz = z + 1; zz < maxDepth; zz++)
if (Pixel.getAlpha(nodeMap[zz][x + y * width]) >= 1)
return;
if (alpha < 1.0f) // If pixel isn't completely opaque, then set it's alpha to the given one
if (nodeMap[z][x + y * width] != color) // If color isn't equal to the one we supply, change it up correctly
color = Pixel.getColorBlend(color, nodeMap[z][x + y * width]);
if (color == Pixel.WHITE) System.out.println("Pixel is white at x: " + x + ", y: " + y);
nodeMap[z][x + y * width] = color;
}
public void drawScreen()
{
int color = clearColor;
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
{
for (int z = maxDepth - 1; z > 0; z--)
{
if (Pixel.getAlpha(nodeMap[z][x + y * width]) > 0f)
color = Pixel.getColorBlend(color, nodeMap[z][x + y * width]);
if (Pixel.getAlpha(color) >= 1f)
break;
}
if (pixels[x + y * width] != color)
{
pixels[x + y * width] = color;
changedPixels.add(x + y * width);
}
}
}
public void clearScreen()
{
for (Integer pixel : changedPixels)
{
for (int z = 0; z < maxDepth; z++)
{
if (z > 0)
nodeMap[z][pixel] = clearColor;
else
nodeMap[z][pixel] = bgColor;
}
}
changedPixels.clear();
}
public void drawRect(int offX, int offY, int z, int width, int height, int color)
{
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
setNode(x + offX, y + offY, z, color);
}
public static int getColorBlend(int color1, int color2)
{
float a1 = getAlpha(color1);
float a2 = getAlpha(color2);
float a = Math.max(a1, a2);
float r = ((getRed(color1) * a1) + (getRed(color2) * a2 * (1 - a1))) / a;
float g = ((getGreen(color1) * a1) + (getGreen(color2) * a2 * (1 - a1))) / a;
float b = ((getBlue(color1) * a1) + (getBlue(color2) * a2 * (1 - a1))) / a;
return Pixel.getColor(a, r, g, b);
}
The Test
What I do currently is initalize the rendering system and set the nodeMap and pixel map to the previously mentioned settings. After this has been completed a game engine begins and then a method in a gui button (you might need it), but it calls drawRect(0(x), 0(y), 1(z), 100(width), 20(height), Pixel.WHITE(color)) which works as I have testing to see if it's running the method and which pixels it's drawing to.
The Problem
The overall problem is that the screen is completely white, I can't quite figure out the reason! I do know it has nothing with the alpha blending, which works fine as I have used it will a previous version of a rendering system I did.
Any help is appreciate and sorry that this is quite a long question, I just wanted to make sure you had everything you may need help me solve this. I do realize this is not be very effiecent, but I still like the system. Thanks again!
I am trying to find image in an image. I do this for desktop automation. At this moment, I'm trying to be fast, not precise. As such, I have decided to match similar image solely based on the same average color.
If I pick several icons on my desktop, for example:
And I will search for the last one (I'm still wondering what this file is):
You can clearly see what is most likely to be the match:
In different situations, this may not work. However when image size is given, it should be pretty reliable and lightning fast.
I can get a screenshot as BufferedImage object:
MSWindow window = MSWindow.windowFromName("Firefox", false);
BufferedImage img = window.screenshot();
//Or, if I can estimate smaller region for searching:
BufferedImage img2 = window.screenshotCrop(20,20,50,50);
Of course, the image to search image will be loaded from template saved in a file:
BufferedImage img = ImageIO.read(...whatever goes in there, I'm still confused...);
I explained what all I know so that we can focus on the only problem:
Q: How can I get average color on buffered image? How can I get such average color on sub-rectangle of that image?
Speed wins here. In this exceptional case, I consider it more valuable than code readability.
I think that no matter what you do, you are going to have an O(wh) operation, where w is your width and h is your height.
Therefore, I'm going to post this (naive) solution to fulfil the first part of your question as I do not believe there is a faster solution.
/*
* Where bi is your image, (x0,y0) is your upper left coordinate, and (w,h)
* are your width and height respectively
*/
public static Color averageColor(BufferedImage bi, int x0, int y0, int w,
int h) {
int x1 = x0 + w;
int y1 = y0 + h;
long sumr = 0, sumg = 0, sumb = 0;
for (int x = x0; x < x1; x++) {
for (int y = y0; y < y1; y++) {
Color pixel = new Color(bi.getRGB(x, y));
sumr += pixel.getRed();
sumg += pixel.getGreen();
sumb += pixel.getBlue();
}
}
int num = w * h;
return new Color(sumr / num, sumg / num, sumb / num);
}
There is a constant time method for finding the mean colour of a rectangular section of an image but it requires a linear preprocess. This should be fine in your case. This method can also be used to find the mean value of a rectangular prism in a 3d array or any higher dimensional analog of the problem. I will be using a gray scale example but this can be easily extended to 3 or more channels simply by repeating the process.
Lets say we have a 2 dimensional array of numbers we will call "img".
The first step is to generate a new array of the same dimensions where each element contains the sum of all values in the original image that lie within the rectangle that bounds that element and the top left element of the image.
You can use the following method to construct such an image in linear time:
int width = 1920;
int height = 1080;
//source data
int[] img = GrayScaleScreenCapture();
int[] helperImg = int[width * height]
for(int y = 0; y < height; ++y)
{
for(int x = 0; x < width; ++x)
{
int total = img[y * width + x];
if(x > 0)
{
//Add value from the pixel to the left in helperImg
total += helperImg[y * width + (x - 1)];
}
if(y > 0)
{
//Add value from the pixel above in helperImg
total += helperImg[(y - 1) * width + x];
}
if(x > 0 && y > 0)
{
//Subtract value from the pixel above and to the left in helperImg
total -= helperImg[(y - 1) * width + (x - 1)];
}
helperImg[y * width + x] = total;
}
}
Now we can use helperImg to find the total of all values within a given rectangle of img in constant time:
//Some Rectangle with corners (x0, y0), (x1, y0) , (x0, y1), (x1, y1)
int x0 = 50;
int x1 = 150;
int y0 = 25;
int y1 = 200;
int totalOfRect = helperImg[y1 * width + x1];
if(x0 > 0)
{
totalOfRect -= helperImg[y1 * width + (x0 - 1)];
}
if(y0 > 0)
{
totalOfRect -= helperImg[(y0 - 1) * width + x1];
}
if(x0 > 0 && y0 > 0)
{
totalOfRect += helperImg[(y0 - 1) * width + (x0 - 1)];
}
Finally, we simply divide totalOfRect by the area of the rectangle to get the mean value:
int rWidth = x1 - x0 + 1;
int rheight = y1 - y0 + 1;
int meanOfRect = totalOfRect / (rWidth * rHeight);
Here's a version based on k_g's answer for a full BufferedImage with adjustable sample precision (step).
public static Color getAverageColor(BufferedImage bi) {
int step = 5;
int sampled = 0;
long sumr = 0, sumg = 0, sumb = 0;
for (int x = 0; x < bi.getWidth(); x++) {
for (int y = 0; y < bi.getHeight(); y++) {
if (x % step == 0 && y % step == 0) {
Color pixel = new Color(bi.getRGB(x, y));
sumr += pixel.getRed();
sumg += pixel.getGreen();
sumb += pixel.getBlue();
sampled++;
}
}
}
int dim = bi.getWidth()*bi.getHeight();
// Log.info("step=" + step + " sampled " + sampled + " out of " + dim + " pixels (" + String.format("%.1f", (float)(100*sampled/dim)) + " %)");
return new Color(Math.round(sumr / sampled), Math.round(sumg / sampled), Math.round(sumb / sampled));
}
I am trying to properly rotate a sword in my 2D game. I have a sword image file, and I wish to rotate the image at the player's location. I tried using Graphics2D and AffineTransform, but the problem is that the player moves on a different coordinate plane, the Screen class, and the Graphics uses the literal location of the pixels on the JFrame. So, I realized that I need to render the sword by rotating the image itself, and then saving it into a pixel array for my screen class to render. However, I don't know how to do this. Here is the code for my screen rendering method:
public void render(double d, double yOffset2, BufferedImage image, int colour,
int mirrorDir, double scale, SpriteSheet sheet) {
d -= xOffset;
yOffset2 -= yOffset;
boolean mirrorX = (mirrorDir & BIT_MIRROR_X) > 0;
boolean mirrorY = (mirrorDir & BIT_MIRROR_Y) > 0;
double scaleMap = scale - 1;
for (int y = 0; y < image.getHeight(); y++) {
int ySheet = y;
if (mirrorY)
ySheet = image.getHeight() - 1 - y;
int yPixel = (int) (y + yOffset2 + (y * scaleMap) - ((scaleMap * 8) / 2));
for (int x = 0; x < image.getWidth(); x++) {
int xPixel = (int) (x + d + (x * scaleMap) - ((scaleMap * 8) / 2));
int xSheet = x;
if (mirrorX)
xSheet = image.getWidth() - 1 - x;
int col = (colour >> (sheet.pixels[xSheet + ySheet
* sheet.width])) & 255;
if (col < 255) {
for (int yScale = 0; yScale < scale; yScale++) {
if (yPixel + yScale < 0 || yPixel + yScale >= height)
continue;
for (int xScale = 0; xScale < scale; xScale++) {
if (x + d < 0 || x + d >= width)
continue;
pixels[(xPixel + xScale) + (yPixel + yScale)
* width] = col;
}
}
}
}
}
}
Here is one of my poor attempts to call the render method from the Sword Class:
public void render(Screen screen) {
AffineTransform at = new AffineTransform();
at.rotate(1, image.getWidth() / 2, image.getHeight() / 2);
AffineTransformOp op = new AffineTransformOp(at,
AffineTransformOp.TYPE_BILINEAR);
image = op.filter(image, null);
screen.render(this.x, this.y, image, SwordColor, 1, 1.5, sheet);
hitBox.setLocation((int) this.x, (int) this.y);
for (Entity entity : level.getEntities()) {
if (entity instanceof Mob) {
if (hitBox.intersects(((Mob) entity).hitBox)) {
// ((Mob) entity).health--;
}
}
}
}
Thank you for any help you can provide, and please feel free to tell me if theres a better way to do this.
You can rotate() the image around an anchor point, also seen here in a Graphics2D context. The method concatenates translate(), rotate() and translate() operations, also seen here as explicit transformations.
Addendum: It rotates the image, but how do I save the pixels of the image as an array?
Once you filter() the image, use one of the ImageIO.write() methods to save the resulting RenderedImage, for example.
I'm working with tutorial from this site - "Fixed timestep" section.
Here's the code - http://pastebin.com/QaHgcLaR
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class GameLoopTest extends JFrame implements ActionListener
{
private GamePanel gamePanel = new GamePanel();
private JButton startButton = new JButton("Start");
private JButton quitButton = new JButton("Quit");
private JButton pauseButton = new JButton("Pause");
private boolean running = false;
private boolean paused = false;
private int fps = 60;
private int frameCount = 0;
public GameLoopTest()
{
super("Fixed Timestep Game Loop Test");
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
JPanel p = new JPanel();
p.setLayout(new GridLayout(1,2));
p.add(startButton);
p.add(pauseButton);
p.add(quitButton);
cp.add(gamePanel, BorderLayout.CENTER);
cp.add(p, BorderLayout.SOUTH);
setSize(500, 500);
startButton.addActionListener(this);
quitButton.addActionListener(this);
pauseButton.addActionListener(this);
}
public static void main(String[] args)
{
GameLoopTest glt = new GameLoopTest();
glt.setVisible(true);
}
public void actionPerformed(ActionEvent e)
{
Object s = e.getSource();
if (s == startButton)
{
running = !running;
if (running)
{
startButton.setText("Stop");
runGameLoop();
}
else
{
startButton.setText("Start");
}
}
else if (s == pauseButton)
{
paused = !paused;
if (paused)
{
pauseButton.setText("Unpause");
}
else
{
pauseButton.setText("Pause");
}
}
else if (s == quitButton)
{
System.exit(0);
}
}
//Starts a new thread and runs the game loop in it.
public void runGameLoop()
{
Thread loop = new Thread()
{
public void run()
{
gameLoop();
}
};
loop.start();
}
//Only run this in another Thread!
private void gameLoop()
{
//This value would probably be stored elsewhere.
final double GAME_HERTZ = 30.0;
//Calculate how many ns each frame should take for our target game hertz.
final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ;
//At the very most we will update the game this many times before a new render.
//If you're worried about visual hitches more than perfect timing, set this to 1.
final int MAX_UPDATES_BEFORE_RENDER = 5;
//We will need the last update time.
double lastUpdateTime = System.nanoTime();
//Store the last time we rendered.
double lastRenderTime = System.nanoTime();
//If we are able to get as high as this FPS, don't render again.
final double TARGET_FPS = 60;
final double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;
//Simple way of finding FPS.
int lastSecondTime = (int) (lastUpdateTime / 1000000000);
while (running)
{
double now = System.nanoTime();
int updateCount = 0;
if (!paused)
{
//Do as many game updates as we need to, potentially playing catchup.
while( now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER )
{
updateGame();
lastUpdateTime += TIME_BETWEEN_UPDATES;
updateCount++;
}
//If for some reason an update takes forever, we don't want to do an insane number of catchups.
//If you were doing some sort of game that needed to keep EXACT time, you would get rid of this.
if ( now - lastUpdateTime > TIME_BETWEEN_UPDATES)
{
lastUpdateTime = now - TIME_BETWEEN_UPDATES;
}
//Render. To do so, we need to calculate interpolation for a smooth render.
float interpolation = Math.min(1.0f, (float) ((now - lastUpdateTime) / TIME_BETWEEN_UPDATES) );
drawGame(interpolation);
lastRenderTime = now;
//Update the frames we got.
int thisSecond = (int) (lastUpdateTime / 1000000000);
if (thisSecond > lastSecondTime)
{
System.out.println("NEW SECOND " + thisSecond + " " + frameCount);
fps = frameCount;
frameCount = 0;
lastSecondTime = thisSecond;
}
//Yield until it has been at least the target time between renders. This saves the CPU from hogging.
while ( now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES)
{
Thread.yield();
//This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it.
//You can remove this line and it will still work (better), your CPU just climbs on certain OSes.
//FYI on some OS's this can cause pretty bad stuttering. Scroll down and have a look at different peoples' solutions to this.
try {Thread.sleep(1);} catch(Exception e) {}
now = System.nanoTime();
}
}
}
}
private void updateGame()
{
gamePanel.update();
}
private void drawGame(float interpolation)
{
gamePanel.setInterpolation(interpolation);
gamePanel.repaint();
}
private class GamePanel extends JPanel
{
float interpolation;
float ballX, ballY, lastBallX, lastBallY;
int ballWidth, ballHeight;
float ballXVel, ballYVel;
float ballSpeed;
int lastDrawX, lastDrawY;
public GamePanel()
{
ballX = lastBallX = 100;
ballY = lastBallY = 100;
ballWidth = 25;
ballHeight = 25;
ballSpeed = 25;
ballXVel = (float) Math.random() * ballSpeed*2 - ballSpeed;
ballYVel = (float) Math.random() * ballSpeed*2 - ballSpeed;
}
public void setInterpolation(float interp)
{
interpolation = interp;
}
public void update()
{
lastBallX = ballX;
lastBallY = ballY;
ballX += ballXVel;
ballY += ballYVel;
if (ballX + ballWidth/2 >= getWidth())
{
ballXVel *= -1;
ballX = getWidth() - ballWidth/2;
ballYVel = (float) Math.random() * ballSpeed*2 - ballSpeed;
}
else if (ballX - ballWidth/2 <= 0)
{
ballXVel *= -1;
ballX = ballWidth/2;
}
if (ballY + ballHeight/2 >= getHeight())
{
ballYVel *= -1;
ballY = getHeight() - ballHeight/2;
ballXVel = (float) Math.random() * ballSpeed*2 - ballSpeed;
}
else if (ballY - ballHeight/2 <= 0)
{
ballYVel *= -1;
ballY = ballHeight/2;
}
}
public void paintComponent(Graphics g)
{
//BS way of clearing out the old rectangle to save CPU.
g.setColor(getBackground());
g.fillRect(lastDrawX-1, lastDrawY-1, ballWidth+2, ballHeight+2);
g.fillRect(5, 0, 75, 30);
g.setColor(Color.RED);
int drawX = (int) ((ballX - lastBallX) * interpolation + lastBallX - ballWidth/2);
int drawY = (int) ((ballY - lastBallY) * interpolation + lastBallY - ballHeight/2);
g.fillOval(drawX, drawY, ballWidth, ballHeight);
lastDrawX = drawX;
lastDrawY = drawY;
g.setColor(Color.BLACK);
g.drawString("FPS: " + fps, 5, 10);
frameCount++;
}
}
private class Ball
{
float x, y, lastX, lastY;
int width, height;
float xVelocity, yVelocity;
float speed;
public Ball()
{
width = (int) (Math.random() * 50 + 10);
height = (int) (Math.random() * 50 + 10);
x = (float) (Math.random() * (gamePanel.getWidth() - width) + width/2);
y = (float) (Math.random() * (gamePanel.getHeight() - height) + height/2);
lastX = x;
lastY = y;
xVelocity = (float) Math.random() * speed*2 - speed;
yVelocity = (float) Math.random() * speed*2 - speed;
}
public void update()
{
lastX = x;
lastY = y;
x += xVelocity;
y += yVelocity;
if (x + width/2 >= gamePanel.getWidth())
{
xVelocity *= -1;
x = gamePanel.getWidth() - width/2;
yVelocity = (float) Math.random() * speed*2 - speed;
}
else if (x - width/2 <= 0)
{
xVelocity *= -1;
x = width/2;
}
if (y + height/2 >= gamePanel.getHeight())
{
yVelocity *= -1;
y = gamePanel.getHeight() - height/2;
xVelocity = (float) Math.random() * speed*2 - speed;
}
else if (y - height/2 <= 0)
{
yVelocity *= -1;
y = height/2;
}
}
public void draw(Graphics g)
{
}
}
}
After run this code, the ball has kind of lag, but there is still 60 FPS. After I move mouse over application's window and move it in random directions, the ball is moving smoothly. It happens even if window application isn't focused! What's wrong? Can it be fixed?
I'm using Ubuntu 13.04 with Oracle JDK7.
I've found that it happens with every application. Similar things happens even in LWJGL application, but effect of the "lag" is much less than in swing application.
17 sec video showing my problem
http://www.youtube.com/watch?v=J8SBjKncgRw
I had same problem under Kubuntu 13.04
I googled something which works for me: http://www.java-gaming.org/index.php?topic=19224.0
The basic idea is to put Toolkit.getDefaultToolkit().sync(); after drawing something. In your code it should be after drawGame(interpolation);.
The explanation seems to be that the window system manages the update intervals, so it is not Java's fault and only occures with some window mangers.
The repaints should be triggered from a Swing based Timer, which ensures that GUI updates are called on the EDT. See Concurrency in Swing for more details.
This appears to be a bug in the VM since Java 6. I had the same problem and found an ugly, but simple workaround: Create a Robot object and let it press a key or position the mouse in each cycle. The animation will then run smoothly. Example:
final Robot robot = new Robot();
javax.swing.Timer timer = new javax.swing.Timer(initialDelay, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
// update your image...
robot.keyPress(62);
}
});
See also: Java animation stutters when not moving mouse cursor
You could invoke setIgnoreRepaint(true) and use a BufferStrategy for drawing that particular component instead. A BufferStrategy allows you to perform the drawing whenever you want. You can also invoke paintComponent methods inside your drawing method.