godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.11k stars 69 forks source link

When running multiple instances, open windows next to each other instead of on top #10262

Open blackears opened 1 month ago

blackears commented 1 month ago

Describe the project you are working on

A networked soccer game

Describe the problem or limitation you are having in your project

Debugging networked programs can be tough, especially because you often need multiple programs running at the same time. While the Debug/Run Multiple Instances provides a useful way to start many sessions at once, you must still manually resize and move the windows each time you start a new session. It would be really helpful if your multiple windows would open next to each other rather than exactly on top of each other.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Add a 'tiled' checkbox option to the Debug/Run Multiple Instances section. When checked, the extra windows will be laid out next to each other in a grid instead of on top of each other.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

When you run, windows will be smaller and laid out in a grid instead of on top of each other.

If this enhancement will not be used often, can it be worked around with a few lines of script?

No

Is there a reason why this should be core and not an add-on in the asset library?

Requires changing how programs are launched.

KoBeWi commented 1 month ago

It can be worked around by manually changing window position based on launch arguments.

blackears commented 1 month ago

Can it be configured in project settings? Or do you need to run script commands in the _ready() of your first scene?

KoBeWi commented 1 month ago

Both is possible actually.

display/window/size/initial_position allows to define initial position of the window. You can use feature overrides to define multiple positions and then run your instances with different feature tags. However seems like you can't use custom tags in the settings dialog, so you can either add these settings manually in project.godot or use existing tags. image Then provide these tags for the instances: image (though using default tags in this way is not recommended; better to use custom tags)

It's easier from code, if you have autoload singleton just set the position in _init(), so it's moved at the very beginning.

noidexe commented 2 days ago

I think this is something Godot can and should do on it's own, for the following reasons:

To flesh out the proposal a bit more I think the new settings could be the following:

Mockup

image

Description

Prop Name Description
Multiple Instance Placement Style Defines placement approach to use when running multiple instances.

None (default): Do nothing. Preserve the current behavior
Cascading: Use a cascading layout.
Tiling: Use a grid layout
Tile Grid Size How to divide the screen space. Defaults to 2x1 (side-to-side split screen)
Preserve Aspect Ratio Off (default) : Each instance is resized to fully cover its assigned tile
On: Each instance is resized so it fits inside it's assigned tile, while respecting the aspect ratio defined in "display/window/size/viewport_width" and "display/window/size/viewport_width"

Some common questions:

Q: Why add cascading? No one requested that A: I added it for completion since most window managers offer it, and we can easily get the title bar size with DisplayServer::window_get_title_size. Feel free to ignore it if you think it's unnecessary bloat.

Q: Why "Tile Grid Size" instead of just side by side? A: Because some users might want two instances side-by-side, others might want them top and bottom, others might have 4 instances instead. By letting the user just specify the grid size you cover all those cases with the same implementation

Q: What happens if the user sets the amount of instances to say, four, and the grid has fewer divisions, e.g: 2x1? A: The tile index wraps around and, in the example, you'd get instances 3 and 4 on top of 1 and 2 respectively. It's the responsibility of the user to set a grid size that makes sense for the amount of running instances. "Auto" could be an option but I don't know if changing the amount of running instances all the time is a common use case.

Q: What's the point of "Preserve Aspect Ratio"? A: Some games have UI and cameras designed for a specific aspect ratio (usually the 16:9 standard), so we might want to support that use case. Again feel free to ignore it if you think it's unnecesary

Implementation details

In editor_run.cpp around https://github.com/godotengine/godot/blob/88ed6af1e6844908293aa1599421b40870be513c/editor/editor_run.cpp#L125 we need to:

Workaround

@blackears I'm using this to have side by side windows when I test the project I'm working on

image

Then on some autoload:


@export var SPLIT_SCREEN_STYLE := VERTICAL
@export var USE_RATIO := true

func _ready():
    var screen_rect = DisplayServer.screen_get_usable_rect()
    var screen_ratio = screen_rect.size.aspect()
    if SPLIT_SCREEN_STYLE == HORIZONTAL:
        get_window().size.x = screen_rect.size.x / 2
        get_window().size.y = get_window().size.x / screen_ratio if USE_RATIO else screen_rect.size.y
        get_window().position.y = screen_rect.position.y
    else:
        get_window().size.y = screen_rect.size.y / 2
        get_window().size.x = get_window().size.y * screen_ratio if USE_RATIO else screen_rect.size.x
        get_window().position.x = screen_rect.position.x

    if "--server" in OS.get_cmdline_args():
        get_window().position = screen_rect.position
    elif "--client" in OS.get_cmdline_args():
        if SPLIT_SCREEN_STYLE == HORIZONTAL:
            get_window().position.x = screen_rect.size.x / 2
        else:
            get_window().position.y = screen_rect.size.y / 2