1

I'm making a javafx gui application/game and need to update character view concurrently. Every move cycle i update the ImageView of the character as follows:

if (inBounds(direction) && !Collisions.collides(newHitbox, hitbox)) {
        this.setX(direction.getX() * this.getVelocity() + (int) this.view.getX());
        this.setY(direction.getY() * this.getVelocity() + (int) this.view.getY());
        this.hitbox.setX(direction.getX() * this.getVelocity() + this.hitbox.getX());
        this.hitbox.setY(direction.getY() * this.getVelocity() + this.hitbox.getY());
    }

I am using a spritesheet for the illusion of movement, so I update the viewport every four cycles.

For a while everything works fine, but at some point i get the following error :

java.lang.IndexOutOfBoundsException: Index -1 out of bounds for length 3
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
at java.base/java.util.Objects.checkIndex(Objects.java:372)
at java.base/java.util.ArrayList.get(ArrayList.java:458)
at com.sun.javafx.collections.ObservableListWrapper.get(ObservableListWrapper.java:89)
at com.sun.javafx.collections.VetoableListDecorator.get(VetoableListDecorator.java:306)
at javafx.scene.Parent.updateCachedBounds(Parent.java:1701)
at javafx.scene.Parent.recomputeBounds(Parent.java:1645)
at javafx.scene.Parent.doComputeGeomBounds(Parent.java:1498)
at javafx.scene.Parent.access$200(Parent.java:79)
at javafx.scene.Parent$1.doComputeGeomBounds(Parent.java:115)
at com.sun.javafx.scene.ParentHelper.computeGeomBoundsImpl(ParentHelper.java:84)
at com.sun.javafx.scene.layout.RegionHelper.superComputeGeomBoundsImpl(RegionHelper.java:78)
at com.sun.javafx.scene.layout.RegionHelper.superComputeGeomBounds(RegionHelper.java:62)
at javafx.scene.layout.Region.doComputeGeomBounds(Region.java:3289)
at javafx.scene.layout.Region.access$300(Region.java:147)
at javafx.scene.layout.Region$1.doComputeGeomBounds(Region.java:168)
at com.sun.javafx.scene.layout.RegionHelper.computeGeomBoundsImpl(RegionHelper.java:89)
at com.sun.javafx.scene.NodeHelper.computeGeomBounds(NodeHelper.java:115)
at javafx.scene.Node.updateGeomBounds(Node.java:3837)
at javafx.scene.Node.getGeomBounds(Node.java:3799)
at javafx.scene.Node.updateBounds(Node.java:771)
at javafx.scene.Parent.updateBounds(Parent.java:1832)
at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2497)
at com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:412)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:411)
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:438)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:519)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:499)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:492)
at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:320)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run$$$capture(InvokeLaterDispatcher.java:96)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java)
at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
at com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:277)
at java.base/java.lang.Thread.run(Thread.java:834)

Because this deosn't happen consistently I assume that at some point the threads try to access the parent of their ImageView at the same time and something breaks.

I was unable to localize the error as the error message doesn't tell me much about where in my code this happens. I tried to make the methods which update the view synchronized as well as the part of the code synchronized with the Parent object as their monitor, but that didn't help at all.

Personally I don't use those Bounds which are getting updated, so if there was a possibility to turn the recomputing off that might do.

Vilup
  • 19
  • 3
  • 3
    JavaFX, like most UI toolkits, is not thread safe. You **must** read/write the state of a live scene graph on the _JavaFX Application Thread_. Doing so on another thread can lead to _undefined behavior_ such as the `IndexOutOfBoundsException` you're getting. If you need to do something related to the UI from a background thread, use [`Platform.runLater(Runnable)`](https://openjfx.io/javadoc/12/javafx.graphics/javafx/application/Platform.html#runLater(java.lang.Runnable)). – Slaw May 11 '19 at 12:06
  • Thanks a lot. runLater helped. Is there a downside to not updating the UI directly on the Application thread but through this method? – Vilup May 11 '19 at 12:46
  • This method receives a `Runnable` that is executed on the JavaFX application thread. The update is actually done on the application thread. Using `runLater` too frequently though may slow down updates or possibly even effectively freeze the GUI... – fabian May 11 '19 at 14:29

0 Answers0