So as of right now I'm implementing Conway's Game of Life using JavaFX. In a nutshell, in my class extending AnimationTimer, within the handle() method, it traverses through every cell in a 2D array and updates each position, then draws to the canvas using the information in the 2D array.
This works completely fine but the problem is it runs far too fast. You can't really see what's going on on the canvas. On the window I have the canvas as well as a few buttons. I added a Thread.sleep(1000) to try to regulate a generation/frame per second, but doing this causes the window to not detect the button presses immediately. The button presses are completely responsive when not telling the thread to sleep.
Does anyone have any suggestions on how to solve this?
You can use Timeline which is probably more suitable for this. Set cycle count to Animation.INDEFINITE, and add a KeyFrame with the delay you want between updates, and your current handle implementation as the frame's onFinished.
final Timeline timeline = new Timeline();
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.getKeyFrames().add(
new KeyFrame(
Duration.seconds(1),
event -> handle()
)
);
timeline.play();
Alternatively, you may try to have the delay of the KeyFrame as zero, and use the Timeline's targetFrameRate, but I personally never tried it.
Edit: Another option is to keep a frameSkip variable in your AnimationTimer:
private int frameSkip = 0;
private final int SKIP = 10;
#Override
public void handle(long now) {
frameSkip++;
if (frameSkip <= SKIP) {
// Do nothing, wait for next frame;
return;
}
// Every SKIP frames, reset frameSkip and do animation
frameSkip = 0;
// Do animation...
}
Related
So I'd like to make a circular progress indicator that runs smoothly from start to end, although I haven't found exactly what I'm looking for, I found a workaround using the class "ProgressIndicator" and grouping it in a way that is shown as I want it to.
I tried running this code that creates a timeline for it to do such smooth result I was looking for (this code isn't mine at all, took it from https://asgteach.com/2011/10/javafx-animation-and-binding-using-the-progressbar/) but it doesn't work as intended at all. The end result should be looking like this from start to end:
expectation
Although it takes some time to load showing this animation:
reality
Is there any way to make that starting animation not appear and make it so it starts instantly? If you have better options for what I'm trying to achieve or a solution using the existing progressIndicator I'm trying to use, I'll gladly hear them out.
The code I'm trying to run right now:
private static final Integer STARTTIME = 30;
private Timeline timeline;
private IntegerProperty timeSeconds = new SimpleIntegerProperty(STARTTIME*100);
public void testProgressIndicator() {
myProgressIndicator.progressProperty().bind(
timeSeconds.divide(STARTTIME*100.0).subtract(1).multiply(-1));
btnTest.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
if (timeline != null) {
timeline.stop();
}
timeSeconds.set((STARTTIME+1)*100);
timeline = new Timeline();
timeline.getKeyFrames().add(
new KeyFrame(Duration.seconds(STARTTIME+1),
new KeyValue(timeSeconds, 0)));
timeline.playFromStart();
}
});
I'm looking for advice on updating the JavaFX scene graph while an animation is running. What I have is a custom level of detail node in a 3D scene that loads and creates a MeshView in a background thread. That's fine, it works without affecting the JavaFX application thread. But the issue is that when I update the node (I actually just replace a child in a Group node) that is live in the scene graph like this (running this part on the JavaFX application thread):
Group group = (some value that is a live node in the scene graph ...)
group.getChildren().set (0, response.view); <-- response.view is my MeshView instance
it works, but a large number of changes like this being pushed to the scene in a small span of time makes the animation of the 3D scene shudder. I know why it's happening -- the pulse is doing a lot of work to update the scene graph to the graphics card. But I'd like some advice and/or code samples on how best to handle this. One thing I can think of would be to use a producer/consumer model where the scene graph changes are produced and placed into a queue, and then the consumer would only consume so many at a time. But there are times when the animation stops and isn't running anymore, and the scene graph changes can be pushed to the scene as fast as they become available.
Is there an example of handling this well online somewhere that I haven't found? Or some standard practices / solutions that are useful? Basically it's like I want to ensure the frame rate of the animation and only push changes at a rate that can be handled without disrupting the animation rate, but I have no idea how to determine what rate that would be. I don't know how to measure the length of time that each of my scene graph modifications are actually taking behind the scenes, or the rate of pulses that are falling below the usual 60 Hz so that I can throttle back my impact if needed.
So I've got something that works well. Whenever I need to perform a scene graph update and I don't know if an animation is running or not (because I'm in another class), I wrap the scene graph update in a Runnable that's accepted by a Consumer:
private Consumer<Runnable> updateConsumer;
...
updateConsumer.accept (() -> group.getChildren().set (0, response.view));
Then in the class that is the final destination of the consumer, I add the scene graph update to queue:
private ConcurrentLinkedQueue<Runnable> sceneGraphChangeQueue;
...
public void addSceneGraphChange (Runnable change) {
sceneGraphChangeQueue.add (change);
}
When an animation is being performed, I watch a variable that I know is changing for each step of the animation, and attach a listener to it so that it can track the animation is happening. In my case, I'm tracking my camera position and I hold onto the count of camera position updates since I last reset the counter (which I'll explain the purpose for below):
private int cameraUpdatesSinceReset;
...
view.cameraPositionProperty().addListener ((obs, oldVal, newVal) -> {
cameraUpdatesSinceReset++;
performSceneChanges (SCENE_CHANGES_PER_PULSE);
});
...
private void performSceneChanges (
int limit
) {
int count = 0;
while (!sceneGraphChangeQueue.isEmpty() && count < limit) {
Runnable change = sceneGraphChangeQueue.remove();
change.run();
count++;
} // while
if (count != 0) LOGGER.finer ("Performed " + count + " scene graph changes");
} // performSceneChanges
So that lets the scene graph changes trickle through during each animation pulse, and I found that setting SCENE_CHANGES_PER_PULSE=2 was about all my machine can handle before the animation starts to shudder. Finally, to make sure the scene change queue is flushed when there are no animations going on anymore, I poll the queue periodically with a ScheduledService and check in on my cameraUpdatesSinceReset counter:
ScheduledService<Void> service = new ScheduledService<Void>() {
protected Task<Void> createTask() {
return new Task<Void>() {
protected Void call() {
Platform.runLater (() -> {
if (cameraUpdatesSinceReset == 0) {
performSceneChanges (Integer.MAX_VALUE);
} // if
else cameraUpdatesSinceReset = 0;
});
return (null);
} // call
};
} // createTask
};
service.setPeriod (Duration.millis (333));
service.start();
The service polls the queue three times per second, which is enough to have 20 pulses pass by and if the camera hasn't updated in that time, I assume there's no animation or user interaction going on. The result is that the animation is smooth and the system does as many scene graph updates as it can when it can.
I noticed that when adding and deleting tabs from a TabPane, it fails to match the position of the order of tabs in the underlying list. This only happens when at least one tab is hidden entirely due to the width of the parent. Here's some code that replicates the issue:
public class TabPaneTester extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Scene scene = sizeScene();
primaryStage.setMinHeight(200);
primaryStage.setWidth(475);
primaryStage.setScene(scene);
primaryStage.show();
}
private Scene sizeScene(){
TabPane tabPane = new TabPane();
tabPane.setTabMinWidth(200);
tabPane.getTabs().addAll(newTabs(3));
Scene scene = new Scene(tabPane);
scene.setOnKeyPressed(e -> tabPane.getTabs().add(1, tabPane.getTabs().remove(0)));
return scene;
}
private static Tab[] newTabs(int numTabs){
Tab[] tabs = new Tab[numTabs];
for(int i = 0; i < numTabs; i++) {
Label label = new Label("Tab Number " + (i + 1));
Tab tab = new Tab();
tab.setGraphic(label);
tabs[i] = tab;
}
return tabs;
}
public static void main(String[] args) {
launch();
}
}
When you press a key, it removes the first tab (at index 0) and puts it back at index 1, effectively swapping the first two tabs. However, when run the tabs don't actually visually swap (even though the tab switcher menu does switch their position).
If you change the width of the screen to include even a pixel of the third tab that was hidden (replace 475 with 500), it works as intended. Any clues as to how to fix this?
This is indeed a bug and I couldn't find it reported in the public JIRA it is now reported at https://bugs.openjdk.java.net/browse/JDK-8193495.
All my analysis is based on the code in TabPaneSkin if you want to have a look yourself.
Summary
The problem arises when you remove and then add the tab "too quickly". When a tab is removed, asynchronous calls are made during the removal process. If you make another change such as adding a tab before the async calls finish (or at least "finish enough"), then the change procedure sees the pane at an invalid state.
Details
Removing a tab calls removeTabs, which is outlined below:
Various internal removal methods are called.
Then it checks if closing should be animated.
If yes (GROW),
an animation queues a call to a requestLayout method, which itself is invoked asynchronously,
and the animations starts (asynchronously) and the method returns.
If not (NONE),
requestLayout is called immediately and the method returns.
The time during which the pane is at an invalid state is the time from when the call returns until requestLayout returns (on another thread). This duration is equivalent to the duration of requestLayout plus the duration of the animation (if there is one), which is ANIMATION_SPEED = 150[ms]. Invoking addTabs during this time can cause undesired effects because the data needed to properly add the tab is not ready yet.
Workaround
Add an artificial pause between the calls:
ObservableList<Tab> tabs = tabPane.getTabs();
PauseTransition p = new PauseTransition(Duration.millis(150 + 20));
scene.setOnKeyPressed(e -> {
Tab remove = tabs.remove(0);
p.setOnFinished(e2 -> tabs.add(1, remove));
p.play();
});
This is enough time for the asynchronous calls to return (don't call the KeyPressed handler too quickly in succession because you will remove the tabs faster than they can be added). You can turn off the removal animation with
tabPane.setStyle("-fx-close-tab-animation: NONE;");
which allows you to decrease the pause duration. On my machine 15 was safe (here you can also call the KeyPressed handler quickly in succession because of the short delay).
Possible fix
Some synchronization on tabHeaderArea.
I have this loop
while (true) {
game.update();
view.repaint();
Thread.sleep(DELAY);
}
In the game.update various components of the game have their position changed and those updates are reflected when the repaint() method is called on the view. The view extends JComponent and loops through the game objects and calls their print methods.
What I want to do is have a boolean called nextLevel in the game and if it's true Flash text on the screen for the player to notify them that they're going onto the next level. Maybe flash 4-5 times. Then continue the game.
Is this possible? I have been playing around with Thead.Sleep() but this only seems to pause the displaying and in the background the game is still going on.
Any ideas on how to do this?
Maybe you want to avoid threading by using a Timer object.
an example like that could be
int flashTimer = 0;
if(nextLevel) {
Timer timer = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
//flash something method here
flashTimer++;
}
});
timer.start();
}
and then check your flashTimer if it reaches the number you want then just stop the timer by timer.stop();
Just an idea which seems to me a bit simpler. the 1000 value is milliseconds which is passed and executes the code inside the actionPerformed method every 1 sec.
Hope it helped
I'm attempting to get an animation working in a game I'm developing. The animation works by setting a button size to very small, then gradually growing it to its normal size again. I have it working, except I'm having timing issues.
Sometimes the button will grow almost instantly, sometimes it goes VERY slow. I'm looking for something inbetween, and I need it to ALWAYS grow at that size, not some times fast sometimes slow.
I've looked into it and I found this pseudocode:
distance_for_dt = speed * delta_time
new_position = old_position + distance_for_dt
Unfortunately I don't understand what's being said, and I don't know how to apply this to my code. Can anyone help with that or explain what's being said in the above pseudocode?
Here's my timer code, timer is already defined above as a Timer, and z[] is just a pair of coordinates:
timer = new Timer(18, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Dimension dim = button[z[0]][z[1]].getSize();
if (dim.getHeight() < 79.9) {
button[z[0]][z[1]].setSize((int) (dim.getWidth() + 6), (int) (dim.getHeight() + 6));
} else {
button[z[0]][z[1]].setSize(80, 80);
timer.stop();
}
}
});
timer.start();
Depending on how many updates you're calling on your Swing application, it may be getting "backed up" and slowing down. For instance, if you wanted to accomplish the animation without a Timer, you could just do something like this:
// example method to do animation
public void animateButton(final int wait){
Thread thread = new Thread(){
public void run(){
// some loop structure to define how long to run animation
Dimension dim = button[z[0]][z[1]].getSize();
while (dim.getHeight() < 79.9){
SwingUtilities.invokeLater(new Runnable(){
//update Swing components here
});
try{ Thread.Sleep(wait); }
catch(Exception e){}
}
}
}
}
thread.start();
}
I think this may be similar to how a Timer updates the GUI, as Timers run on a separate thread. I would look into whether or not you need to use invokeLater(new Runnable) inside a timer to properly schedule the task. I had to do this to allow a project I was working on to keep responsive during long tasks. If you really needed to ensure the speed and maybe DROP updates to adjust for system lag, then you'll need to be calculating how complete the animation is vs how much time has passed, using a method call such as System.currentTimeMillis() or System.nanoTime(). Then, adjust accordingly for each step of the animation.