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

Add support for embedding game process in the Android Editor #102492

Merged
merged 1 commit into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions doc/classes/EditorSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1154,19 +1154,11 @@
- [b]Auto (based on screen size)[/b] (default) will automatically choose how to launch the Play window based on the device and screen metrics. Defaults to [b]Same as Editor[/b] on phones and [b]Side-by-side with Editor[/b] on tablets.
- [b]Same as Editor[/b] will launch the Play window in the same window as the Editor.
- [b]Side-by-side with Editor[/b] will launch the Play window side-by-side with the Editor window.
- [b]Launch in PiP mode[/b] will launch the Play window directly in picture-in-picture (PiP) mode if PiP mode is supported and enabled. When maximized, the Play window will occupy the same window as the Editor.
[b]Note:[/b] Only available in the Android editor.
</member>
<member name="run/window_placement/game_embed_mode" type="int" setter="" getter="">
Overrides game embedding setting for all newly opened projects. If enabled, game embedding settings are not saved.
</member>
<member name="run/window_placement/play_window_pip_mode" type="int" setter="" getter="">
Specifies the picture-in-picture (PiP) mode for the Play window.
- [b]Disabled:[/b] PiP is disabled for the Play window.
- [b]Enabled:[/b] If the device supports it, PiP is always enabled for the Play window. The Play window will contain a button to enter PiP mode.
- [b]Enabled when Play window is same as Editor[/b] (default for Android editor): If the device supports it, PiP is enabled when the Play window is the same as the Editor. The Play window will contain a button to enter PiP mode.
[b]Note:[/b] Only available in the Android editor.
</member>
<member name="run/window_placement/rect" type="int" setter="" getter="">
The window mode to use to display the project when starting the project from the editor.
[b]Note:[/b] Game embedding is not available for "Force Maximized" or "Force Fullscreen."
Expand Down
7 changes: 7 additions & 0 deletions editor/editor_main_screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,11 @@ EditorPlugin *EditorMainScreen::get_selected_plugin() const {
return selected_plugin;
}

EditorPlugin *EditorMainScreen::get_plugin_by_name(const String &p_plugin_name) const {
ERR_FAIL_COND_V(!main_editor_plugins.has(p_plugin_name), nullptr);
return main_editor_plugins[p_plugin_name];
}

VBoxContainer *EditorMainScreen::get_control() const {
return main_screen_vbox;
}
Expand All @@ -254,6 +259,7 @@ void EditorMainScreen::add_main_plugin(EditorPlugin *p_editor) {
buttons.push_back(tb);
button_hb->add_child(tb);
editor_table.push_back(p_editor);
main_editor_plugins.insert(p_editor->get_plugin_name(), p_editor);
}

void EditorMainScreen::remove_main_plugin(EditorPlugin *p_editor) {
Expand All @@ -280,6 +286,7 @@ void EditorMainScreen::remove_main_plugin(EditorPlugin *p_editor) {
}

editor_table.erase(p_editor);
main_editor_plugins.erase(p_editor->get_plugin_name());
}

EditorMainScreen::EditorMainScreen() {
Expand Down
2 changes: 2 additions & 0 deletions editor/editor_main_screen.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class EditorMainScreen : public PanelContainer {
HBoxContainer *button_hb = nullptr;
Vector<Button *> buttons;
Vector<EditorPlugin *> editor_table;
HashMap<String, EditorPlugin *> main_editor_plugins;

int _get_current_main_editor() const;

Expand All @@ -80,6 +81,7 @@ class EditorMainScreen : public PanelContainer {
int get_selected_index() const;
int get_plugin_index(EditorPlugin *p_editor) const;
EditorPlugin *get_selected_plugin() const;
EditorPlugin *get_plugin_by_name(const String &p_plugin_name) const;

VBoxContainer *get_control() const;

Expand Down
17 changes: 10 additions & 7 deletions editor/editor_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -939,17 +939,20 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("run/window_placement/rect_custom_position", Vector2());
EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_ENUM, "run/window_placement/screen", -5, screen_hints)
#endif
// Should match the ANDROID_WINDOW_* constants in 'platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt'
String android_window_hints = "Auto (based on screen size):0,Same as Editor:1,Side-by-side with Editor:2,Launch in PiP mode:3";
// Should match the ANDROID_WINDOW_* constants in 'platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt'.
String android_window_hints = "Auto (based on screen size):0,Same as Editor:1,Side-by-side with Editor:2";
EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_ENUM, "run/window_placement/android_window", 0, android_window_hints)

EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_ENUM, "run/window_placement/game_embed_mode", 0, "Use Per-Project Configuration:0,Embed Game:1,Make Game Workspace Floating:2,Disabled:3");

int default_play_window_pip_mode = 0;
String game_embed_mode_hints = "Disabled:-1,Use Per-Project Configuration:0,Embed Game:1,Make Game Workspace Floating:2";
#ifdef ANDROID_ENABLED
default_play_window_pip_mode = 2;
if (OS::get_singleton()->has_feature("xr_editor")) {
game_embed_mode_hints = "Disabled:-1";
} else {
game_embed_mode_hints = "Disabled:-1,Auto (based on screen size):0,Enabled:1";
}
#endif
EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_ENUM, "run/window_placement/play_window_pip_mode", default_play_window_pip_mode, "Disabled:0,Enabled:1,Enabled when Play window is same as Editor:2")
int default_game_embed_mode = OS::get_singleton()->has_feature("xr_editor") ? -1 : 0;
EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_ENUM, "run/window_placement/game_embed_mode", default_game_embed_mode, game_embed_mode_hints);

// Auto save
_initial_set("run/auto_save/save_before_running", true, true);
Expand Down
54 changes: 36 additions & 18 deletions editor/plugins/game_view_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,10 @@ void GameView::_notification(int p_what) {
// Embedding available.
int game_mode = EDITOR_GET("run/window_placement/game_embed_mode");
switch (game_mode) {
case -1: { // Disabled.
embed_on_play = false;
make_floating_on_play = false;
} break;
case 1: { // Embed.
embed_on_play = true;
make_floating_on_play = false;
Expand All @@ -622,10 +626,6 @@ void GameView::_notification(int p_what) {
embed_on_play = true;
make_floating_on_play = true;
} break;
case 3: { // Disabled.
embed_on_play = false;
make_floating_on_play = false;
} break;
default: {
embed_on_play = EditorSettings::get_singleton()->get_project_metadata("game_view", "embed_on_play", true);
make_floating_on_play = EditorSettings::get_singleton()->get_project_metadata("game_view", "make_floating_on_play", true);
Expand Down Expand Up @@ -1027,6 +1027,18 @@ GameView::GameView(Ref<GameViewDebugger> p_debugger, WindowWrapper *p_wrapper) {

///////

void GameViewPlugin::selected_notify() {
if (_is_window_wrapper_enabled()) {
#ifdef ANDROID_ENABLED
notify_main_screen_changed(get_plugin_name());
#else
window_wrapper->grab_window_focus();
#endif
_focus_another_editor();
}
}

#ifndef ANDROID_ENABLED
void GameViewPlugin::make_visible(bool p_visible) {
if (p_visible) {
window_wrapper->show();
Expand All @@ -1035,13 +1047,6 @@ void GameViewPlugin::make_visible(bool p_visible) {
}
}

void GameViewPlugin::selected_notify() {
if (window_wrapper->get_window_enabled()) {
window_wrapper->grab_window_focus();
_focus_another_editor();
}
}

void GameViewPlugin::set_window_layout(Ref<ConfigFile> p_layout) {
game_view->set_window_layout(p_layout);
}
Expand All @@ -1058,6 +1063,11 @@ Dictionary GameViewPlugin::get_state() const {
return game_view->get_state();
}

void GameViewPlugin::_window_visibility_changed(bool p_visible) {
_focus_another_editor();
}
#endif

void GameViewPlugin::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
Expand All @@ -1082,13 +1092,11 @@ void GameViewPlugin::_feature_profile_changed() {
debugger->set_is_feature_enabled(is_feature_enabled);
}

#ifndef ANDROID_ENABLED
if (game_view) {
game_view->set_is_feature_enabled(is_feature_enabled);
}
}

void GameViewPlugin::_window_visibility_changed(bool p_visible) {
_focus_another_editor();
#endif
}

void GameViewPlugin::_save_last_editor(const String &p_editor) {
Expand All @@ -1098,7 +1106,7 @@ void GameViewPlugin::_save_last_editor(const String &p_editor) {
}

void GameViewPlugin::_focus_another_editor() {
if (window_wrapper->get_window_enabled()) {
if (_is_window_wrapper_enabled()) {
if (last_editor.is_empty()) {
EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_2D);
} else {
Expand All @@ -1107,13 +1115,22 @@ void GameViewPlugin::_focus_another_editor() {
}
}

bool GameViewPlugin::_is_window_wrapper_enabled() const {
#ifdef ANDROID_ENABLED
return true;
#else
return window_wrapper->get_window_enabled();
#endif
}

GameViewPlugin::GameViewPlugin() {
debugger.instantiate();

#ifndef ANDROID_ENABLED
window_wrapper = memnew(WindowWrapper);
window_wrapper->set_window_title(vformat(TTR("%s - Godot Engine"), TTR("Game Workspace")));
window_wrapper->set_margins_enabled(true);

debugger.instantiate();

game_view = memnew(GameView(debugger, window_wrapper));
game_view->set_v_size_flags(Control::SIZE_EXPAND_FILL);

Expand All @@ -1123,6 +1140,7 @@ GameViewPlugin::GameViewPlugin() {
window_wrapper->set_v_size_flags(Control::SIZE_EXPAND_FILL);
window_wrapper->hide();
window_wrapper->connect("window_visibility_changed", callable_mp(this, &GameViewPlugin::_window_visibility_changed));
#endif

EditorFeatureProfileManager::get_singleton()->connect("current_feature_profile_changed", callable_mp(this, &GameViewPlugin::_feature_profile_changed));
}
Expand Down
13 changes: 12 additions & 1 deletion editor/plugins/game_view_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#define GAME_VIEW_PLUGIN_H

#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_main_screen.h"
#include "editor/plugins/editor_debugger_plugin.h"
#include "editor/plugins/editor_plugin.h"
#include "scene/debugger/scene_debugger.h"
Expand Down Expand Up @@ -208,17 +209,22 @@ class GameView : public VBoxContainer {
class GameViewPlugin : public EditorPlugin {
GDCLASS(GameViewPlugin, EditorPlugin);

#ifndef ANDROID_ENABLED
GameView *game_view = nullptr;
WindowWrapper *window_wrapper = nullptr;
#endif

Ref<GameViewDebugger> debugger;

String last_editor;

void _feature_profile_changed();
#ifndef ANDROID_ENABLED
void _window_visibility_changed(bool p_visible);
#endif
void _save_last_editor(const String &p_editor);
void _focus_another_editor();
bool _is_window_wrapper_enabled() const;

protected:
void _notification(int p_what);
Expand All @@ -228,14 +234,19 @@ class GameViewPlugin : public EditorPlugin {
bool has_main_screen() const override { return true; }
virtual void edit(Object *p_object) override {}
virtual bool handles(Object *p_object) const override { return false; }
virtual void make_visible(bool p_visible) override;
virtual void selected_notify() override;

Ref<GameViewDebugger> get_debugger() const { return debugger; }

#ifndef ANDROID_ENABLED
virtual void make_visible(bool p_visible) override;

virtual void set_window_layout(Ref<ConfigFile> p_layout) override;
virtual void get_window_layout(Ref<ConfigFile> p_layout) override;

virtual void set_state(const Dictionary &p_state) override;
virtual Dictionary get_state() const override;
#endif

GameViewPlugin();
~GameViewPlugin();
Expand Down
1 change: 1 addition & 0 deletions platform/android/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ android_files = [
"rendering_context_driver_vulkan_android.cpp",
"variant/callable_jni.cpp",
"dialog_utils_jni.cpp",
"game_menu_utils_jni.cpp",
]

env_android = env.Clone()
Expand Down
Loading