4

I'm making a game engine with JavaFX and I've come across this problem that I'm struggling to fix. There is a one pixel wide white line on the right side of my window that shouldn't be there:

The problem appears to be caused by my Scene being ~0.6 pixels too large, even though my canvas is the correct size. However, I don't know what to do to fix it.

Running the following lines gives me the following outputs, respectively:

System.out.println(sr.getCanvas().getWidth());
System.out.println(GameEngine.scene.getWidth());
System.out.println(GameEngine.stage.getWidth());

System.out.println(sr.getCanvas().getHeight());
System.out.println(GameEngine.scene.getHeight());
System.out.println(GameEngine.stage.getHeight());
1280.0
1280.6259765625
1280.6259765625

720.0
720.102783203125
749.026611328125

Creating the canvas:

public void createCanvas() {
    canvas = new Canvas(GameEngine.getConfig().width,GameEngine.getConfig().height);
    gc = canvas.getGraphicsContext2D();
}

Creating the window:

private void createWindow() {
    root = new Pane();
    scene = new Scene(root);
    
    stage.setTitle(config.title);
    stage.setScene(scene);
    
    for(Renderer r : GameEngine.renderers) {
        r.createCanvas();
    }
    
    for(Renderer r : renderers) {
        root.getChildren().add(r.getCanvas());
    }
}

I've been looking all over trying the couple things I could find but they haven't worked, including:

  • Running stage.hide(), then stage.show()
  • Running stage.setResizeable(false) and manually editing the stage's width and height
  • Running this code I found on another StackOverflow answer:
stage.setWidth(scene.getRoot().minWidth(-1));
stage.setHeight(scene.getRoot().minHeight(-1));

Any ideas? I'm willing to give more information if needed, I'm apparently not great at giving detailed enough answers so hopefully this was alright. Thanks!

EDIT: Here's a reproducible example of my problem (at least on my device): TestMain.class

public class TestMain {
    
    public static void main(String[] args) {
        Application.launch(TestApp.class);
    }
}

TestApp.class

public class TestApp extends Application {
        
    GraphicsContext gc;
    Stage stage;
    Pane root;
    Scene scene;
    int width;
    int height;
    Canvas canvas;
    
    public TestApp() {}
    
    public void fillCanvas(Paint c) {
        gc.setFill(c);
        gc.fillRect(0, 0, 1280, 720);
    }
    
    public void testLoop() {
        new AnimationTimer() {
            @Override
            public void handle(long nanoTime) {
                fillCanvas(new Color(0.3, 0.3, 0.3, 1));
            }
        }.start();
    }

    @Override
    public void start(Stage arg0) throws Exception {
        Stage stage = new Stage();
        Pane root = new Pane();
        Scene scene = new Scene(root);
        
        int width = 1280;
        int height = 720;
        
        Canvas canvas = new Canvas(width, height);
        gc = canvas.getGraphicsContext2D();
        
        stage.setScene(scene);
        
        root.getChildren().add(canvas);
        stage.show();
        testLoop();
    }
}

If anything else is needed please let me know!

EpicOweo
  • 63
  • 4
  • 1
    Thanks. However, I do not see the white edge on my system (JDK 20, JavaFX 20.0.1, Mac OS X). What JDK/JavaFX versions do you have and which OS? – James_D Jun 02 '23 at 16:51
  • @James_D That's odd. I'm running Java 17 OpenJDK, JavaFX 20.0.1, and OpenSuse TW. I recreated the example there from scratch so I don't see what the issue could be. – EpicOweo Jun 02 '23 at 17:16
  • I'm not sure if this matters, but running `System.out.println(stage.getWidth())` (and then with scene width and canvas width) produces slightly different values: `1282.620`, `1280.625`, and `1280.0`, respectively. – EpicOweo Jun 02 '23 at 17:20
  • 1
    I can't reproduce this on MacOS, Java/FX 17. Also consider the approaches examined [here](https://stackoverflow.com/q/31761361/230513) that fill the entire canvas. – trashgod Jun 02 '23 at 17:57
  • On a Mac, the sample code (with the added outputs for canvas, stage, and scene height and width), gives canvas 1280.0 x 720.0, scene 1280.0 x 720.0, stage 1280.0 x 748.0 with the extra height in the stage to account for the title bar. That is why the problem does not reproduce on a Mac. On a Mac the canvas fills the scene entirely so the default which scene background fill is not seen, unlike on your OpenSuse TW system. – jewelsea Jun 02 '23 at 18:58
  • 2
    Normally the scene is supposed to precisely size to its content. But for some reason, the calculations for that were slightly wrong on your system. Perhaps if you manually set the size of the scene to 1280 x 720 in the constructor, rather than letting the system calculate a desired size, it will work. You also might want to make sure that the stage is not resizable or has a max size. Otherwise, if the user resizes the scene to enlarge it, it may look weird. – jewelsea Jun 02 '23 at 19:04

3 Answers3

0

I didn't find the source of the problem but I found a workaround. Basically the idea is to just use scene.setFill() to clear the screen. When I do that, it clears the entire screen even though the Scene's size is a pixel off. I also will probably still clear the canvas every frame as well to make sure nothing weird happens though.

EDIT: Also helps to make the canvas one pixel bigger so that whatever you're drawing spans the entire window.

EpicOweo
  • 63
  • 4
  • 3
    [`scene.setFill()`](https://openjfx.io/javadoc/20/javafx.graphics/javafx/scene/Scene.html#fillProperty) sets the background of the scene. It does not "clear the screen". The scene graph is persistent. The canvas is rendered on top of the background, so setting the scene fill does not affect the canvas. Also clearing the canvas every frame will only clear the canvas, not the area outside of the canvas (where the line you see resides). Setting the canvas one pixel bigger, however, will ensure that (in your case) the canvas spans the entire scene and fix your issue. – jewelsea Jun 02 '23 at 19:08
0

I can't reproduce this either on Windows 10/Ubuntu 22.04 (both running JDK/JavaFX 20). If you're fine with not letting the user resize the game window, try the following code, which should prevent resizing* of the window/stage and sets an initial size for the scene.

stage.setResizable(false);
Scene scene = new Scene(root, 1280, 720);

* Do note that there are a few WMs that don't respect this setting.

0

UPDATE: I figured out what actually works! Making the canvas bigger just worsened the problem, so after some playing around I bound the maxWidthProperty/maxHeightProperty to the Canvas' width/height properties minus one.

stage.maxWidthProperty().bind(canvas.widthProperty().subtract(1));
stage.maxHeightProperty().bind(canvas.heightProperty().subtract(1));

Since this seems to mainly be a me problem, I just included these as a seperate patch function in my code in case someone using my engine also has the issue.

EpicOweo
  • 63
  • 4