Nearly a month ago I was testing JavaFX graphics API, however I was worried about performance. The performance problem is that I had to pass a Paint object for colorizing shapes.
Issue: it's impossible to change settings of an existing Paint
I just need to update a Paint, for example, Color, but there are no methods like Color#setRed, Color#setGreen, Color#setBlue and Color#setOpacity. There are no fields, too (javafx.scene.paint.Color).
Performance
The unique way I can try to change settings of a Paint is to construct another Paint instead, however this will involve on running the garbage collector. Is that correct?
The compiler should optimize these objects automatically depending on how they're entirely used. But Oracle said nothing about that on its compiler, at least for me, so far.
If they say garbage collection is good, the fault isn't mine. I didn't mention anything about garbage collection being helpful.
So is it a good solution to re-construct a Paint? Do you guys also know of JavaFX alternatives that handle what I want?
The issue you are concerned with doesn't really exist. Since Paint is an immutable class (which is a good design choice for a number of reasons), the correct way to update the color of something in JavaFX is to create a new Paint instance and set it as a property where needed. Previously-used instances which no longer have references to them will be cleaned up - efficiently - by the garbage collector at some point in the future.
Here is a simple demo which creates new Paint instances every time the scene is rendered (also new Background and BackgroundFill instances). It prints warnings if the frame rendering takes more than 25ms (an arbitrary threshold). On my system, it warns for the first couple of frames, and sometimes warns when the window is hidden and completely reshown.
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class UpdateColorContinuously extends Application {
#Override
public void start(Stage primaryStage) {
Pane root = new Pane();
root.setMinSize(600, 600);
new AnimationTimer() {
private long start = -1 ;
private long lastUpdate ;
#Override
public void handle(long now) {
if (start < 0) {
start = now ;
lastUpdate = now ;
}
long elapsed = now - start ;
double elapsedSeconds = elapsed / 1_000_000_000.0 ;
Color newColor = Color.hsb(elapsedSeconds * 5, 1.0, 1.0);
BackgroundFill fill = new BackgroundFill(newColor, CornerRadii.EMPTY, Insets.EMPTY);
Background bg = new Background(fill);
root.setBackground(bg);
if (now - lastUpdate > 25_000_000) {
System.err.println("Warning: frame rendering took "+ (now-lastUpdate)/1_000_000 + " ms");
}
lastUpdate = now ;
}
}.start();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
It's possible I haven't run this long enough for the GC to need to run; a conservative estimate of the amount of memory a Paint instance needs is 50 bytes, so you would need to create of the order of 10^7 such instances to use 500MB (a reasonably small amount of RAM on a modern desktop system, which would likely be equivalent to one GC sweep). So, ballpark figures, this would generate one GC sweep in two days.
Related
I am using Intellij to program Java.
I am currently trying to make a top down tile based shooter.
My issue is that my game, after approximately 2 minutes, crashes with a popup saying "Java(TM) Platform SE Binary has stopped working. I recorded the time it took for it to crash 3 times: 1m57s, 1m59s, 1m58s.
The game is in a very simple state right now and I am not sure what could be causing the crash. All of the relevant code is in just two classes: GameFrame.java (extends JFrame) and GamePanel.java (which extends JPanel).
GameFrame.java:
package net.magnusfrater.tds.game;
import javax.swing.*;
public class GameFrame extends JFrame {
public static final int width = 1000;
public static final int height = width / 16 * 10;
private GamePanel gp;
public GameFrame () {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(width,height);
setResizable(false);
setLocationRelativeTo(null);
setTitle("Time Based Fast Paced Top Down Shooter Demo");
gp = new GamePanel();
add(gp);
setVisible(true);
}
public static void main (String[] args) {
GameFrame gf = new GameFrame();
}
}
GamePanel.java
package net.magnusfrater.tds.game;
import net.magnusfrater.tds.input.Keyboard;
import javax.swing.*;
import java.awt.*;
public class GamePanel extends JPanel implements Runnable {
//panel
private Thread thread;
private static boolean running;
private boolean fpsLock;
//input
private Keyboard kb;
//game
private Game game;
public GamePanel () {
//panel
thread = new Thread(this, "Time Based Fast Paced Top Down Shooter Demo");
running = true;
fpsLock = true;
//input
//kb = new Keyboard();
//addKeyListener(kb);
//game
//game = new Game(1);
thread.start();
}
public void run () {
long iTimeNS = System.nanoTime();
int tickRate = 60;
long ns = 1000000000 / tickRate;
int ups = 0;
int fps = 0;
long iTimeS = System.nanoTime();
long s = 1000000000;
while (running) {
long fTimeNS = System.nanoTime();
if (fTimeNS - iTimeNS >= ns){
iTimeNS = System.nanoTime();
tick();
ups++;
if (fpsLock){
repaint();
fps++;
}
}
if (!fpsLock){
repaint();
fps++;
}
long fTimeS = System.nanoTime();
if (fTimeS - iTimeS >= s){
iTimeS = System.nanoTime();
System.out.println("ups: " + ups + "\tfps: " + fps);
ups = 0;
fps = 0;
}
}
System.exit(0);
}
public void tick () {
if (kb != null)
kb.tick();
if (game != null)
game.tick();
}
#Override
public void update (Graphics g) {
paint(g);
}
#Override
public void paint (Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0,0,GameFrame.width, GameFrame.height);
//if (game != null)
//game.paint(g);
}
public static void quitGame () {
running = false;
}
}
I originally thought that the issue was because of the way that I was loading images for spritesheets or maybe the way I was loading text files for the level design but after reworking both of those, the issue remained.
That left me curious and a little bit fed up so I tried finding out more about the explanation for the crash. First I read more from the popup but it didn't say anything useful: (See below)
Second I looked at the exit code given by Intellij: (See below)
I looked up what exit code 255 was but there wasn't anything useful. The best explanation I could find was that exit code 255 meant that the real exit code was out of range: (See below)
I was out of ideas at this point so I just started plain old googling everything I could think of. The problem with googling "Java(TM) Platform SE Binary has stopped working" is that almost every suggested link is a question about Minecraft. Limiting my search to Stack Overflow yielded me some results, but nothing conclusive. Some of the fixes I found were stuff I already tried (such as not handling input streams properly, not handling buffered reader properly, not disposing of elements, etc). I found these links but none of them were truly related to my issue:
(See below)
(See below)
(See below)
(See below)
(See below)
(See below)
The last fix I tried was to re-install Java SE Development Kit 8u101 AND Java SE Development Kit 8u102. I then restarted Intellij. I then restarted my computer.
Nothing worked.
At this point I think I'm just dumb. I've overlooked something easy I can tell. What am I missing?
(ps~ This is a possibly related issue. So if I run my game with almost no content in it with the fps not locked to 60, I get really absurd numbers of frames per second. I didn't think that fps as high as 7,000,000 was possible. I think. I don't know. Have I programmed that wrong as well? Here is a related picture of my ups/fps output: [see below])
(SEE HERE) So Stack Overflow doesn't allow members with a score within a certain threshold post more than 2 links and allows absolutely no posting of images. So here is a link to a google doc with all of the links and images I mentioned above:
https://docs.google.com/document/d/1XrBuVio19GmkFz0EfRzXVp5AJmM5zPfVO6vK3oS3Eaw/edit?usp=sharing
Try and set your -Xmx to something like 2G and see if it runs longer. If so, something is allocating memory and maybe you have that other setting set that exits instead of garbage collecting for some reason.
Also, try changing your code to limit things using Guava's RateLimiter.
…
// class level
final RateLimiter frameLimiter = RateLimiter.create(60.0);
final RateLimiter outputLimiter = RateLimiter.create(1.0);
…
// in run method
while (running) {
frameLimiter.acquire();
repaint();
fps++;
if (outputLimiter.tryAcquire()){
System.out.println("fps: " + fps);
fps = 0;
}
}
I've removed ups and tick(). You should do your work after repainting and I don't think you want to do more work than needed for the next frame, which at the soonest should be at your max rate. Later you'll need to add logic to handle skipping work when frames are being skipped. I might make more sense to increment the fps within repaint.
You could put the output in its own thread and only acquire that limiter if you synchronized the increments and resetting of fps.
Hello stackoverflow community! I am learning to program and am working on a "duck hunt" style javafx game for mobile devices where 3 types of ducks move horizontally across the screen. Each time a certain type of duck is clicked, that type of duck gets a point next to its name.
What I would like to do is display a global leaderboard after the game so you can see which type of duck was clicked the most out of everyone who has played the game. What is everyones preferred way of implementing something like this? If you have any questions please feel free to ask me. Thanks, -H.J.
Edit (4/20/16 1:40PM PST): Because my original post was put on hold because it was deemed to vague, I wrote up a very basic example program to help explain what I am trying to do. On my menu scene I would like to retrieve the total number of clicks on the red and green rectangles for everyone who has played this game worldwide, which is currently globalScoreRed and globalScoreGreen and set to 0 because this is one of the parts I need help with.
On the playGame scene, when you hit the GG button, I would like scoreRed and scoreGreen to be submitted to the leaderboard which would adjust the values when viewed from the menu screen.
This is a very bad game and I can attest is not a game I would enjoy playing, but I am trying to learn the concepts of how to send the values and retrieve them. Once again thanks for viewing my post and helping out, you are very much appreciated.
package LeaderboardHelp;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class LeaderboardHelpMain extends Application {
Stage window;
Scene menu, playGame;
private Rectangle targetRed = new Rectangle(40, 40);
private Rectangle targetGreen = new Rectangle(40, 40);
private int scoreRed = 0;
private int scoreGreen = 0;
private Text redScoreText = new Text("Red: " + scoreRed);
private Text greenScoreText = new Text("Green: " + scoreGreen);
private int globalScoreRed = 0;
private int globalScoreGreen = 0;
private Text globalRedScoreText = new Text("Worldwide Red Clicks: " + globalScoreRed);
private Text globalGreenScoreText = new Text("Worldwide Green Clicks: " + globalScoreGreen);
private boolean playable = false;
public static void main(String[] args) { launch(args); }
#Override
public void start(Stage primaryStage) throws Exception {
window = primaryStage;
Button startButton = new Button("Start!");
startButton.setOnAction(e -> window.setScene(playGame));
//menu scene layout
VBox menuLayout = new VBox(20);
menuLayout.getChildren().addAll(globalGreenScoreText, globalRedScoreText, startButton);
menu = new Scene(menuLayout, 600, 600);
Button ggButton = new Button("GG");
ggButton.setOnAction(e -> window.setScene(menu));
targetRed.setFill(Color.RED);
targetRed.setOnMouseClicked(event -> {
scoreRed += 1;
redScoreText.setText("Red: " + scoreRed);
});
targetGreen.setFill(Color.GREEN);
targetGreen.setOnMouseClicked(event -> {
scoreGreen += 1;
greenScoreText.setText("Green: " + scoreGreen);
});
targetRed.setTranslateX(50);
targetRed.setTranslateY(50);
targetGreen.setTranslateX(100);
targetGreen.setTranslateY(50);
greenScoreText.setTranslateX(500);
greenScoreText.setTranslateY(50);
redScoreText.setTranslateX(500);
redScoreText.setTranslateY(70);
ggButton.setTranslateX(250);
ggButton.setTranslateY(250);
//game layout
Pane gameLayout = new Pane();
gameLayout.getChildren().addAll(targetRed, targetGreen, redScoreText, greenScoreText, ggButton);
playGame = new Scene(gameLayout, 600, 600);
window.setScene(menu);
window.setTitle("Leaderboard Helper");
window.show();
}
}
Edit (4/20/16 8:45PM PST) So after more reading I came across oracles EchoServer example which is getting me on a track (not sure if its right or wrong track). I was thinking about having the server save the submitted scores in a text file. When the menu scene is called by client it will connect to the server and request the scores. The server will read the text file and send scores to the application client. When the GG button is pressed, the score is sent to the server which reads the text file, adds the news score to old score and then overwrites the score text file. Is there a better way of doing this? I do want this to be scalable allowing lots of users to add their score at the same time.
A web service would be required. A simple one that takes maybe the type of duck and the number of times it was clicked.
POST /duck/{type}/{clicks}
The web-service would need to be secured using HTTPS or HMAC, or better yet both.
For HMAC the key would need to be hidden and obfuscated in your code. Security is always breakable, its about making the effort not worth it for such a small payout to any would-be hacker.
To do HMAC properly one should use a NONCE when creating the digest (aka signature). The NONCE should have a short expiration time. Many implementations use the timestamp as a NONCE, other implementations have the NONCE being provided by the web service. For simplicity I would go with the former. If the NONCE is older than 2 or 3 seconds, then reject the request.
What I am doing is making a program that constantly takes screenshots of the users desktop and saves them as long as the user wants. I was initially placed a call to the method that captures in a while method but that was too slow and I need as many images take as soon as possible. What I decided to do was use threading.
While the program is running my PC get's slow (ie mouse flashes, moves slow, ect) and when I stop the program I get the following error.
Exception in thread "Thread-294" java.lang.OutOfMemoryError: Java heap space
at sun.awt.windows.WRobotPeer.getRGBPixels(Unknown Source)
at java.awt.Robot.createScreenCapture(Unknown Source)
at maple.Record.run(Record.java:29)
Here is the line that produces the error.
img = r.createScreenCapture(new Rectangle (0, 0, width, height));
Here is my class that contains the thread.
import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Vector;
import javax.imageio.ImageIO;
import javax.media.MediaLocator;
public class Record implements Runnable {
Robot r;
static int width = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
static int height = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
BufferedImage img;
public static boolean stop = false;
public void run() {
try {
r = new Robot();
img = r.createScreenCapture(new Rectangle (0, 0, width, height));
ImageIO.write(img, "png", new File(JavCapture.tmpLocation + "\\tmp\\" + System.currentTimeMillis() + ".png"));
} catch (IOException | AWTException e) { e.printStackTrace(); }
}
}
The error is produced when I am calling the JpegImagesToMovies that has been edited to work with .png files.
Please check out this link for more information on that.
How can I solve the problem, and how can I make the thread use less memory so that it doesn't slow the PC down.
Here is the call in the main method.
do {
(new Thread(new Record())).start();
} while (!Record.stop);
From the looks of it you are creating a separate thread for every screenshot, this will run you out of memory very fast. Instead you should have a single thread that sits in loop, sleeps and takes screenshots from time to time.
public class ScreenshotTaker implements Runnable {
private volatile boolean done = false;
public void run( ) {
while (!done) {
... take screenshot...
...sleep ...
}
}
public void setDone( ) {
done = true;
}
}
You're asking for trouble, you should go for some Threadpool implementation that would limit your thread count. To get idea on usage, see the official tutorial: http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
As in your solution:
do {
(new Thread(new Record())).start();
} while (!Record.stop);
you create threads faster than they can process their job. Basically you just say create many threads as fast as possible, there is no guarantee on count of them processed till new one is created. So it's just a question of time till program dies (resources are exhausted = CPU + Memory)
You should limit the thread creation process here
(new Thread(new Record())).start();
Those threads start almost together and bloats your memory up.
With so little code to go with it is hard to say, but you should definitely make the following:
Robot r;
BufferedImage img;
into local variables instead of instance variables. The way you have it, the objects they refer to will be reachable for much longer than your program actually needs them. That's one road to an OutOfMemoryException.
I'm somewhat new to creating games in Java, however my current setup is such that the FPS on the paint method is bounded only by the system. So, my FPS tends to be between 300 and 450. In order to standardize movement speeds on objects, I've been dividing the increment by the FPS so that it will increment that total amount in a one second time-frame.
I have the following code. What I want to do it make it so that map.addEntity() is not called 300 or 400 times per second, in accordance with the FPS; but rather make it so that I can choose, for example, to make the projectile fire at 10 RPS or so. How can I achieve this?
public void mousePressed(MouseEvent e) {
if (gameStarted)
shootProjectile = true;
}
public void paint(Graphics g) {
if (shootProjectile)
map.addEntity(new Projectile("projectile.png", x, y, 0, projectileSpeed));
}
I've been dividing the increment by the FPS
Don't do that! Use a Timer or a swingTimer to update everything at a constant rate. For example you could do something like this:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) throws InterruptedException {
Timer timer = new Timer(10,new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
updateProjectilePositions();
}
});
timer.start();
}
private static void updateProjectilePositions() {
// TODO Auto-generated method stub
}
}
Note that if you use a swing Timer, your projectile updating will happen on the swing thread. If the updating hangs, your GUI will also hang.
You also should not call map.addEntity inside paint() since paint() does one thing and one thing only: paint everything. You can get around this but mixing up the code that updates things like position with code that renders the objects will eventually bite you.
You should never use FPSs as your quantizer otherwise your game will run at different speeds on different machines and even on the same machine according to frame drops or spikes.
You can do two different things:
use a delta time between each frame update and update your game logic proportionally
use an accumulator in which you add the time delta and then you fire a logic update every fixed amount (so that you don't need to do things in proportion as your logic update will be fixed)
in c++ or c programming language, we know to change the cordinate we use gotoxy(x,y) and we can use looping and sleep to change the cordinate and making animation. like this;
for(x = 20; x < 25; x++){
gotoxy(x,20); cout << "*"
}
but my queston is how about in JAVAFX 2.0 programming? i'm using netbeans 7.2.
thanks for your any help.
Use the JavaFX Animation Package.
There are numerous examples in the JavaFX Animation Tutorial, as Andy pointed out in his comment.
And there is a cute example of a running horse animation loop.
The key is that you don't sleep the JavaFX application thread and you have to release control of the JavaFX thread back to the JavaFX system each time you update something and want it rendered. The JavaFX animation classes take care of these things for you so that you don't have to worry about it. If you just loop like you do in the sample code from your question, JavaFX will just render the scene once after your loop has completed and you will never see anything happen.
Here is a fairly boring example which uses a Timeline to emulate the c++ code in your question to move a dot a pixel every 400 milliseconds.
import java.util.Date;
import javafx.animation.*;
import javafx.application.Application;
import javafx.event.*;
import javafx.scene.*;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
/** Simple JavaFX Animation Sample. */
public class AnimationSample extends Application {
private int x = 20;
private String status = "";
private final Circle dot = new Circle(20, 20, 3);
private final TimeCounter counter = new TimeCounter();
public static void main(String[] args) throws Exception { launch(args); }
#Override public void start(final Stage stage) throws Exception {
final Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO, new EventHandler() {
#Override public void handle(Event event) {
refreshScene();
}
}),
new KeyFrame(Duration.millis(400))
);
timeline.setCycleCount(Timeline.INDEFINITE);
stage.setScene(new Scene(new Group(dot), 50, 50));
stage.show();
counter.reset();
timeline.play();
}
private void refreshScene() {
gotoxy(x, 20);
status = "*****".equals(status) ? "*" : status + "*";
System.out.println(String.format("%7d", counter.elapsed()) + " ms " + x + " " + status);
if (x == 24) {
x = 20;
} else {
x++;
}
}
private void gotoxy(int x, int y) {
dot.setCenterX(x);
dot.setCenterY(y);
}
class TimeCounter {
private long start = new Date().getTime();
void reset() { start = new Date().getTime(); }
long elapsed() { return new Date().getTime() - start; }
}
}
There are three different options in JavaFX, depending on your needs.
The most basic one is AnimationTimer. It's equivalent to Swing's Timer. It simply contains a handle method which is called on every frame, and passed current time as argument. You probably want some internal bookkeeping so that you do not do expensive calculations every time handle is called.
Transition has an interpolate(frac) method, which gets called with values of frac between 0.0 and 1.0. It's up to you to do all UI changes you want to, based on the frac value. Both Transition and Timeline extend Animation, so you can set stuff like cycle duration, whether the Transition is reversed at end, etc.
Timeline is the most complex one. You define arbitrary amount of KeyFrames (think of states) that contain wanted properties of different Nodes, and the Timeline will do all the work for you interpolating how to animate the change between the provided values. For example, you can give a keyframe where x property of a Node is 0, and another where it's 100, and Timeline will do the animating for you.
Have look at using a Timeline Animation. It is a key component of animation in JavaFX and
is used to establish when, and in what sequence, key parts of an animation occur.
Here is an example