AlmasB / FXGL

Java / JavaFX / Kotlin Game Library (Engine)
http://almasb.github.io/FXGL/
MIT License
4.42k stars 553 forks source link

Preserve scene ratio #634

Closed iamgio closed 5 years ago

iamgio commented 5 years ago

Hi Almas, basically, I want to make my game resizable, so I set manualResizeEnabled to true. The built-in scale mechanism works well, except for the fact that I want to preserve the original ratio. I opted to create rectangles on both left and right of the scene so that the window could be resized as much as wanted, so that the actual game resolution would be the same. Honestly, I don't know how to do this in FXGL since width/scale properties aren't modified in contentRoot and I don't know where to set listeners.

AlmasB commented 5 years ago

Hi, could this be related to #624 ? Could you clarify what properties you need access to?

AlmasB commented 5 years ago

I've just re-read this and I think I got the idea. During resize if the ratio is different, append black bars (similar to video playing software) either top/bot or left/right, so that the ratio remains the same. Is that right?

If so, this should probably be implemented as a setting: settings.setPreserveResizeRatio(true) or smth along these lines

iamgio commented 5 years ago

Yes, that's exactly what I meant (I'm not native English, sorry). That implementation would be great.

AlmasB commented 5 years ago

I am unsure about black bars. The original background color is transparent, so I think it would be sensible to leave the bars as transparent (effectively white). Would that work for you?

iamgio commented 5 years ago

Would that work for you?

That's not a problem. However, couldn't the color be set by changing -fx-background-color of the content root?

AlmasB commented 5 years ago

I think so, which means even if I don't set the color, you will be able to update it as necessary.

AlmasB commented 5 years ago

To get the latest build (once it completes), you can use this:

<dependencies>
    <dependency>
        <groupId>com.github.almasb</groupId>
        <artifactId>fxgl</artifactId>
        <version>dev-SNAPSHOT</version>
    </dependency>
</dependencies>

<repositories>
    <repository>
        <id>oss.sonatype.org-snapshot</id>
        <url>http://oss.sonatype.org/content/repositories/snapshots</url>
    </repository>
</repositories>
AlmasB commented 5 years ago

Need to enable both

settings.setManualResizeEnabled(true);
settings.setPreserveResizeRatio(true);
iamgio commented 5 years ago

Is this build compatible with Java < 11?

AlmasB commented 5 years ago

No, I'm afraid. Only 11+ is being developed. As FXGL 11 has a completely new architecture, it is not sensible to develop both, especially considering it is superior to FXGL 8 arch. Eventually, there might be a backport of the 11 codebase to 8 but no plans at the moment.

iamgio commented 5 years ago

Is it possible to align the game scene to center?

Image

AlmasB commented 5 years ago

You can play around with root or contentRoot of the game scene. Maybe layoutX/Y or translateX/Y can offset the scene as required.

iamgio commented 5 years ago

I think it's not working as expected. Set initial size to 1280x720, I manually resized the window to a similar size but quite smaller than the original one. gameScene.root (orange) should cover the whole window because w1/h1 == w2/h2. Also, gameScene.contentRoot (gray) is able to go out of the root.

Image

I've made this image that shows how the scene (initial size 1280x720, w/h = 1.77) could be resized every time the window size is modified:

Image2

AlmasB commented 5 years ago

I think I have an idea why this is happening. The root scaling is fixed to preserve ratio, but scene size isn’t. I don’t have a workaround in mind at the moment. I am going to come back to it a bit later and see if I can fix this

On Mon, 10 Jun 2019 at 2:29 pm, iAmGio notifications@github.com wrote:

I think it's not working as expected. Set initial size to 1280x720, I manually resized the window to a similar size but quite smaller than the original one. gameScene.root (orange) should cover the whole window because w1/h1 == w2/h2. Also, gameScene.contentRoot (gray) is able to go out of the root.

[image: Image] https://camo.githubusercontent.com/c817468e360f85c0a3b57118a43932dfb0610cb0/68747470733a2f2f692e696d6775722e636f6d2f6d48434c3649502e706e67

I've made this image that shows how the scene (initial size 1280x720) could be resized every time the window size is modified:

[image: Image2] https://camo.githubusercontent.com/cef8edc22404bdf8a1a63af99333779264691dd0/68747470733a2f2f692e696d6775722e636f6d2f7a4652374233722e706e67

— You are receiving this because you commented.

Reply to this email directly, view it on GitHub https://github.com/AlmasB/FXGL/issues/634?email_source=notifications&email_token=AA3NT5WYKLACOJYKK4UTOKLPZZJMNA5CNFSM4HWLXJ72YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXJ4EBQ#issuecomment-500417030, or mute the thread https://github.com/notifications/unsubscribe-auth/AA3NT5RXSVYE5JR75TXEXJ3PZZJMNANCNFSM4HWLXJ7Q .

iamgio commented 5 years ago

The formula would be: scene = window * w2/h2 / w1/h1 where: scene = width (if w1/h1 < w2/h2) or height of the game scene. window = width or height (as above) of the window w1/h1 = ratio of width to height of the original window w2/h2 = ratio of width to height of the resized window

AlmasB commented 5 years ago

So effectively, 2 fixes are required for this:

iamgio commented 5 years ago

Yes.

AlmasB commented 5 years ago

Done. Give CI a few minutes to build and upload, then just refresh your dev-SNAPSHOT dependency.

You can set color to background either way:

getGameScene().getRoot().setBackground(new Background(new BackgroundFill(Color.BLACK, null, null)));
getGameScene().getRoot().setStyle("-fx-background-color: black");
iamgio commented 5 years ago

The fixes look great. However, the content root can still expand out of the game scene: Image

Orange = gameScene.root; Blue = UI node that should match the correct content root size; How you can see, my character is able to go out of it.

AlmasB commented 5 years ago

Could you post the minimal example please

On Mon, Jun 17, 2019 at 6:20 PM iAmGio notifications@github.com wrote:

The fixes look great. However, the content root can still expand out of the game scene: [image: Image] https://camo.githubusercontent.com/efa0cadd481755cc3859ebb0170a68872f731ef6/68747470733a2f2f692e696d6775722e636f6d2f525579784e744d2e706e67

Orange = gameScene.root; Blue = UI node that should match the correct content root size; How you can see, my character is able to go out of it.

— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/AlmasB/FXGL/issues/634?email_source=notifications&email_token=AA3NT5S5HFC2D6ZHND3LRZ3P27BVTA5CNFSM4HWLXJ72YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODX33VCQ#issuecomment-502774410, or mute the thread https://github.com/notifications/unsubscribe-auth/AA3NT5XR46BJA22MLDYNRHLP27BVTANCNFSM4HWLXJ7Q .

iamgio commented 5 years ago

Player component: https://hastebin.com/itudoxadod.m For visibility (in initGame()):

getGameScene().root.style = "-fx-background-color: orange"
getGameScene().addUINode(Rectangle(getAppWidth().toDouble(), getAppHeight().toDouble(), Color(.0, .0, 1.0, .5)))
AlmasB commented 5 years ago

Sorry, I meant a minimal example that I can run, so App : GameApplication(), etc. that demonstrates the issue

iamgio commented 5 years ago
import com.almasb.fxgl.app.GameApplication;
import com.almasb.fxgl.app.GameScene;
import com.almasb.fxgl.app.GameSettings;
import com.almasb.fxgl.dsl.FXGL;
import com.almasb.fxgl.entity.*;
import com.almasb.fxgl.input.Input;
import com.almasb.fxgl.input.UserAction;
import javafx.scene.input.KeyCode;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;

public class Demo extends GameApplication {

    private Entity player;

    @Override
    protected void initSettings(GameSettings settings) {
        settings.setManualResizeEnabled(true);
        settings.setPreserveResizeRatio(true);
        settings.setWidth(1280);
        settings.setHeight(720);
        settings.setTitle("Demo");
    }

    @Override
    protected void initInput() {
        Input input = FXGL.getInput();
        input.addAction(new UserAction("Up") {
            @Override
            protected void onAction() {
                player.translateY(-5.0);
            }
        }, KeyCode.W);
        input.addAction(new UserAction("Right") {
            @Override
            protected void onAction() {
                player.translateX(5.0);
            }
        }, KeyCode.D);
        input.addAction(new UserAction("Down") {
            @Override
            protected void onAction() {
                player.translateY(5.0);
            }
        }, KeyCode.S);
        input.addAction(new UserAction("Left") {
            @Override
            protected void onAction() {
                player.translateX(-5.0);
            }
        }, KeyCode.A);
    }

    @Override
    protected void initGame() {
        GameWorld gameWorld = FXGL.getGameWorld();
        gameWorld.addEntityFactory(new DemoEntityFactory());
        player = gameWorld.spawn("player", new SpawnData(200, 200));
    }

    @Override
    protected void initUI() {
        GameScene gameScene = FXGL.getGameScene();
        gameScene.addUINode(new Rectangle(FXGL.getAppWidth(), FXGL.getAppHeight(), new Color(.0, .0, 1.0, .5)));
    }

    public static void main(String[] args) {
        GameApplication.launch(args);
    }

    public class DemoEntityFactory implements EntityFactory {

        @Spawns("player")
        public Entity newPlayer(SpawnData data) {
            return FXGL.entityBuilder()
                    .at(data.getX(), data.getY())
                    .view(new Rectangle(100.0, 100.0, Color.BLACK))
                    .build();
        }
    }
}
AlmasB commented 5 years ago

I think I've finally understood the issue :) I adapted your initial approach with Rectangles.

You should be able to do this:

getGameScene().getPaddingTop().setFill(Color.YELLOW);
getGameScene().getPaddingBot().setFill(Color.RED);
getGameScene().getPaddingRight().setFill(Color.GREEN);
getGameScene().getPaddingLeft().setFill(Color.BLUE);

which actually gives you more control over each padding

AlmasB commented 5 years ago

build on its way

iamgio commented 5 years ago

It seems to work well, thank you!