Why is the animation not showing and is slowing down the rest? - java

The animation isn't showing on the Pane background and it slows down the other animation already present. I pass on the Pane background as a parameter so I don't know maybe that is the cause but I have other methods doing that and it's not causing any problems.
public static void fireAnimation(Pane animatedBackground) {
AnimationTimer animationTimer = new AnimationTimer(){
#Override
public void handle (long now){
listFireParticles.addAll(addFireParticles(200,200));
for(Iterator<FireParticles> iteratorFirePart = listFireParticles.iterator(); iteratorFirePart.hasNext();){
FireParticles fireParticle = iteratorFirePart.next();
fireParticle.particlesUpdate();
if(!fireParticle.isAlive()){
iteratorFirePart.remove();
continue;
}
fireParticle.render(graphicsContext);
}
Canvas canvas = new Canvas (400, 400);
graphicsContext = canvas.getGraphicsContext2D();
animatedBackground.getChildren().add(canvas);
}
};
animationTimer.start();
System.out.println("ANIMATION");
}

You are creating a new canvas for each animation frame, rather than reusing an existing one.
You take a Pane and you add a new Canvas to it.
You do this an AnimationTimer handle method.
The timer method is called each pulse.
Be default, JavaFX generates a pulse sixty times a second.
In one second, your pane will contain sixty canvases.
In a minute there will be 3600 canvases.
In a day there will be five million one hundred and eighty four thousand canvases.
At some point before that, something is going to slow down and break.
FAQ
How would I make the canvas stop being created so many times? I can't figure it out.
Remove the lines which create a canvas and add it to the scene graph from the animation timer handle method.
Create the canvas and add it to the scene graph (e.g. a pane) only once, outside of the handle method.
Store a reference to the canvas and access the reference in the handle method.
You likely have other issues in code not shown, so don't expect a simple fix as outlined above to just make your application work as you expect.
Perhaps something roughly like this:
final int W = 200;
final in H = 200;
List<FireParticle> fireParticles = createFireParticles(W, H);
Canvas canvas = new Canvas (W, H);
animatedBackground.getChildren().add(canvas);
AnimationTimer animationTimer = new AnimationTimer() {
#Override
public void handle (long now){
GraphicsContext2D graphicsContext = canvas.getGraphicsContext2D();
graphicsContext.clearRect(0, 0, W, H);
if (fireParticles.isEmpty()) {
this.stop();
animatedBackground.getChildren().remove(canvas);
return;
}
for (Iterator<FireParticle> iteratorFirePart = fireParticles.iterator(); iteratorFirePart.hasNext();) {
FireParticle fireParticle = iteratorFirePart.next();
fireParticle.particlesUpdate();
if (!fireParticle.isAlive()){
iteratorFirePart.remove();
continue;
}
fireParticle.render(graphicsContext);
}
}
};
animationTimer.start();
Important: the above code is indicative only. I make no assertion it will exhibit the behaviour you wish. It is untested and won't work stand-alone, I didn't even try to compile it. Without the requested minimal example, that is the best that can be provided.

Related

Regulating the number of executions per second using JavaFX

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...
}

How to create an animated Image from still frames?

Given a list of true-color full frames in BufferedImage and a list of frame durations, how can I create an Image losslessly, that when put on a JLabel, will animate?
From what I can find, I could create an ImageWriter wrapping a ByteArrayOutputStream, write IIOImage frames to it, then Toolkit.getDefaultToolkit().createImage the stream into a ToolkitImage.
There are two problems with this attempt.
ImageWriter can only be instantiated with one of the known image encoders, and there is none for a lossless true-color animated image format (e.g. MNG),
It encodes (compresses) the image, then decompresses it again, becoming an unnecessary performance hazard.
[Edit]
Some more concise constraints and requirements. Please don't come up with anything that bends these rules.
What I don't want:
Making an animation thread and painting/updating each frame of the animation myself,
Using any kind of 3rd party library,
Borrowing any external process, for example a web browser,
Display it in some kind of video player object or 3D-accelerated scene (OpenGL/etc),
Work directly with classes from the sun.* packages
What I do want:
Frame size can be as large as monitor size. Please don't worry about performance. I'll worry about that. You'll just worry about correctness.
Frames all have the same size,
an Image subclass. I should be able to draw the image like g.drawImage(ani, 0, 0, this) and it would animate, or wrap it in an ImageIcon and display it on a JLabel/JButton/etc and it would animate,
Each frame can each have a different delay, from 10ms up to a second,
Animation can loop or can end, and this is defined once per animation (just like GIF),
I can use anything packaged with Oracle Java 8 (e.g. JavaFX),
Whatever happens, it should integrate with SWING
Optional:
Frames can have transparency. If needed, I can opaquify my images beforehand as the animation will be shown on a known background (single color) anyway.
I don't care if I have to subclass Image myself and add an animation thread in there that will cooperate with the ImageObserver, or write my own InputStreamImageSource, but I don't know how.
If I can somehow display a JavaFX scene with some HTML and CSS code that animates my images, then that's fine too. BUT as long as it's all encapsulated in a single SWING-compatible object that I can pass around.
You're right that ImageIO isn't an option, as the only animated format for which support is guaranteed is GIF.
You say you don't want to make an animation thread, but what about a JavaFX Animation object, like a Timeline?
public JComponent createAnimationComponent(List<BufferedImage> images,
List<Long> durations) {
Objects.requireNonNull(images, "Image list cannot be null");
Objects.requireNonNull(durations, "Duration list cannot be null");
if (new ArrayList<Object>(images).contains(null)) {
throw new IllegalArgumentException("Null image not permitted");
}
if (new ArrayList<Object>(durations).contains(null)) {
throw new IllegalArgumentException("Null duration not permitted");
}
int count = images.size();
if (count != durations.size()) {
throw new IllegalArgumentException(
"Lists must have the same number of elements");
}
ImageView view = new ImageView();
ObjectProperty<Image> imageProperty = view.imageProperty();
Rectangle imageSize = new Rectangle();
KeyFrame[] frames = new KeyFrame[count];
long time = 0;
for (int i = 0; i < count; i++) {
Duration duration = Duration.millis(time);
time += durations.get(i);
BufferedImage bufImg = images.get(i);
imageSize.add(bufImg.getWidth(), bufImg.getHeight());
Image image = SwingFXUtils.toFXImage(bufImg, null);
KeyValue imageValue = new KeyValue(imageProperty, image,
Interpolator.DISCRETE);
frames[i] = new KeyFrame(duration, imageValue);
}
Timeline timeline = new Timeline(frames);
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
JFXPanel panel = new JFXPanel();
panel.setScene(new Scene(new Group(view)));
panel.setPreferredSize(imageSize.getSize());
return panel;
}
(I don't know why it's necessary to set the JFXPanel's preferred size explicitly, but it is. Probably a bug.)
Note that, like all JavaFX code, it has to be run in the JavaFX Application Thread. If you're using it from a Swing application, you can do something like this:
public JComponent createAnimationComponentFromAWTThread(
final List<BufferedImage> images,
final List<Long> durations)
throws InterruptedException {
final JComponent[] componentHolder = { null };
Platform.runLater(new Runnable() {
#Override
public void run() {
synchronized (componentHolder) {
componentHolder[0] =
createAnimationComponent(images, durations);
componentHolder.notifyAll();
}
}
});
synchronized (componentHolder) {
while (componentHolder[0] == null) {
componentHolder.wait();
}
return componentHolder[0];
}
}
But that's still not quite enough. You first have to initialize JavaFX by calling Application.launch, either with an explicit method call, or implicitly by specifying your Application subclass as the main class.
something like
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(getImageForCurrentTime(), 3, 4, this);
}

Can't stop flickering on JPanel

I have class that creates a new thread.
`
public ScreenG(JPanel PanelR)
{
Panel = PanelR;
RenderImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
FPS = 25;
Hide = false;
(new Thread()
{
public void run()
{
while(true)
{
if(Hide == false)
{
Timer = System.currentTimeMillis() + (1000/FPS);
if(!DrawRendering)
{
Graphics g = Panel.getGraphics();
g.drawImage(RenderImage, 0, 0, null);
}
DrawRendering = false;
while(System.currentTimeMillis() <= Timer) try { Thread.sleep(1); } catch (InterruptedException e) {Thread.currentThread().interrupt();}
}
}
}
}).start();
}
public void draw(BufferedImage ImageR)
{
DrawRendering = true;
RenderImage = ImageR;
Graphics g = Panel.getGraphics();
g.drawImage(RenderImage, 0, 0, null);
}`
In my main I create a new instance of ScreenG. This will start a new thread that draws a bufferedImage onto a JPanel with a consistent FPS.
In the main I would then call draw with the image that I created. Sometimes it works but sometimes the image on the panel flickers. I try variations like the draw function taking over the drawing. Non of them work. I could only REDUCE the flickering.
Not possible by design. Swing does not synchronize to the bitmap raster DMA that's actually sending the screen data to your monitor, so it always possible that the screen buffer is read by the DMA while you're busy rendering to it (possible exception is Fullscreen mode).
To at least minimize flickering follow the recommended method of custom Swing painting: https://docs.oracle.com/javase/tutorial/uiswing/painting/
You can easily trigger periodic repaints on the EDT using a Swing timer, or SwingUtilities.invokeAndWait/invokeLater from another thread (whatever works best in your design).
The flickering can be because the rendering isn't fast enough from an update.
Now I do recommend you use Swings paintComponent (Graphics g) when rendering its components. That being said. To solve the flickering for you add a BufferStrategy in your JFrame
Without that code avaible I can only provide a general solution.
JFrame jframe = new JFrame ();
...
BufferStrategy bufferstrategy = jframe.getBufferStrategy ();
if (bufferstrategy == null) {
jframe.createBufferStrategy(3);
return;
}
g.dispose();
bufferstrategy.show();
To read more about BufferStrategy I recommend a read over at the documentation.
Small Note
There is no reason in your code to either store JPanel PanelR or BufferedImage ImageR. You can instead directly invoke methods directly on PanelR resp. ImageR.
Thank you for the answers. I read Oracle tutorial that you recommended and get my paintComponent() function working correctly on the main thread. To do that I am calling JPanel().repaint() from the draw() function. I will learn about using BufferStrategy next.

LibGDX loading scene2D widgets very slow for first time, need suggestions

I started using scene2D in my LibGDX game to make a more professional looking login/register menu. The only problem is that switching to those menus is very long for menu navigation (3-5 sec).
I want to know if there is a better way to load them before hand, like during the game's initial loading screen. The thing is that once one of the menus is loaded, it loads very quick the second time.
I know for sure that its the create method of my screens that takes this long. Here is all that it is loading:
public void create(){
stage = new Stage(new StretchViewport(1920, 1080));
Gdx.input.setInputProcessor(stage);
loginBox = new Image(Textures.Gui.BOX);
loginBox.setSize(650, 1000);
loginBox.setPosition(635, 40);
stage.addActor(loginBox);
loginLBL = new Label("Login", Archipelo.SKIN, "basic-large-font", Color.LIGHT_GRAY);
loginLBL.setPosition(880, 955);
stage.addActor(loginLBL);
selectionHighlight = new Image(Textures.Gui.SELECTION_HIGHLIGHT);
selectionHighlight.setSize(540, 140);
stage.addActor(selectionHighlight);
usernameTF = new TextField("", Archipelo.SKIN);
usernameTF.setMaxLength(24);
usernameTF.setPosition(usernameTFx, usernameTFy);
usernameTF.setSize(400, 60);
stage.addActor(usernameTF);
passwordTF = new TextField("", Archipelo.SKIN);
passwordTF.setPasswordMode(true);
passwordTF.setPasswordCharacter('•');
passwordTF.setPosition(passwordTFx, passwordTFy);
passwordTF.setSize(400, 60);
stage.addActor(passwordTF);
usernameLBL = new Label("Username", Archipelo.SKIN, "basic-medium-font", new Color(1, 1, 1, 0.5f));
usernameLBL.setPosition(usernameTFx + 10, usernameTFy + 5);
stage.addActor(usernameLBL);
passwordLBL = new Label("Password", Archipelo.SKIN, "basic-medium-font", new Color(1, 1, 1, 0.5f));
passwordLBL.setPosition(passwordTFx + 10, passwordTFy + 5);
stage.addActor(passwordLBL);
remember = new CheckBox(" Remember Login?", Archipelo.SKIN);
remember.setPosition(rememberX, rememberY);
remember.getCells().get(0).size(30, 30);
stage.addActor(remember);
errorLBL = new Label("", Archipelo.SKIN, "basic-small-font", Color.RED);
errorLBL.setPosition(750, 650);
errorLBL.setWrap(true);
errorLBL.setBounds(750, 500, 400, 250);
stage.addActor(errorLBL);
continueLBL = new Label("Continue", Archipelo.SKIN, "basic-big-font", Color.WHITE);
continueLBL.setPosition(875, 100);
stage.addActor(continueLBL);
}
Also, I load the uiskin files before in the game's initial loading screen. Archipelo.SKIN is a static variable that refers to that uiskin. I also wanted to mention that my screen class is custom made and that whenever create() is called it is because a new screen instance is being created.
The thing that I don't get is why it takes so long to create the screen the first time and then every other time, it still goes through the same process except its much faster. Is there a way to make it faster the first time?
Thanks in advance. If you need more info by all means ask.
To summarize the comments...
The OP had a convenience class for texture references, that listed the textures like this:
public class Gui {
public static final Texture BOX = new Texture("box.png");
public static final Texture SELECTION_HIGHLIGHT= new Texture("selectionHighlight.png");
//...
}
Since they are declared static, they are members of the class, not an instance of a class. Static members of a class are all initialized at once, but only the first time the class or an instance of the class is accessed. This setup results in all the Gui textures getting loaded all at once at some inopportune, unplanned time.
The Texture's constructor Texture(String filename) causes a texture to be loaded from a file, which is time-consuming, so the loading of the Gui class takes a few seconds.
The solution is to not instantiate member texture variables in their declaration. Instantiate them within some method so you can decide exactly when they should be loaded.

Java .drawImage : How do I "unDraw" or delete a image?

I need a certain image to be redrawn at different locations constantly as the program runs. So I set up a while loop that should move an image across the screen, but it just redraws the image on top of itself over and over again. What am I doing wrong? Is there a way to delete the old image before drawing it in a new location?
JFrame frame = buildFrame();
final BufferedImage image = ImageIO.read(new File("BeachRoad_double_size.png"));
JPanel pane = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int num = 0;
boolean fluff = true;
while (fluff == true) {
num = num + 1;
g.drawImage(image, num, 0, null);
if (num == 105) {
fluff = false;
}
}
}
};
frame.add(pane);
You can't code a loop in the paintComponent() method. The code will execute so fast that the image will only be painted in the final position, which in your case should be with an x position of 105.
Instead you need to use a Swing Timer to schedule the animation every 100 milliseconds or so. Then when the timer fires you update the x position and invoke repaint() on the panel. Read the Swing tutorial on Using Swing Timers for more information.
Putting a while loop inside a paintComponent method is not the way to do it. Instead, there should be some setup like the following:
...
final int num = 0;
final JPanel pane;
Timer timer = new Timer(10, new ActionListener() {
public void actionPerformed(ActionEvent e) {
num++;
pane.repaint();
}
});
pane = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, num, 0, null);
}
});
timer.start();
This will move the image ever 10 milliseconds, as specified in the Timer constructor.
This is a common issue people starting out in animation have, as I did. You can't 'remove an image' from the screen. However, you can repaint the entire screen, then redraw your image at a new location.
In psuedocode:
while (condition)
background(white); //or whatever color your background is
drawImage(x,y);
The code above clears the screen so it's safe for you to redraw your image. This effectively 'deletes' your image.
Edit: I didn't read your code, I just addressed your question. So other answers that fix your code are probably better than mine.

Categories

Resources