Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Embed game process in editor #99010

Merged
merged 1 commit into from
Dec 20, 2024

Conversation

Hilderin
Copy link
Contributor

@Hilderin Hilderin commented Nov 10, 2024

Implements game embedding for Windows and Linux (X11 only) in the new Game Workspace: GitHub PR #97257

As suggested, this implementation places the running game window as a child of the editor and embeds it inside the Game Workspace. The running game remains a separate process, with all keys and inputs handled in a separate event loop to maintain performance even when embedded.

image

image

U7w7iZ8zil.mp4

New Options in the Game Workspace Toolbar

  • Embed game on Next Play: On/Off: Enables or disables embedding the next time the game is started. (Default: Enabled)

    • If multiple instances are configured, only the first instance will be embedded.
  • Make Game Workspace floating on Next Play: On/Off: When enabled, the Game Workspace opens in a floating window when the game starts. (Default: Enabled)

    • This option is not available when "Embed game on Play" is Off.
    • This option is not available in single-window mode.
  • Keep the aspect ratio of the embedded game: On/Off: Maintains the aspect ratio of the game window in the Game Workspace while embedding is enabled. (Default: Enabled)


Important Information

  • When embedded, some display settings cannot be changed at runtime to prevent the game window from exiting the Game Workspace. These settings will generate an error if modified during runtime in embedded mode:
    • Window Mode
    • Size
    • Minimum size
    • Maximum size
    • Position
    • Resizable
    • Always on Top
    • Popup
    • Current screen

Additional Features

  • Added the Engine.is_embedded_in_editor() method in GDScript/C#, which helps prevent errors when attempting to change unsupported window settings while embedded or for adjusting behavior based on whether embedding is active.
    • The same information can also be retrieved using OS.has_feature("embedded_in_editor").

Making Your Game "Embedded-Compatible"

  • Handling Mouse Capture: Currently, no default keyboard shortcut exists to exit mouse capture mode when embedded, which can be inconvenient for games like the TPS Demo that hide the mouse cursor during gameplay. As a workaround, you can modify the game to toggle the mouse cursor on pressing the Escape key instead of returning to the menu. Here’s an example for level.gd:

    func _input(event):
        if event.is_action_pressed("quit"):
            if Engine.is_embedded_in_editor():
                if Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE:
                    Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
                else:
                    Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
            else:
                emit_signal("quit")
  • Preventing Fullscreen Errors: In the TPS Demo, errors occur when starting the game in fullscreen mode while embedded, as the DisplayServer does not allow window mode changes in embedded mode. You can prevent these errors using a simple check:

    if !Engine.is_embedded_in_editor():
        get_window().mode = Settings.config_file.get_value("video", "display_mode")

Known Issues

  • Windows: If the user focuses on the embedded game and quickly clicks a button in the editor, the click may not register. This seems to be due to Windows taking too long to reactivate the editor window, causing the mouse-up event to be missed. Disabling Unfocused Low Power Mode while the game is embedded mitigates this issue but is not a perfect solution.

  • Linux: X11 does not support moving windows outside screen bounds. If the editor window is moved outside the screen boundaries while embedding a game, the window is resized to prevent Linux from resetting its position to the screen edge. This limitation appears to be a known issue without a programmatic workaround.

  • Scene Previews Disabled: When the Game Workspace tab is active, scene previews are disabled because they appear under the embedded game. Since previews are standard controls rather than popups, this is a temporary workaround to prevent issues, though it disables scene previews. Addressing this in a future PR is recommended.

  • Game Process Recording: Tools like OBS Studio or Game Bar cannot record the embedded game by capturing the Godot Editor process. To record the game, you must capture the entire screen or a specific section of it. This limitation is expected given the separate processes for the editor and game.

  • Single Window Mode and Popups/Tooltips: When the game is embedded and the editor runs in single-window mode, all popups and tooltips are displayed beneath the embedded game process.

  • Embedded window can be moved with OS shortcuts: When the game is embedded some Windows or Linux shortcuts came be used to move the game window. I was not able to find a way to disable those shortcuts. On Windows we are talking about WindowKey+Shit+Right/Left and on Linux WindowKey+LeftMouseButton.


Testing

  • Windows: Tested on Windows 11 (1, 2, and 3 monitors) and Windows 10 (1 monitor).
  • Linux: Tested on Ubuntu 24.04 (1 and 2 monitors) and Fedora 40 KDE Plasma 6.1.5 (1 and 2 monitors).

@SheepYhangCN
Copy link
Contributor

Added the Engine.is_embedded() method in GDScript/C#

Add embedded into has_feature might be a better solution to ensure consistency since it is similar to the feature tags like debugor editor?

@AThousandShips AThousandShips changed the title Embedding game process in editor Embed game process in editor Nov 10, 2024
scene/scene_string_names.h Outdated Show resolved Hide resolved
servers/display_server.h Outdated Show resolved Hide resolved
@KoBeWi
Copy link
Member

KoBeWi commented Nov 10, 2024

If multiple instances are configured, only the first instance will be embedded.

Is this a limitation? If not, you could embed multiple instances inside a TabContainer.

EDIT:
Also the WINDOW_FLAG_HIDDEN allows for making apps that minimize to tray I think? (we already support tray icons)

@KoBeWi

This comment was marked as resolved.

@Hilderin Hilderin force-pushed the embedding-game-process branch from 9b9207e to abeae2c Compare November 10, 2024 11:48
@Hilderin
Copy link
Contributor Author

Add embedded into has_feature might be a better solution to ensure consistency since it is similar to the feature tags like debugor editor?

I added "embedded" to OS.has_feature to returns true when the game is running embedded. I still kept Engine.is_embedded to be consistent with Engine.is_editor_hint. Sounds good?

@Hilderin Hilderin force-pushed the embedding-game-process branch from abeae2c to c34b874 Compare November 10, 2024 12:05
@akien-mga akien-mga merged commit a11364d into godotengine:master Dec 20, 2024
20 checks passed
@akien-mga
Copy link
Member

Thanks for the amazing work @Hilderin, this complements the new Game tab very well!
Let's get this well-tested over the Christmas break so we can iron things out for 4.4 beta 1 in early January.

@stuartcarnie
Copy link
Contributor

stuartcarnie commented Dec 21, 2024

@akien-mga / @bruvzg OpenEmu does what you describe for macOS, as it spawns a child XPC process, and uses a remote layer (the Metal / Vulkan frame buffer), to display in the parent process, which is the most efficient. WebKit does this, by running subprocesses for the browser engine and displaying them all in a single, parent process.

I might take a look at that, if no one else is.

@nevyn
Copy link

nevyn commented Jan 17, 2025

@akien-mga / @bruvzg OpenEmu does what you describe for macOS, as it spawns a child XPC process, and uses a remote layer (the Metal / Vulkan frame buffer), to display in the parent process, which is the most efficient. WebKit does this, by running subprocesses for the browser engine and displaying them all in a single, parent process.

I might take a look at that, if no one else is.

I’ve worked with IOSurface before and always wanted an excuse to do more XPC on macOS, so I’m also putting this on my todo and hope to get to contribute unless someone gets there before me :)

@MikeGO2611
Copy link

MikeGO2611 commented Jan 17, 2025

I've been testing this feature (don't know if I have to report this on a new ticket, or here as is for this feature).

I'm testing with dual-monitor. If I use the Embed Game Process, the settings configured in Editor Settings > Run > Window Placement are ignored (I have set the Screen Placement on Next Screen, but the embed game process appears on the last sceen, place and size). If I disable the embed game, the settings work as expected.

Another issue is that once the embed game is disabled from the embed window there is no place to turn it back on (I might missed the setting? I looked on the Editor, Project and Debug Settings), the only place to set it up again is from the Game tab. The embed On/Off state seems to be per-project rather than a global editor setting.

I would suggest making the Game Embed state editor based than project based to add consinstency with the Editor Settings > Run settings and also adding the option to turn it on or off on the Editor Settings > Run > Window Placement as it seems more organic and intuitive to find those settings there.

@stevenlgreen00
Copy link

Is there a way to tell the editor to embed in GDScript? I'd like my tooling scenes to run in the editor while the main play to run its own window. I keep closing godot when I am intending to close the game (old habits die hard)

@bruvzg
Copy link
Member

bruvzg commented Jan 23, 2025

Is there a way to tell the editor to embed in GDScript?

Engine.is_embedded_in_editor()

@stevenlgreen00
Copy link

This checks if the editor is embedded, but I wanted the script to tell the editor to run it embedded or not for a specific scene.

@Hilderin
Copy link
Contributor Author

If I understand correctly you want to be able via a @tool script to activate or deactivated the embedding for a specific scene?
Right now, there's no way via GDScript to activate or deactivate the embedding and right now the option is global and not per scene.

I can see this could be useful in some situations. I suggest you create a proposal for this.

@KoBeWi
Copy link
Member

KoBeWi commented Jan 23, 2025

It's possible if you find the embedding menu in the scene tree.

@Calinou
Copy link
Member

Calinou commented Jan 23, 2025

I keep closing godot when I am intending to close the game (old habits die hard)

#95392 should help with this 🙂

@donpers
Copy link

donpers commented Jan 30, 2025

Not sure if its intended but you leave fullscreen while being in embedded game mode. In my case its unwanted. Fullscreen been activated to stay inside godot without being interrupted (by external pop ups for example).

@Hilderin
Copy link
Contributor Author

Hilderin commented Jan 31, 2025

Not sure if its intended but you leave fullscreen while being in embedded game mode. In my case its unwanted. Fullscreen been activated to stay inside godot without being interrupted (by external pop ups for example).

Not sure I understand exactly what you mean. You are walking about the Fullscreen mode of the editor right? I tested on my side and the editor stays in fullscreen while starting the game when embedded. Are you on Windows or Linux? Maybe with a bit more information and a video I could understand better.

@donpers
Copy link

donpers commented Jan 31, 2025

Not sure if its intended but you leave fullscreen while being in embedded game mode. In my case its unwanted. Fullscreen been activated to stay inside godot without being interrupted (by external pop ups for example).

Not sure I understand exactly what you mean. You are walking about the Fullscreen mode of the editor right? I tested on my side and the editor stays in fullscreen while starting the game when embedded. Are you on Windows or Linux? Maybe with a bit more information and a video I could understand better.

Yes im talking about the editor fullscreen - Im on linux (xfce - x11). The taskbar of my system only appears when im not in fullscreen and when i run an embedded project, it pops up.

If i think about it, it could also caused by the fact, that the embedded project is a separated software instance, which opens up my taskbar.

I dont have a screen recorder on this hardware to make a vid.

@Hilderin
Copy link
Contributor Author

I see, I'm not sure we could fix that with the way embedding works right now, but we could try. I suggest you create an issue with all this information. Thanks.

@haslingerm
Copy link

Is there already an issue tracking the support of this great feature under (X)Wayland? My cursory search didn't turn up anything, but I might have missed it.

I am on Fedora where the 'main' GNOME and KDE spins have removed X11 already and many of my students are running similar setups (or Arch with Hyprland, because that is 😎 now, I guess), so we cannot use it.

Unfortunately, I'm not familiar with this area and can't offer a PR myself, but I would like to add a +1 to an existing or open a new issue to track the eventual inclusion of Wayland.

@akien-mga
Copy link
Member

@haslingerm It already works fine under XWayland with multiple windows (default configuration). I tested on Fedora 41 KDE on Wayland.

If it doesn't work for you, please open a bug report.

@haslingerm
Copy link

@haslingerm It already works fine under XWayland with multiple windows (default configuration). I tested on Fedora 41 KDE on Wayland.

If it doesn't work for you, please open a bug report.

Oh! I totally misunderstood the 'floating window' option, somehow thinking they are mutually exclusive => PEBKAC, it works fine, sorry!

@vonflyhighace2
Copy link

Are we going to get Native Wayland support or is this relegated to X11/XWayland? Testing the Beta I noticed that Running/Prefering Wayland in the editor gives me a message that my OS is not supported. Scaling is still a big issues which I can only get from Native wayland. Example is that I set my monitor to 125% scaling on an OS level but XWayland backend on Godot sees that as 200% for reasons of how fractional scaling works. Native Wayland in the editor solves this, except the runtime window. A (1280 X 720) window still gets scaled down and look more like half the size for some reason. See attached Image.
Screenshot From 2025-02-04 19-17-35

@Calinou
Copy link
Member

Calinou commented Feb 5, 2025

Are we going to get Native Wayland support or is this relegated to X11/XWayland?

This depends on #101774 to be merged first, but even with that in place, I don't know if it's technically feasible. Wayland is more strict with what apps can do with windowing compared to X11 when they are not compositors.

@Riteo
Copy link
Contributor

Riteo commented Feb 5, 2025

Hi @vonflyhighace2! Yes, I'm planning to implement a Wayland-native approach to game embedding.

I'm currently focusing on #101774 but note that this feature does not actually depend on it, as we don't have the same window handling capabilities as other display servers.

I plan of using a "nested compositor" approach, basically turning Godot into an extremely stripped down Wayland compositor that displays the child process.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.