Add "Game" editor for better runtime debugging

This commit is contained in:
Michael Alexsander 2024-09-18 18:07:18 -03:00
parent 8004c7524f
commit 16524a8a01
No known key found for this signature in database
GPG Key ID: A9C91EE110F4EABA
45 changed files with 2378 additions and 274 deletions

View File

@ -116,6 +116,10 @@ void Engine::set_time_scale(double p_scale) {
} }
double Engine::get_time_scale() const { double Engine::get_time_scale() const {
return freeze_time_scale ? 0 : _time_scale;
}
double Engine::get_unfrozen_time_scale() const {
return _time_scale; return _time_scale;
} }
@ -404,6 +408,10 @@ bool Engine::notify_frame_server_synced() {
return server_syncs > SERVER_SYNC_FRAME_COUNT_WARNING; return server_syncs > SERVER_SYNC_FRAME_COUNT_WARNING;
} }
void Engine::set_freeze_time_scale(bool p_frozen) {
freeze_time_scale = p_frozen;
}
Engine::Engine() { Engine::Engine() {
singleton = this; singleton = this;
} }

View File

@ -99,6 +99,8 @@ private:
int server_syncs = 0; int server_syncs = 0;
bool frame_server_synced = false; bool frame_server_synced = false;
bool freeze_time_scale = false;
public: public:
static Engine *get_singleton(); static Engine *get_singleton();
@ -130,6 +132,7 @@ public:
void set_time_scale(double p_scale); void set_time_scale(double p_scale);
double get_time_scale() const; double get_time_scale() const;
double get_unfrozen_time_scale() const;
void set_print_to_stdout(bool p_enabled); void set_print_to_stdout(bool p_enabled);
bool is_printing_to_stdout() const; bool is_printing_to_stdout() const;
@ -197,6 +200,8 @@ public:
void increment_frames_drawn(); void increment_frames_drawn();
bool notify_frame_server_synced(); bool notify_frame_server_synced();
void set_freeze_time_scale(bool p_frozen);
Engine(); Engine();
virtual ~Engine(); virtual ~Engine();
}; };

View File

@ -87,11 +87,50 @@ Input *Input::get_singleton() {
void Input::set_mouse_mode(MouseMode p_mode) { void Input::set_mouse_mode(MouseMode p_mode) {
ERR_FAIL_INDEX((int)p_mode, 5); ERR_FAIL_INDEX((int)p_mode, 5);
if (p_mode == mouse_mode) {
return;
}
// Allow to be set even if overridden, to see if the platform allows the mode.
set_mouse_mode_func(p_mode); set_mouse_mode_func(p_mode);
mouse_mode = get_mouse_mode_func();
if (mouse_mode_override_enabled) {
set_mouse_mode_func(mouse_mode_override);
}
} }
Input::MouseMode Input::get_mouse_mode() const { Input::MouseMode Input::get_mouse_mode() const {
return get_mouse_mode_func(); return mouse_mode;
}
void Input::set_mouse_mode_override_enabled(bool p_enabled) {
if (p_enabled == mouse_mode_override_enabled) {
return;
}
mouse_mode_override_enabled = p_enabled;
if (p_enabled) {
set_mouse_mode_func(mouse_mode_override);
mouse_mode_override = get_mouse_mode_func();
} else {
set_mouse_mode_func(mouse_mode);
}
}
void Input::set_mouse_mode_override(MouseMode p_mode) {
ERR_FAIL_INDEX((int)p_mode, 5);
if (p_mode == mouse_mode_override) {
return;
}
if (mouse_mode_override_enabled) {
set_mouse_mode_func(p_mode);
mouse_mode_override = get_mouse_mode_func();
}
} }
void Input::_bind_methods() { void Input::_bind_methods() {
@ -252,6 +291,10 @@ Input::VelocityTrack::VelocityTrack() {
bool Input::is_anything_pressed() const { bool Input::is_anything_pressed() const {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
if (disable_input) {
return false;
}
if (!keys_pressed.is_empty() || !joy_buttons_pressed.is_empty() || !mouse_button_mask.is_empty()) { if (!keys_pressed.is_empty() || !joy_buttons_pressed.is_empty() || !mouse_button_mask.is_empty()) {
return true; return true;
} }
@ -267,21 +310,41 @@ bool Input::is_anything_pressed() const {
bool Input::is_key_pressed(Key p_keycode) const { bool Input::is_key_pressed(Key p_keycode) const {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
if (disable_input) {
return false;
}
return keys_pressed.has(p_keycode); return keys_pressed.has(p_keycode);
} }
bool Input::is_physical_key_pressed(Key p_keycode) const { bool Input::is_physical_key_pressed(Key p_keycode) const {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
if (disable_input) {
return false;
}
return physical_keys_pressed.has(p_keycode); return physical_keys_pressed.has(p_keycode);
} }
bool Input::is_key_label_pressed(Key p_keycode) const { bool Input::is_key_label_pressed(Key p_keycode) const {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
if (disable_input) {
return false;
}
return key_label_pressed.has(p_keycode); return key_label_pressed.has(p_keycode);
} }
bool Input::is_mouse_button_pressed(MouseButton p_button) const { bool Input::is_mouse_button_pressed(MouseButton p_button) const {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
if (disable_input) {
return false;
}
return mouse_button_mask.has_flag(mouse_button_to_mask(p_button)); return mouse_button_mask.has_flag(mouse_button_to_mask(p_button));
} }
@ -295,11 +358,21 @@ static JoyButton _combine_device(JoyButton p_value, int p_device) {
bool Input::is_joy_button_pressed(int p_device, JoyButton p_button) const { bool Input::is_joy_button_pressed(int p_device, JoyButton p_button) const {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
if (disable_input) {
return false;
}
return joy_buttons_pressed.has(_combine_device(p_button, p_device)); return joy_buttons_pressed.has(_combine_device(p_button, p_device));
} }
bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const { bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action)); ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
if (disable_input) {
return false;
}
HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
if (!E) { if (!E) {
return false; return false;
@ -310,6 +383,11 @@ bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const {
bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const { bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action)); ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
if (disable_input) {
return false;
}
HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
if (!E) { if (!E) {
return false; return false;
@ -331,6 +409,11 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con
bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const { bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action)); ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
if (disable_input) {
return false;
}
HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
if (!E) { if (!E) {
return false; return false;
@ -352,6 +435,11 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co
float Input::get_action_strength(const StringName &p_action, bool p_exact) const { float Input::get_action_strength(const StringName &p_action, bool p_exact) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action)); ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action));
if (disable_input) {
return 0.0f;
}
HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
if (!E) { if (!E) {
return 0.0f; return 0.0f;
@ -366,6 +454,11 @@ float Input::get_action_strength(const StringName &p_action, bool p_exact) const
float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) const { float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action)); ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action));
if (disable_input) {
return 0.0f;
}
HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
if (!E) { if (!E) {
return 0.0f; return 0.0f;
@ -410,6 +503,11 @@ Vector2 Input::get_vector(const StringName &p_negative_x, const StringName &p_po
float Input::get_joy_axis(int p_device, JoyAxis p_axis) const { float Input::get_joy_axis(int p_device, JoyAxis p_axis) const {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
if (disable_input) {
return 0;
}
JoyAxis c = _combine_device(p_axis, p_device); JoyAxis c = _combine_device(p_axis, p_device);
if (_joy_axis.has(c)) { if (_joy_axis.has(c)) {
return _joy_axis[c]; return _joy_axis[c];
@ -1664,6 +1762,14 @@ int Input::get_unused_joy_id() {
return -1; return -1;
} }
void Input::set_disable_input(bool p_disable) {
disable_input = p_disable;
}
bool Input::is_input_disabled() const {
return disable_input;
}
Input::Input() { Input::Input() {
singleton = this; singleton = this;

View File

@ -103,6 +103,11 @@ private:
Vector2 mouse_pos; Vector2 mouse_pos;
int64_t mouse_window = 0; int64_t mouse_window = 0;
bool legacy_just_pressed_behavior = false; bool legacy_just_pressed_behavior = false;
bool disable_input = false;
MouseMode mouse_mode = MOUSE_MODE_VISIBLE;
bool mouse_mode_override_enabled = false;
MouseMode mouse_mode_override = MOUSE_MODE_VISIBLE;
struct ActionState { struct ActionState {
uint64_t pressed_physics_frame = UINT64_MAX; uint64_t pressed_physics_frame = UINT64_MAX;
@ -279,6 +284,8 @@ protected:
public: public:
void set_mouse_mode(MouseMode p_mode); void set_mouse_mode(MouseMode p_mode);
MouseMode get_mouse_mode() const; MouseMode get_mouse_mode() const;
void set_mouse_mode_override_enabled(bool p_enabled);
void set_mouse_mode_override(MouseMode p_mode);
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
@ -380,6 +387,9 @@ public:
void set_event_dispatch_function(EventDispatchFunc p_function); void set_event_dispatch_function(EventDispatchFunc p_function);
void set_disable_input(bool p_disable);
bool is_input_disabled() const;
Input(); Input();
~Input(); ~Input();
}; };

View File

@ -121,7 +121,10 @@
<constant name="FEATURE_HISTORY_DOCK" value="7" enum="Feature"> <constant name="FEATURE_HISTORY_DOCK" value="7" enum="Feature">
The History dock. If this feature is disabled, the History dock won't be visible. The History dock. If this feature is disabled, the History dock won't be visible.
</constant> </constant>
<constant name="FEATURE_MAX" value="8" enum="Feature"> <constant name="FEATURE_GAME" value="8" enum="Feature">
The Game tab, which allows embedding the game window and selecting nodes by clicking inside of it. If this feature is disabled, the Game tab won't display.
</constant>
<constant name="FEATURE_MAX" value="9" enum="Feature">
Represents the size of the [enum Feature] enum. Represents the size of the [enum Feature] enum.
</constant> </constant>
</constants> </constants>

View File

@ -105,6 +105,7 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() {
node->connect("breakpoint_selected", callable_mp(this, &EditorDebuggerNode::_error_selected).bind(id)); node->connect("breakpoint_selected", callable_mp(this, &EditorDebuggerNode::_error_selected).bind(id));
node->connect("clear_execution", callable_mp(this, &EditorDebuggerNode::_clear_execution)); node->connect("clear_execution", callable_mp(this, &EditorDebuggerNode::_clear_execution));
node->connect("breaked", callable_mp(this, &EditorDebuggerNode::_breaked).bind(id)); node->connect("breaked", callable_mp(this, &EditorDebuggerNode::_breaked).bind(id));
node->connect("remote_tree_select_requested", callable_mp(this, &EditorDebuggerNode::_remote_tree_select_requested).bind(id));
node->connect("remote_tree_updated", callable_mp(this, &EditorDebuggerNode::_remote_tree_updated).bind(id)); node->connect("remote_tree_updated", callable_mp(this, &EditorDebuggerNode::_remote_tree_updated).bind(id));
node->connect("remote_object_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_updated).bind(id)); node->connect("remote_object_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_updated).bind(id));
node->connect("remote_object_property_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_property_updated).bind(id)); node->connect("remote_object_property_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_property_updated).bind(id));
@ -637,6 +638,13 @@ void EditorDebuggerNode::request_remote_tree() {
get_current_debugger()->request_remote_tree(); get_current_debugger()->request_remote_tree();
} }
void EditorDebuggerNode::_remote_tree_select_requested(ObjectID p_id, int p_debugger) {
if (p_debugger != tabs->get_current_tab()) {
return;
}
remote_scene_tree->select_node(p_id);
}
void EditorDebuggerNode::_remote_tree_updated(int p_debugger) { void EditorDebuggerNode::_remote_tree_updated(int p_debugger) {
if (p_debugger != tabs->get_current_tab()) { if (p_debugger != tabs->get_current_tab()) {
return; return;

View File

@ -51,11 +51,8 @@ class EditorDebuggerNode : public MarginContainer {
public: public:
enum CameraOverride { enum CameraOverride {
OVERRIDE_NONE, OVERRIDE_NONE,
OVERRIDE_2D, OVERRIDE_INGAME,
OVERRIDE_3D_1, // 3D Viewport 1 OVERRIDE_EDITORS,
OVERRIDE_3D_2, // 3D Viewport 2
OVERRIDE_3D_3, // 3D Viewport 3
OVERRIDE_3D_4 // 3D Viewport 4
}; };
private: private:
@ -132,6 +129,7 @@ protected:
void _debugger_stopped(int p_id); void _debugger_stopped(int p_id);
void _debugger_wants_stop(int p_id); void _debugger_wants_stop(int p_id);
void _debugger_changed(int p_tab); void _debugger_changed(int p_tab);
void _remote_tree_select_requested(ObjectID p_id, int p_debugger);
void _remote_tree_updated(int p_debugger); void _remote_tree_updated(int p_debugger);
void _remote_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button); void _remote_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
void _remote_object_updated(ObjectID p_id, int p_debugger); void _remote_object_updated(ObjectID p_id, int p_debugger);

View File

@ -30,6 +30,7 @@
#include "editor_debugger_tree.h" #include "editor_debugger_tree.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_node.h" #include "editor/editor_node.h"
#include "editor/editor_string_names.h" #include "editor/editor_string_names.h"
#include "editor/gui/editor_file_dialog.h" #include "editor/gui/editor_file_dialog.h"
@ -148,7 +149,8 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
updating_scene_tree = true; updating_scene_tree = true;
const String last_path = get_selected_path(); const String last_path = get_selected_path();
const String filter = SceneTreeDock::get_singleton()->get_filter(); const String filter = SceneTreeDock::get_singleton()->get_filter();
bool filter_changed = filter != last_filter; bool should_scroll = scrolling_to_item || filter != last_filter;
scrolling_to_item = false;
TreeItem *scroll_item = nullptr; TreeItem *scroll_item = nullptr;
// Nodes are in a flatten list, depth first. Use a stack of parents, avoid recursion. // Nodes are in a flatten list, depth first. Use a stack of parents, avoid recursion.
@ -185,8 +187,18 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
// Select previously selected node. // Select previously selected node.
if (debugger_id == p_debugger) { // Can use remote id. if (debugger_id == p_debugger) { // Can use remote id.
if (node.id == inspected_object_id) { if (node.id == inspected_object_id) {
if (selection_uncollapse_all) {
selection_uncollapse_all = false;
// Temporarily set to `false`, to allow caching the unfolds.
updating_scene_tree = false;
item->uncollapse_tree();
updating_scene_tree = true;
}
item->select(0); item->select(0);
if (filter_changed) {
if (should_scroll) {
scroll_item = item; scroll_item = item;
} }
} }
@ -194,7 +206,7 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
if (last_path == _get_path(item)) { if (last_path == _get_path(item)) {
updating_scene_tree = false; // Force emission of new selection. updating_scene_tree = false; // Force emission of new selection.
item->select(0); item->select(0);
if (filter_changed) { if (should_scroll) {
scroll_item = item; scroll_item = item;
} }
updating_scene_tree = true; updating_scene_tree = true;
@ -258,14 +270,30 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
} }
} }
} }
debugger_id = p_debugger; // Needed by hook, could be avoided if every debugger had its own tree
debugger_id = p_debugger; // Needed by hook, could be avoided if every debugger had its own tree.
if (scroll_item) { if (scroll_item) {
callable_mp((Tree *)this, &Tree::scroll_to_item).call_deferred(scroll_item, false); scroll_to_item(scroll_item, false);
} }
last_filter = filter; last_filter = filter;
updating_scene_tree = false; updating_scene_tree = false;
} }
void EditorDebuggerTree::select_node(ObjectID p_id) {
// Manually select, as the tree control may be out-of-date for some reason (e.g. not shown yet).
selection_uncollapse_all = true;
inspected_object_id = uint64_t(p_id);
scrolling_to_item = true;
emit_signal(SNAME("object_selected"), inspected_object_id, debugger_id);
if (!updating_scene_tree) {
// Request a tree refresh.
EditorDebuggerNode::get_singleton()->request_remote_tree();
}
// Set the value immediately, so no update flooding happens and causes a crash.
updating_scene_tree = true;
}
Variant EditorDebuggerTree::get_drag_data(const Point2 &p_point) { Variant EditorDebuggerTree::get_drag_data(const Point2 &p_point) {
if (get_button_id_at_position(p_point) != -1) { if (get_button_id_at_position(p_point) != -1) {
return Variant(); return Variant();

View File

@ -49,6 +49,8 @@ private:
ObjectID inspected_object_id; ObjectID inspected_object_id;
int debugger_id = 0; int debugger_id = 0;
bool updating_scene_tree = false; bool updating_scene_tree = false;
bool scrolling_to_item = false;
bool selection_uncollapse_all = false;
HashSet<ObjectID> unfold_cache; HashSet<ObjectID> unfold_cache;
PopupMenu *item_menu = nullptr; PopupMenu *item_menu = nullptr;
EditorFileDialog *file_dialog = nullptr; EditorFileDialog *file_dialog = nullptr;
@ -78,6 +80,7 @@ public:
ObjectID get_selected_object(); ObjectID get_selected_object();
int get_current_debugger(); // Would love to have one tree for every debugger. int get_current_debugger(); // Would love to have one tree for every debugger.
void update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger); void update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger);
void select_node(ObjectID p_id);
EditorDebuggerTree(); EditorDebuggerTree();
}; };

View File

@ -806,6 +806,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
} else if (p_msg == "request_quit") { } else if (p_msg == "request_quit") {
emit_signal(SNAME("stop_requested")); emit_signal(SNAME("stop_requested"));
_stop_and_notify(); _stop_and_notify();
} else if (p_msg == "remote_node_clicked") {
if (!p_data.is_empty()) {
emit_signal(SNAME("remote_tree_select_requested"), p_data[0]);
}
} else if (p_msg == "performance:profile_names") { } else if (p_msg == "performance:profile_names") {
Vector<StringName> monitors; Vector<StringName> monitors;
monitors.resize(p_data.size()); monitors.resize(p_data.size());
@ -905,37 +909,42 @@ void ScriptEditorDebugger::_notification(int p_what) {
if (is_session_active()) { if (is_session_active()) {
peer->poll(); peer->poll();
if (camera_override == CameraOverride::OVERRIDE_2D) { if (camera_override == CameraOverride::OVERRIDE_EDITORS) {
Dictionary state = CanvasItemEditor::get_singleton()->get_state(); // CanvasItem Editor
float zoom = state["zoom"]; {
Point2 offset = state["ofs"]; Dictionary state = CanvasItemEditor::get_singleton()->get_state();
Transform2D transform; float zoom = state["zoom"];
Point2 offset = state["ofs"];
Transform2D transform;
transform.scale_basis(Size2(zoom, zoom)); transform.scale_basis(Size2(zoom, zoom));
transform.columns[2] = -offset * zoom; transform.columns[2] = -offset * zoom;
Array msg; Array msg;
msg.push_back(transform); msg.push_back(transform);
_put_msg("scene:override_camera_2D:transform", msg); _put_msg("scene:transform_camera_2d", msg);
}
} else if (camera_override >= CameraOverride::OVERRIDE_3D_1) {
int viewport_idx = camera_override - CameraOverride::OVERRIDE_3D_1; // Node3D Editor
Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_editor_viewport(viewport_idx); {
Camera3D *const cam = viewport->get_camera_3d(); Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_last_used_viewport();
const Camera3D *cam = viewport->get_camera_3d();
Array msg;
msg.push_back(cam->get_camera_transform()); Array msg;
if (cam->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) { msg.push_back(cam->get_camera_transform());
msg.push_back(false); if (cam->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) {
msg.push_back(cam->get_size()); msg.push_back(false);
} else { msg.push_back(cam->get_size());
msg.push_back(true); } else {
msg.push_back(cam->get_fov()); msg.push_back(true);
msg.push_back(cam->get_fov());
}
msg.push_back(cam->get_near());
msg.push_back(cam->get_far());
_put_msg("scene:transform_camera_3d", msg);
} }
msg.push_back(cam->get_near());
msg.push_back(cam->get_far());
_put_msg("scene:override_camera_3D:transform", msg);
} }
if (is_breaked() && can_request_idle_draw) { if (is_breaked() && can_request_idle_draw) {
_put_msg("servers:draw", Array()); _put_msg("servers:draw", Array());
can_request_idle_draw = false; can_request_idle_draw = false;
@ -1469,23 +1478,10 @@ CameraOverride ScriptEditorDebugger::get_camera_override() const {
} }
void ScriptEditorDebugger::set_camera_override(CameraOverride p_override) { void ScriptEditorDebugger::set_camera_override(CameraOverride p_override) {
if (p_override == CameraOverride::OVERRIDE_2D && camera_override != CameraOverride::OVERRIDE_2D) { Array msg;
Array msg; msg.push_back(p_override != CameraOverride::OVERRIDE_NONE);
msg.push_back(true); msg.push_back(p_override == CameraOverride::OVERRIDE_EDITORS);
_put_msg("scene:override_camera_2D:set", msg); _put_msg("scene:override_cameras", msg);
} else if (p_override != CameraOverride::OVERRIDE_2D && camera_override == CameraOverride::OVERRIDE_2D) {
Array msg;
msg.push_back(false);
_put_msg("scene:override_camera_2D:set", msg);
} else if (p_override >= CameraOverride::OVERRIDE_3D_1 && camera_override < CameraOverride::OVERRIDE_3D_1) {
Array msg;
msg.push_back(true);
_put_msg("scene:override_camera_3D:set", msg);
} else if (p_override < CameraOverride::OVERRIDE_3D_1 && camera_override >= CameraOverride::OVERRIDE_3D_1) {
Array msg;
msg.push_back(false);
_put_msg("scene:override_camera_3D:set", msg);
}
camera_override = p_override; camera_override = p_override;
} }
@ -1776,6 +1772,7 @@ void ScriptEditorDebugger::_bind_methods() {
ADD_SIGNAL(MethodInfo("remote_object_updated", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("remote_object_updated", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("remote_object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property"))); ADD_SIGNAL(MethodInfo("remote_object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property")));
ADD_SIGNAL(MethodInfo("remote_tree_updated")); ADD_SIGNAL(MethodInfo("remote_tree_updated"));
ADD_SIGNAL(MethodInfo("remote_tree_select_requested", PropertyInfo(Variant::NODE_PATH, "path")));
ADD_SIGNAL(MethodInfo("output", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::INT, "level"))); ADD_SIGNAL(MethodInfo("output", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::INT, "level")));
ADD_SIGNAL(MethodInfo("stack_dump", PropertyInfo(Variant::ARRAY, "stack_dump"))); ADD_SIGNAL(MethodInfo("stack_dump", PropertyInfo(Variant::ARRAY, "stack_dump")));
ADD_SIGNAL(MethodInfo("stack_frame_vars", PropertyInfo(Variant::INT, "num_vars"))); ADD_SIGNAL(MethodInfo("stack_frame_vars", PropertyInfo(Variant::INT, "num_vars")));

View File

@ -43,6 +43,7 @@
const char *EditorFeatureProfile::feature_names[FEATURE_MAX] = { const char *EditorFeatureProfile::feature_names[FEATURE_MAX] = {
TTRC("3D Editor"), TTRC("3D Editor"),
TTRC("Script Editor"), TTRC("Script Editor"),
TTRC("Game View"),
TTRC("Asset Library"), TTRC("Asset Library"),
TTRC("Scene Tree Editing"), TTRC("Scene Tree Editing"),
TTRC("Node Dock"), TTRC("Node Dock"),
@ -54,6 +55,7 @@ const char *EditorFeatureProfile::feature_names[FEATURE_MAX] = {
const char *EditorFeatureProfile::feature_descriptions[FEATURE_MAX] = { const char *EditorFeatureProfile::feature_descriptions[FEATURE_MAX] = {
TTRC("Allows to view and edit 3D scenes."), TTRC("Allows to view and edit 3D scenes."),
TTRC("Allows to edit scripts using the integrated script editor."), TTRC("Allows to edit scripts using the integrated script editor."),
TTRC("Provides tools for selecting and debugging nodes at runtime."),
TTRC("Provides built-in access to the Asset Library."), TTRC("Provides built-in access to the Asset Library."),
TTRC("Allows editing the node hierarchy in the Scene dock."), TTRC("Allows editing the node hierarchy in the Scene dock."),
TTRC("Allows to work with signals and groups of the node selected in the Scene dock."), TTRC("Allows to work with signals and groups of the node selected in the Scene dock."),
@ -65,6 +67,7 @@ const char *EditorFeatureProfile::feature_descriptions[FEATURE_MAX] = {
const char *EditorFeatureProfile::feature_identifiers[FEATURE_MAX] = { const char *EditorFeatureProfile::feature_identifiers[FEATURE_MAX] = {
"3d", "3d",
"script", "script",
"game",
"asset_lib", "asset_lib",
"scene_tree", "scene_tree",
"node_dock", "node_dock",
@ -307,6 +310,7 @@ void EditorFeatureProfile::_bind_methods() {
BIND_ENUM_CONSTANT(FEATURE_FILESYSTEM_DOCK); BIND_ENUM_CONSTANT(FEATURE_FILESYSTEM_DOCK);
BIND_ENUM_CONSTANT(FEATURE_IMPORT_DOCK); BIND_ENUM_CONSTANT(FEATURE_IMPORT_DOCK);
BIND_ENUM_CONSTANT(FEATURE_HISTORY_DOCK); BIND_ENUM_CONSTANT(FEATURE_HISTORY_DOCK);
BIND_ENUM_CONSTANT(FEATURE_GAME);
BIND_ENUM_CONSTANT(FEATURE_MAX); BIND_ENUM_CONSTANT(FEATURE_MAX);
} }

View File

@ -55,6 +55,7 @@ public:
FEATURE_FILESYSTEM_DOCK, FEATURE_FILESYSTEM_DOCK,
FEATURE_IMPORT_DOCK, FEATURE_IMPORT_DOCK,
FEATURE_HISTORY_DOCK, FEATURE_HISTORY_DOCK,
FEATURE_GAME,
FEATURE_MAX FEATURE_MAX
}; };

View File

@ -47,6 +47,7 @@ public:
EDITOR_2D = 0, EDITOR_2D = 0,
EDITOR_3D, EDITOR_3D,
EDITOR_SCRIPT, EDITOR_SCRIPT,
EDITOR_GAME,
EDITOR_ASSETLIB, EDITOR_ASSETLIB,
}; };

View File

@ -145,6 +145,7 @@
#include "editor/plugins/editor_plugin.h" #include "editor/plugins/editor_plugin.h"
#include "editor/plugins/editor_preview_plugins.h" #include "editor/plugins/editor_preview_plugins.h"
#include "editor/plugins/editor_resource_conversion_plugin.h" #include "editor/plugins/editor_resource_conversion_plugin.h"
#include "editor/plugins/game_view_plugin.h"
#include "editor/plugins/gdextension_export_plugin.h" #include "editor/plugins/gdextension_export_plugin.h"
#include "editor/plugins/material_editor_plugin.h" #include "editor/plugins/material_editor_plugin.h"
#include "editor/plugins/mesh_library_editor_plugin.h" #include "editor/plugins/mesh_library_editor_plugin.h"
@ -357,6 +358,8 @@ void EditorNode::shortcut_input(const Ref<InputEvent> &p_event) {
editor_main_screen->select(EditorMainScreen::EDITOR_3D); editor_main_screen->select(EditorMainScreen::EDITOR_3D);
} else if (ED_IS_SHORTCUT("editor/editor_script", p_event)) { } else if (ED_IS_SHORTCUT("editor/editor_script", p_event)) {
editor_main_screen->select(EditorMainScreen::EDITOR_SCRIPT); editor_main_screen->select(EditorMainScreen::EDITOR_SCRIPT);
} else if (ED_IS_SHORTCUT("editor/editor_game", p_event)) {
editor_main_screen->select(EditorMainScreen::EDITOR_GAME);
} else if (ED_IS_SHORTCUT("editor/editor_help", p_event)) { } else if (ED_IS_SHORTCUT("editor/editor_help", p_event)) {
emit_signal(SNAME("request_help_search"), ""); emit_signal(SNAME("request_help_search"), "");
} else if (ED_IS_SHORTCUT("editor/editor_assetlib", p_event) && AssetLibraryEditorPlugin::is_available()) { } else if (ED_IS_SHORTCUT("editor/editor_assetlib", p_event) && AssetLibraryEditorPlugin::is_available()) {
@ -6577,6 +6580,7 @@ void EditorNode::_feature_profile_changed() {
editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_3D, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)); editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_3D, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D));
editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_SCRIPT, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT)); editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_SCRIPT, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT));
editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_GAME, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_GAME));
if (AssetLibraryEditorPlugin::is_available()) { if (AssetLibraryEditorPlugin::is_available()) {
editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_ASSETLIB, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB)); editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_ASSETLIB, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB));
} }
@ -6587,6 +6591,7 @@ void EditorNode::_feature_profile_changed() {
editor_dock_manager->set_dock_enabled(history_dock, true); editor_dock_manager->set_dock_enabled(history_dock, true);
editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_3D, true); editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_3D, true);
editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_SCRIPT, true); editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_SCRIPT, true);
editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_GAME, true);
if (AssetLibraryEditorPlugin::is_available()) { if (AssetLibraryEditorPlugin::is_available()) {
editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_ASSETLIB, true); editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_ASSETLIB, true);
} }
@ -7714,6 +7719,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(CanvasItemEditorPlugin)); add_editor_plugin(memnew(CanvasItemEditorPlugin));
add_editor_plugin(memnew(Node3DEditorPlugin)); add_editor_plugin(memnew(Node3DEditorPlugin));
add_editor_plugin(memnew(ScriptEditorPlugin)); add_editor_plugin(memnew(ScriptEditorPlugin));
add_editor_plugin(memnew(GameViewPlugin));
EditorAudioBuses *audio_bus_editor = EditorAudioBuses::register_editor(); EditorAudioBuses *audio_bus_editor = EditorAudioBuses::register_editor();
@ -7896,12 +7902,14 @@ EditorNode::EditorNode() {
ED_SHORTCUT_AND_COMMAND("editor/editor_2d", TTR("Open 2D Editor"), KeyModifierMask::CTRL | Key::F1); ED_SHORTCUT_AND_COMMAND("editor/editor_2d", TTR("Open 2D Editor"), KeyModifierMask::CTRL | Key::F1);
ED_SHORTCUT_AND_COMMAND("editor/editor_3d", TTR("Open 3D Editor"), KeyModifierMask::CTRL | Key::F2); ED_SHORTCUT_AND_COMMAND("editor/editor_3d", TTR("Open 3D Editor"), KeyModifierMask::CTRL | Key::F2);
ED_SHORTCUT_AND_COMMAND("editor/editor_script", TTR("Open Script Editor"), KeyModifierMask::CTRL | Key::F3); ED_SHORTCUT_AND_COMMAND("editor/editor_script", TTR("Open Script Editor"), KeyModifierMask::CTRL | Key::F3);
ED_SHORTCUT_AND_COMMAND("editor/editor_assetlib", TTR("Open Asset Library"), KeyModifierMask::CTRL | Key::F4); ED_SHORTCUT_AND_COMMAND("editor/editor_game", TTR("Open Game View"), KeyModifierMask::CTRL | Key::F4);
ED_SHORTCUT_AND_COMMAND("editor/editor_assetlib", TTR("Open Asset Library"), KeyModifierMask::CTRL | Key::F5);
ED_SHORTCUT_OVERRIDE("editor/editor_2d", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_1); ED_SHORTCUT_OVERRIDE("editor/editor_2d", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_1);
ED_SHORTCUT_OVERRIDE("editor/editor_3d", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_2); ED_SHORTCUT_OVERRIDE("editor/editor_3d", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_2);
ED_SHORTCUT_OVERRIDE("editor/editor_script", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_3); ED_SHORTCUT_OVERRIDE("editor/editor_script", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_3);
ED_SHORTCUT_OVERRIDE("editor/editor_assetlib", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_4); ED_SHORTCUT_OVERRIDE("editor/editor_game", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_4);
ED_SHORTCUT_OVERRIDE("editor/editor_assetlib", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_5);
ED_SHORTCUT_AND_COMMAND("editor/editor_next", TTR("Open the next Editor")); ED_SHORTCUT_AND_COMMAND("editor/editor_next", TTR("Open the next Editor"));
ED_SHORTCUT_AND_COMMAND("editor/editor_prev", TTR("Open the previous Editor")); ED_SHORTCUT_AND_COMMAND("editor/editor_prev", TTR("Open the previous Editor"));

1
editor/icons/2DNodes.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16"><path fill="none" stroke="#8da5f3" stroke-width="2" d="M 8,13 C 5.2385763,13 3,10.761424 3,8 3,5.2385763 5.2385763,3 8,3"/><path fill="none" stroke="#8eef97" stroke-width="2" d="m 8,13 c 2.761424,0 5,-2.238576 5,-5 C 13,5.2385763 10.761424,3 8,3"/></svg>

After

Width:  |  Height:  |  Size: 338 B

1
editor/icons/Camera.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16"><path fill="#e0e0e0" d="M9 2a3 3 0 0 0-3 2.777 3 3 0 1 0-3 5.047V12a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-1l3 2V7l-3 2V7.23A3 3 0 0 0 9 2z"/></svg>

After

Width:  |  Height:  |  Size: 224 B

1
editor/icons/Game.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M 1,15 V 12 C 1,11.5 1.5,11 2,11 H 3 V 10 C 3,9.5 3.5,9 4,9 h 1 c 0.5,0 1,0.5 1,1 v 1 H 8 V 5 h 2 v 6 h 4 c 0.5,0 1,0.5 1,1 v 3 z"/><circle cx="9" cy="4" r="3" fill="#e0e0e0"/></svg>

After

Width:  |  Height:  |  Size: 269 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m 12,3 c -0.552285,0 -1,0.4477153 -1,1 v 8 c 0,0.552285 0.447715,1 1,1 h 1 c 0.552285,0 1,-0.447715 1,-1 V 4 C 14,3.4477153 13.552285,3 13,3 Z M 2.975,3.002 C 2.4332786,3.0155465 2.0009144,3.45811 2,4 v 8 c -3.148e-4,0.838862 0.9701632,1.305289 1.625,0.781 l 5,-4 c 0.4989606,-0.4003069 0.4989606,-1.1596931 0,-1.56 l -5,-4 C 3.4409271,3.0736532 3.2107095,2.9960875 2.975,3.002 Z"/></svg>

After

Width:  |  Height:  |  Size: 475 B

View File

@ -3977,7 +3977,6 @@ void CanvasItemEditor::_update_editor_settings() {
grid_snap_button->set_button_icon(get_editor_theme_icon(SNAME("SnapGrid"))); grid_snap_button->set_button_icon(get_editor_theme_icon(SNAME("SnapGrid")));
snap_config_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); snap_config_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
skeleton_menu->set_button_icon(get_editor_theme_icon(SNAME("Bone"))); skeleton_menu->set_button_icon(get_editor_theme_icon(SNAME("Bone")));
override_camera_button->set_button_icon(get_editor_theme_icon(SNAME("Camera2D")));
pan_button->set_button_icon(get_editor_theme_icon(SNAME("ToolPan"))); pan_button->set_button_icon(get_editor_theme_icon(SNAME("ToolPan")));
ruler_button->set_button_icon(get_editor_theme_icon(SNAME("Ruler"))); ruler_button->set_button_icon(get_editor_theme_icon(SNAME("Ruler")));
pivot_button->set_button_icon(get_editor_theme_icon(SNAME("EditPivot"))); pivot_button->set_button_icon(get_editor_theme_icon(SNAME("EditPivot")));
@ -4016,8 +4015,6 @@ void CanvasItemEditor::_notification(int p_what) {
case NOTIFICATION_READY: { case NOTIFICATION_READY: {
_update_lock_and_group_button(); _update_lock_and_group_button();
EditorRunBar::get_singleton()->connect("play_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true));
EditorRunBar::get_singleton()->connect("stop_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(false));
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &CanvasItemEditor::_project_settings_changed)); ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &CanvasItemEditor::_project_settings_changed));
} break; } break;
@ -4116,15 +4113,6 @@ void CanvasItemEditor::_notification(int p_what) {
_update_editor_settings(); _update_editor_settings();
} break; } break;
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible() && override_camera_button->is_pressed()) {
EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_NONE);
override_camera_button->set_pressed(false);
}
} break;
case NOTIFICATION_APPLICATION_FOCUS_OUT: case NOTIFICATION_APPLICATION_FOCUS_OUT:
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: { case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
if (drag_type != DRAG_NONE) { if (drag_type != DRAG_NONE) {
@ -4282,16 +4270,6 @@ void CanvasItemEditor::_button_toggle_grid_snap(bool p_status) {
viewport->queue_redraw(); viewport->queue_redraw();
} }
void CanvasItemEditor::_button_override_camera(bool p_pressed) {
EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
if (p_pressed) {
debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_2D);
} else {
debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_NONE);
}
}
void CanvasItemEditor::_button_tool_select(int p_index) { void CanvasItemEditor::_button_tool_select(int p_index) {
Button *tb[TOOL_MAX] = { select_button, list_select_button, move_button, scale_button, rotate_button, pivot_button, pan_button, ruler_button }; Button *tb[TOOL_MAX] = { select_button, list_select_button, move_button, scale_button, rotate_button, pivot_button, pan_button, ruler_button };
for (int i = 0; i < TOOL_MAX; i++) { for (int i = 0; i < TOOL_MAX; i++) {
@ -4398,17 +4376,6 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
te->commit_insert_queue(); te->commit_insert_queue();
} }
void CanvasItemEditor::_update_override_camera_button(bool p_game_running) {
if (p_game_running) {
override_camera_button->set_disabled(false);
override_camera_button->set_tooltip_text(TTR("Project Camera Override\nOverrides the running project's camera with the editor viewport camera."));
} else {
override_camera_button->set_disabled(true);
override_camera_button->set_pressed(false);
override_camera_button->set_tooltip_text(TTR("Project Camera Override\nNo project instance running. Run the project from the editor to use this feature."));
}
}
void CanvasItemEditor::_popup_callback(int p_op) { void CanvasItemEditor::_popup_callback(int p_op) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
last_option = MenuOption(p_op); last_option = MenuOption(p_op);
@ -5514,16 +5481,6 @@ CanvasItemEditor::CanvasItemEditor() {
main_menu_hbox->add_child(memnew(VSeparator)); main_menu_hbox->add_child(memnew(VSeparator));
override_camera_button = memnew(Button);
override_camera_button->set_theme_type_variation("FlatButton");
main_menu_hbox->add_child(override_camera_button);
override_camera_button->connect(SceneStringName(toggled), callable_mp(this, &CanvasItemEditor::_button_override_camera));
override_camera_button->set_toggle_mode(true);
override_camera_button->set_disabled(true);
_update_override_camera_button(false);
main_menu_hbox->add_child(memnew(VSeparator));
view_menu = memnew(MenuButton); view_menu = memnew(MenuButton);
view_menu->set_flat(false); view_menu->set_flat(false);
view_menu->set_theme_type_variation("FlatMenuButton"); view_menu->set_theme_type_variation("FlatMenuButton");

View File

@ -335,7 +335,6 @@ private:
Button *group_button = nullptr; Button *group_button = nullptr;
Button *ungroup_button = nullptr; Button *ungroup_button = nullptr;
Button *override_camera_button = nullptr;
MenuButton *view_menu = nullptr; MenuButton *view_menu = nullptr;
PopupMenu *grid_menu = nullptr; PopupMenu *grid_menu = nullptr;
PopupMenu *theme_menu = nullptr; PopupMenu *theme_menu = nullptr;
@ -518,11 +517,8 @@ private:
void _zoom_on_position(real_t p_zoom, Point2 p_position = Point2()); void _zoom_on_position(real_t p_zoom, Point2 p_position = Point2());
void _button_toggle_smart_snap(bool p_status); void _button_toggle_smart_snap(bool p_status);
void _button_toggle_grid_snap(bool p_status); void _button_toggle_grid_snap(bool p_status);
void _button_override_camera(bool p_pressed);
void _button_tool_select(int p_index); void _button_tool_select(int p_index);
void _update_override_camera_button(bool p_game_running);
HSplitContainer *left_panel_split = nullptr; HSplitContainer *left_panel_split = nullptr;
HSplitContainer *right_panel_split = nullptr; HSplitContainer *right_panel_split = nullptr;
VSplitContainer *bottom_split = nullptr; VSplitContainer *bottom_split = nullptr;

View File

@ -0,0 +1,478 @@
/**************************************************************************/
/* game_view_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "game_view_plugin.h"
#include "editor/editor_main_screen.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/button.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/panel.h"
#include "scene/gui/separator.h"
void GameViewDebugger::_session_started(Ref<EditorDebuggerSession> p_session) {
p_session->send_message("scene:runtime_node_select_setup", Array());
Array type;
type.append(node_type);
p_session->send_message("scene:runtime_node_select_set_type", type);
Array visible;
visible.append(selection_visible);
p_session->send_message("scene:runtime_node_select_set_visible", visible);
Array mode;
mode.append(select_mode);
p_session->send_message("scene:runtime_node_select_set_mode", mode);
emit_signal(SNAME("session_started"));
}
void GameViewDebugger::_session_stopped() {
emit_signal(SNAME("session_stopped"));
}
void GameViewDebugger::set_suspend(bool p_enabled) {
Array message;
message.append(p_enabled);
for (Ref<EditorDebuggerSession> &I : sessions) {
if (I->is_active()) {
I->send_message("scene:suspend_changed", message);
}
}
}
void GameViewDebugger::next_frame() {
for (Ref<EditorDebuggerSession> &I : sessions) {
if (I->is_active()) {
I->send_message("scene:next_frame", Array());
}
}
}
void GameViewDebugger::set_node_type(int p_type) {
node_type = p_type;
Array message;
message.append(p_type);
for (Ref<EditorDebuggerSession> &I : sessions) {
if (I->is_active()) {
I->send_message("scene:runtime_node_select_set_type", message);
}
}
}
void GameViewDebugger::set_selection_visible(bool p_visible) {
selection_visible = p_visible;
Array message;
message.append(p_visible);
for (Ref<EditorDebuggerSession> &I : sessions) {
if (I->is_active()) {
I->send_message("scene:runtime_node_select_set_visible", message);
}
}
}
void GameViewDebugger::set_select_mode(int p_mode) {
select_mode = p_mode;
Array message;
message.append(p_mode);
for (Ref<EditorDebuggerSession> &I : sessions) {
if (I->is_active()) {
I->send_message("scene:runtime_node_select_set_mode", message);
}
}
}
void GameViewDebugger::set_camera_override(bool p_enabled) {
EditorDebuggerNode::get_singleton()->set_camera_override(p_enabled ? camera_override_mode : EditorDebuggerNode::OVERRIDE_NONE);
}
void GameViewDebugger::set_camera_manipulate_mode(EditorDebuggerNode::CameraOverride p_mode) {
camera_override_mode = p_mode;
if (EditorDebuggerNode::get_singleton()->get_camera_override() != EditorDebuggerNode::OVERRIDE_NONE) {
set_camera_override(true);
}
}
void GameViewDebugger::reset_camera_2d_position() {
for (Ref<EditorDebuggerSession> &I : sessions) {
if (I->is_active()) {
I->send_message("scene:runtime_node_select_reset_camera_2d", Array());
}
}
}
void GameViewDebugger::reset_camera_3d_position() {
for (Ref<EditorDebuggerSession> &I : sessions) {
if (I->is_active()) {
I->send_message("scene:runtime_node_select_reset_camera_3d", Array());
}
}
}
void GameViewDebugger::setup_session(int p_session_id) {
Ref<EditorDebuggerSession> session = get_session(p_session_id);
ERR_FAIL_COND(session.is_null());
sessions.append(session);
session->connect("started", callable_mp(this, &GameViewDebugger::_session_started).bind(session));
session->connect("stopped", callable_mp(this, &GameViewDebugger::_session_stopped));
}
void GameViewDebugger::_bind_methods() {
ADD_SIGNAL(MethodInfo("session_started"));
ADD_SIGNAL(MethodInfo("session_stopped"));
}
///////
void GameView::_sessions_changed() {
// The debugger session's `session_started/stopped` signal can be unreliable, so count it manually.
active_sessions = 0;
Array sessions = debugger->get_sessions();
for (int i = 0; i < sessions.size(); i++) {
if (Object::cast_to<EditorDebuggerSession>(sessions[i])->is_active()) {
active_sessions++;
}
}
_update_debugger_buttons();
}
void GameView::_update_debugger_buttons() {
bool empty = active_sessions == 0;
suspend_button->set_disabled(empty);
camera_override_button->set_disabled(empty);
PopupMenu *menu = camera_override_menu->get_popup();
bool disable_camera_reset = empty || !camera_override_button->is_pressed() || !menu->is_item_checked(menu->get_item_index(CAMERA_MODE_INGAME));
menu->set_item_disabled(CAMERA_RESET_2D, disable_camera_reset);
menu->set_item_disabled(CAMERA_RESET_3D, disable_camera_reset);
if (empty) {
suspend_button->set_pressed(false);
camera_override_button->set_pressed(false);
}
next_frame_button->set_disabled(!suspend_button->is_pressed());
}
void GameView::_suspend_button_toggled(bool p_pressed) {
_update_debugger_buttons();
debugger->set_suspend(p_pressed);
}
void GameView::_node_type_pressed(int p_option) {
RuntimeNodeSelect::NodeType type = (RuntimeNodeSelect::NodeType)p_option;
for (int i = 0; i < RuntimeNodeSelect::NODE_TYPE_MAX; i++) {
node_type_button[i]->set_pressed_no_signal(i == type);
}
_update_debugger_buttons();
debugger->set_node_type(type);
}
void GameView::_select_mode_pressed(int p_option) {
RuntimeNodeSelect::SelectMode mode = (RuntimeNodeSelect::SelectMode)p_option;
for (int i = 0; i < RuntimeNodeSelect::SELECT_MODE_MAX; i++) {
select_mode_button[i]->set_pressed_no_signal(i == mode);
}
debugger->set_select_mode(mode);
}
void GameView::_hide_selection_toggled(bool p_pressed) {
hide_selection->set_button_icon(get_editor_theme_icon(p_pressed ? SNAME("GuiVisibilityHidden") : SNAME("GuiVisibilityVisible")));
debugger->set_selection_visible(!p_pressed);
}
void GameView::_camera_override_button_toggled(bool p_pressed) {
_update_debugger_buttons();
debugger->set_camera_override(p_pressed);
}
void GameView::_camera_override_menu_id_pressed(int p_id) {
PopupMenu *menu = camera_override_menu->get_popup();
if (p_id != CAMERA_RESET_2D && p_id != CAMERA_RESET_3D) {
for (int i = 0; i < menu->get_item_count(); i++) {
menu->set_item_checked(i, false);
}
}
switch (p_id) {
case CAMERA_RESET_2D: {
debugger->reset_camera_2d_position();
} break;
case CAMERA_RESET_3D: {
debugger->reset_camera_3d_position();
} break;
case CAMERA_MODE_INGAME: {
debugger->set_camera_manipulate_mode(EditorDebuggerNode::OVERRIDE_INGAME);
menu->set_item_checked(menu->get_item_index(p_id), true);
_update_debugger_buttons();
} break;
case CAMERA_MODE_EDITORS: {
debugger->set_camera_manipulate_mode(EditorDebuggerNode::OVERRIDE_EDITORS);
menu->set_item_checked(menu->get_item_index(p_id), true);
_update_debugger_buttons();
} break;
}
}
void GameView::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
suspend_button->set_button_icon(get_editor_theme_icon(SNAME("Pause")));
next_frame_button->set_button_icon(get_editor_theme_icon(SNAME("NextFrame")));
node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_button_icon(get_editor_theme_icon(SNAME("InputEventJoypadMotion")));
node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_button_icon(get_editor_theme_icon(SNAME("2DNodes")));
#ifndef _3D_DISABLED
node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_button_icon(get_editor_theme_icon(SNAME("Node3D")));
#endif // _3D_DISABLED
select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_button_icon(get_editor_theme_icon(SNAME("ToolSelect")));
select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->set_button_icon(get_editor_theme_icon(SNAME("ListSelect")));
hide_selection->set_button_icon(get_editor_theme_icon(hide_selection->is_pressed() ? SNAME("GuiVisibilityHidden") : SNAME("GuiVisibilityVisible")));
camera_override_button->set_button_icon(get_editor_theme_icon(SNAME("Camera")));
camera_override_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
} break;
}
}
void GameView::set_state(const Dictionary &p_state) {
if (p_state.has("hide_selection")) {
hide_selection->set_pressed(p_state["hide_selection"]);
_hide_selection_toggled(hide_selection->is_pressed());
}
if (p_state.has("select_mode")) {
_select_mode_pressed(p_state["select_mode"]);
}
if (p_state.has("camera_override_mode")) {
_camera_override_menu_id_pressed(p_state["camera_override_mode"]);
}
}
Dictionary GameView::get_state() const {
Dictionary d;
d["hide_selection"] = hide_selection->is_pressed();
for (int i = 0; i < RuntimeNodeSelect::SELECT_MODE_MAX; i++) {
if (select_mode_button[i]->is_pressed()) {
d["select_mode"] = i;
break;
}
}
PopupMenu *menu = camera_override_menu->get_popup();
for (int i = CAMERA_MODE_INGAME; i < CAMERA_MODE_EDITORS + 1; i++) {
if (menu->is_item_checked(menu->get_item_index(i))) {
d["camera_override_mode"] = i;
break;
}
}
return d;
}
GameView::GameView(Ref<GameViewDebugger> p_debugger) {
debugger = p_debugger;
// Add some margin to the sides for better aesthetics.
// This prevents the first button's hover/pressed effect from "touching" the panel's border,
// which looks ugly.
MarginContainer *toolbar_margin = memnew(MarginContainer);
toolbar_margin->add_theme_constant_override("margin_left", 4 * EDSCALE);
toolbar_margin->add_theme_constant_override("margin_right", 4 * EDSCALE);
add_child(toolbar_margin);
HBoxContainer *main_menu_hbox = memnew(HBoxContainer);
toolbar_margin->add_child(main_menu_hbox);
suspend_button = memnew(Button);
main_menu_hbox->add_child(suspend_button);
suspend_button->set_toggle_mode(true);
suspend_button->set_theme_type_variation("FlatButton");
suspend_button->connect(SceneStringName(toggled), callable_mp(this, &GameView::_suspend_button_toggled));
suspend_button->set_tooltip_text(TTR("Suspend"));
next_frame_button = memnew(Button);
main_menu_hbox->add_child(next_frame_button);
next_frame_button->set_theme_type_variation("FlatButton");
next_frame_button->connect(SceneStringName(pressed), callable_mp(*debugger, &GameViewDebugger::next_frame));
next_frame_button->set_tooltip_text(TTR("Next Frame"));
main_menu_hbox->add_child(memnew(VSeparator));
node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE] = memnew(Button);
main_menu_hbox->add_child(node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]);
node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_text(TTR("Input"));
node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_toggle_mode(true);
node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_pressed(true);
node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_theme_type_variation("FlatButton");
node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->connect(SceneStringName(pressed), callable_mp(this, &GameView::_node_type_pressed).bind(RuntimeNodeSelect::NODE_TYPE_NONE));
node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_tooltip_text(TTR("Allow game input."));
node_type_button[RuntimeNodeSelect::NODE_TYPE_2D] = memnew(Button);
main_menu_hbox->add_child(node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]);
node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_text(TTR("2D"));
node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_toggle_mode(true);
node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_theme_type_variation("FlatButton");
node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->connect(SceneStringName(pressed), callable_mp(this, &GameView::_node_type_pressed).bind(RuntimeNodeSelect::NODE_TYPE_2D));
node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_tooltip_text(TTR("Disable game input and allow to select Node2Ds, Controls, and manipulate the 2D camera."));
#ifndef _3D_DISABLED
node_type_button[RuntimeNodeSelect::NODE_TYPE_3D] = memnew(Button);
main_menu_hbox->add_child(node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]);
node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_text(TTR("3D"));
node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_toggle_mode(true);
node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_theme_type_variation("FlatButton");
node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->connect(SceneStringName(pressed), callable_mp(this, &GameView::_node_type_pressed).bind(RuntimeNodeSelect::NODE_TYPE_3D));
node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_tooltip_text(TTR("Disable game input and allow to select Node3Ds and manipulate the 3D camera."));
#endif // _3D_DISABLED
main_menu_hbox->add_child(memnew(VSeparator));
hide_selection = memnew(Button);
main_menu_hbox->add_child(hide_selection);
hide_selection->set_toggle_mode(true);
hide_selection->set_theme_type_variation("FlatButton");
hide_selection->connect(SceneStringName(toggled), callable_mp(this, &GameView::_hide_selection_toggled));
hide_selection->set_tooltip_text(TTR("Toggle Selection Visibility"));
main_menu_hbox->add_child(memnew(VSeparator));
select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE] = memnew(Button);
main_menu_hbox->add_child(select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]);
select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_toggle_mode(true);
select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_pressed(true);
select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_theme_type_variation("FlatButton");
select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->connect(SceneStringName(pressed), callable_mp(this, &GameView::_select_mode_pressed).bind(RuntimeNodeSelect::SELECT_MODE_SINGLE));
select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_select", TTR("Select Mode"), Key::Q));
select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_shortcut_context(this);
select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_tooltip_text(keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Alt+RMB: Show list of all nodes at position clicked."));
select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST] = memnew(Button);
main_menu_hbox->add_child(select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]);
select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->set_toggle_mode(true);
select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->set_theme_type_variation("FlatButton");
select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->connect(SceneStringName(pressed), callable_mp(this, &GameView::_select_mode_pressed).bind(RuntimeNodeSelect::SELECT_MODE_LIST));
select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->set_tooltip_text(TTR("Show list of selectable nodes at position clicked."));
main_menu_hbox->add_child(memnew(VSeparator));
camera_override_button = memnew(Button);
main_menu_hbox->add_child(camera_override_button);
camera_override_button->set_toggle_mode(true);
camera_override_button->set_theme_type_variation("FlatButton");
camera_override_button->connect(SceneStringName(toggled), callable_mp(this, &GameView::_camera_override_button_toggled));
camera_override_button->set_tooltip_text(TTR("Override the in-game camera."));
camera_override_menu = memnew(MenuButton);
main_menu_hbox->add_child(camera_override_menu);
camera_override_menu->set_flat(false);
camera_override_menu->set_theme_type_variation("FlatMenuButton");
camera_override_menu->set_h_size_flags(SIZE_SHRINK_END);
camera_override_menu->set_tooltip_text(TTR("Camera Override Options"));
PopupMenu *menu = camera_override_menu->get_popup();
menu->connect(SceneStringName(id_pressed), callable_mp(this, &GameView::_camera_override_menu_id_pressed));
menu->add_item(TTR("Reset 2D Camera"), CAMERA_RESET_2D);
menu->add_item(TTR("Reset 3D Camera"), CAMERA_RESET_3D);
menu->add_separator();
menu->add_radio_check_item(TTR("Manipulate In-Game"), CAMERA_MODE_INGAME);
menu->set_item_checked(menu->get_item_index(CAMERA_MODE_INGAME), true);
menu->add_radio_check_item(TTR("Manipulate From Editors"), CAMERA_MODE_EDITORS);
_update_debugger_buttons();
panel = memnew(Panel);
add_child(panel);
panel->set_theme_type_variation("GamePanel");
panel->set_v_size_flags(SIZE_EXPAND_FILL);
p_debugger->connect("session_started", callable_mp(this, &GameView::_sessions_changed));
p_debugger->connect("session_stopped", callable_mp(this, &GameView::_sessions_changed));
}
///////
void GameViewPlugin::make_visible(bool p_visible) {
game_view->set_visible(p_visible);
}
void GameViewPlugin::set_state(const Dictionary &p_state) {
game_view->set_state(p_state);
}
Dictionary GameViewPlugin::get_state() const {
return game_view->get_state();
}
void GameViewPlugin::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
add_debugger_plugin(debugger);
} break;
case NOTIFICATION_EXIT_TREE: {
remove_debugger_plugin(debugger);
} break;
}
}
GameViewPlugin::GameViewPlugin() {
debugger.instantiate();
game_view = memnew(GameView(debugger));
game_view->set_v_size_flags(Control::SIZE_EXPAND_FILL);
EditorNode::get_singleton()->get_editor_main_screen()->get_control()->add_child(game_view);
game_view->hide();
}
GameViewPlugin::~GameViewPlugin() {
}

View File

@ -0,0 +1,152 @@
/**************************************************************************/
/* game_view_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef GAME_VIEW_PLUGIN_H
#define GAME_VIEW_PLUGIN_H
#include "editor/debugger/editor_debugger_node.h"
#include "editor/plugins/editor_debugger_plugin.h"
#include "editor/plugins/editor_plugin.h"
#include "scene/debugger/scene_debugger.h"
#include "scene/gui/box_container.h"
class GameViewDebugger : public EditorDebuggerPlugin {
GDCLASS(GameViewDebugger, EditorDebuggerPlugin);
private:
Vector<Ref<EditorDebuggerSession>> sessions;
int node_type = RuntimeNodeSelect::NODE_TYPE_NONE;
bool selection_visible = true;
int select_mode = RuntimeNodeSelect::SELECT_MODE_SINGLE;
EditorDebuggerNode::CameraOverride camera_override_mode = EditorDebuggerNode::OVERRIDE_INGAME;
void _session_started(Ref<EditorDebuggerSession> p_session);
void _session_stopped();
protected:
static void _bind_methods();
public:
void set_suspend(bool p_enabled);
void next_frame();
void set_node_type(int p_type);
void set_select_mode(int p_mode);
void set_selection_visible(bool p_visible);
void set_camera_override(bool p_enabled);
void set_camera_manipulate_mode(EditorDebuggerNode::CameraOverride p_mode);
void reset_camera_2d_position();
void reset_camera_3d_position();
virtual void setup_session(int p_session_id) override;
GameViewDebugger() {}
};
class GameView : public VBoxContainer {
GDCLASS(GameView, VBoxContainer);
enum {
CAMERA_RESET_2D,
CAMERA_RESET_3D,
CAMERA_MODE_INGAME,
CAMERA_MODE_EDITORS,
};
Ref<GameViewDebugger> debugger;
int active_sessions = 0;
Button *suspend_button = nullptr;
Button *next_frame_button = nullptr;
Button *node_type_button[RuntimeNodeSelect::NODE_TYPE_MAX];
Button *select_mode_button[RuntimeNodeSelect::SELECT_MODE_MAX];
Button *hide_selection = nullptr;
Button *camera_override_button = nullptr;
MenuButton *camera_override_menu = nullptr;
Panel *panel = nullptr;
void _sessions_changed();
void _update_debugger_buttons();
void _suspend_button_toggled(bool p_pressed);
void _node_type_pressed(int p_option);
void _select_mode_pressed(int p_option);
void _hide_selection_toggled(bool p_pressed);
void _camera_override_button_toggled(bool p_pressed);
void _camera_override_menu_id_pressed(int p_id);
protected:
void _notification(int p_what);
public:
void set_state(const Dictionary &p_state);
Dictionary get_state() const;
GameView(Ref<GameViewDebugger> p_debugger);
};
class GameViewPlugin : public EditorPlugin {
GDCLASS(GameViewPlugin, EditorPlugin);
GameView *game_view = nullptr;
Ref<GameViewDebugger> debugger;
protected:
void _notification(int p_what);
public:
virtual String get_name() const override { return "Game"; }
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 set_state(const Dictionary &p_state) override;
virtual Dictionary get_state() const override;
GameViewPlugin();
~GameViewPlugin();
};
#endif // GAME_VIEW_PLUGIN_H

View File

@ -1692,7 +1692,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> b = p_event; Ref<InputEventMouseButton> b = p_event;
if (b.is_valid()) { if (b.is_valid()) {
emit_signal(SNAME("clicked"), this); emit_signal(SNAME("clicked"));
ViewportNavMouseButton orbit_mouse_preference = (ViewportNavMouseButton)EDITOR_GET("editors/3d/navigation/orbit_mouse_button").operator int(); ViewportNavMouseButton orbit_mouse_preference = (ViewportNavMouseButton)EDITOR_GET("editors/3d/navigation/orbit_mouse_button").operator int();
ViewportNavMouseButton pan_mouse_preference = (ViewportNavMouseButton)EDITOR_GET("editors/3d/navigation/pan_mouse_button").operator int(); ViewportNavMouseButton pan_mouse_preference = (ViewportNavMouseButton)EDITOR_GET("editors/3d/navigation/pan_mouse_button").operator int();
@ -4210,7 +4210,7 @@ Dictionary Node3DEditorViewport::get_state() const {
void Node3DEditorViewport::_bind_methods() { void Node3DEditorViewport::_bind_methods() {
ADD_SIGNAL(MethodInfo("toggle_maximize_view", PropertyInfo(Variant::OBJECT, "viewport"))); ADD_SIGNAL(MethodInfo("toggle_maximize_view", PropertyInfo(Variant::OBJECT, "viewport")));
ADD_SIGNAL(MethodInfo("clicked", PropertyInfo(Variant::OBJECT, "viewport"))); ADD_SIGNAL(MethodInfo("clicked"));
} }
void Node3DEditorViewport::reset() { void Node3DEditorViewport::reset() {
@ -6572,18 +6572,6 @@ void Node3DEditor::_menu_item_toggled(bool pressed, int p_option) {
tool_option_button[TOOL_OPT_USE_SNAP]->set_pressed(pressed); tool_option_button[TOOL_OPT_USE_SNAP]->set_pressed(pressed);
snap_enabled = pressed; snap_enabled = pressed;
} break; } break;
case MENU_TOOL_OVERRIDE_CAMERA: {
EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton();
using Override = EditorDebuggerNode::CameraOverride;
if (pressed) {
debugger->set_camera_override((Override)(Override::OVERRIDE_3D_1 + camera_override_viewport_id));
} else {
debugger->set_camera_override(Override::OVERRIDE_NONE);
}
} break;
} }
} }
@ -6610,36 +6598,6 @@ void Node3DEditor::_menu_gizmo_toggled(int p_option) {
update_all_gizmos(); update_all_gizmos();
} }
void Node3DEditor::_update_camera_override_button(bool p_game_running) {
Button *const button = tool_option_button[TOOL_OPT_OVERRIDE_CAMERA];
if (p_game_running) {
button->set_disabled(false);
button->set_tooltip_text(TTR("Project Camera Override\nOverrides the running project's camera with the editor viewport camera."));
} else {
button->set_disabled(true);
button->set_pressed(false);
button->set_tooltip_text(TTR("Project Camera Override\nNo project instance running. Run the project from the editor to use this feature."));
}
}
void Node3DEditor::_update_camera_override_viewport(Object *p_viewport) {
Node3DEditorViewport *current_viewport = Object::cast_to<Node3DEditorViewport>(p_viewport);
if (!current_viewport) {
return;
}
EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton();
camera_override_viewport_id = current_viewport->index;
if (debugger->get_camera_override() >= EditorDebuggerNode::OVERRIDE_3D_1) {
using Override = EditorDebuggerNode::CameraOverride;
debugger->set_camera_override((Override)(Override::OVERRIDE_3D_1 + camera_override_viewport_id));
}
}
void Node3DEditor::_menu_item_pressed(int p_option) { void Node3DEditor::_menu_item_pressed(int p_option) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
switch (p_option) { switch (p_option) {
@ -6670,6 +6628,9 @@ void Node3DEditor::_menu_item_pressed(int p_option) {
} break; } break;
case MENU_VIEW_USE_1_VIEWPORT: { case MENU_VIEW_USE_1_VIEWPORT: {
viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_1_VIEWPORT); viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_1_VIEWPORT);
if (last_used_viewport > 0) {
last_used_viewport = 0;
}
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), true); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), true);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false);
@ -6681,6 +6642,9 @@ void Node3DEditor::_menu_item_pressed(int p_option) {
} break; } break;
case MENU_VIEW_USE_2_VIEWPORTS: { case MENU_VIEW_USE_2_VIEWPORTS: {
viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_2_VIEWPORTS); viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_2_VIEWPORTS);
if (last_used_viewport > 1) {
last_used_viewport = 0;
}
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), true); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), true);
@ -6692,6 +6656,9 @@ void Node3DEditor::_menu_item_pressed(int p_option) {
} break; } break;
case MENU_VIEW_USE_2_VIEWPORTS_ALT: { case MENU_VIEW_USE_2_VIEWPORTS_ALT: {
viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_2_VIEWPORTS_ALT); viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_2_VIEWPORTS_ALT);
if (last_used_viewport > 1) {
last_used_viewport = 0;
}
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false);
@ -6703,6 +6670,9 @@ void Node3DEditor::_menu_item_pressed(int p_option) {
} break; } break;
case MENU_VIEW_USE_3_VIEWPORTS: { case MENU_VIEW_USE_3_VIEWPORTS: {
viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_3_VIEWPORTS); viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_3_VIEWPORTS);
if (last_used_viewport > 2) {
last_used_viewport = 0;
}
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false);
@ -6714,6 +6684,9 @@ void Node3DEditor::_menu_item_pressed(int p_option) {
} break; } break;
case MENU_VIEW_USE_3_VIEWPORTS_ALT: { case MENU_VIEW_USE_3_VIEWPORTS_ALT: {
viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_3_VIEWPORTS_ALT); viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_3_VIEWPORTS_ALT);
if (last_used_viewport > 2) {
last_used_viewport = 0;
}
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false);
@ -8033,7 +8006,6 @@ void Node3DEditor::_update_theme() {
tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_button_icon(get_editor_theme_icon(SNAME("Object"))); tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_button_icon(get_editor_theme_icon(SNAME("Object")));
tool_option_button[TOOL_OPT_USE_SNAP]->set_button_icon(get_editor_theme_icon(SNAME("Snap"))); tool_option_button[TOOL_OPT_USE_SNAP]->set_button_icon(get_editor_theme_icon(SNAME("Snap")));
tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_button_icon(get_editor_theme_icon(SNAME("Camera3D")));
view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), get_editor_theme_icon(SNAME("Panels1"))); view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), get_editor_theme_icon(SNAME("Panels1")));
view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), get_editor_theme_icon(SNAME("Panels2"))); view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), get_editor_theme_icon(SNAME("Panels2")));
@ -8068,9 +8040,6 @@ void Node3DEditor::_notification(int p_what) {
SceneTreeDock::get_singleton()->get_tree_editor()->connect("node_changed", callable_mp(this, &Node3DEditor::_refresh_menu_icons)); SceneTreeDock::get_singleton()->get_tree_editor()->connect("node_changed", callable_mp(this, &Node3DEditor::_refresh_menu_icons));
editor_selection->connect("selection_changed", callable_mp(this, &Node3DEditor::_selection_changed)); editor_selection->connect("selection_changed", callable_mp(this, &Node3DEditor::_selection_changed));
EditorRunBar::get_singleton()->connect("stop_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button).bind(false));
EditorRunBar::get_singleton()->connect("play_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button).bind(true));
_update_preview_environment(); _update_preview_environment();
sun_state->set_custom_minimum_size(sun_vb->get_combined_minimum_size()); sun_state->set_custom_minimum_size(sun_vb->get_combined_minimum_size());
@ -8106,15 +8075,6 @@ void Node3DEditor::_notification(int p_what) {
} }
} break; } break;
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible() && tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->is_pressed()) {
EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_NONE);
tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_pressed(false);
}
} break;
case NOTIFICATION_PHYSICS_PROCESS: { case NOTIFICATION_PHYSICS_PROCESS: {
if (do_snap_selected_nodes_to_floor) { if (do_snap_selected_nodes_to_floor) {
_snap_selected_nodes_to_floor(); _snap_selected_nodes_to_floor();
@ -8216,6 +8176,10 @@ VSplitContainer *Node3DEditor::get_shader_split() {
return shader_split; return shader_split;
} }
Node3DEditorViewport *Node3DEditor::get_last_used_viewport() {
return viewports[last_used_viewport];
}
void Node3DEditor::add_control_to_left_panel(Control *p_control) { void Node3DEditor::add_control_to_left_panel(Control *p_control) {
left_panel_split->add_child(p_control); left_panel_split->add_child(p_control);
left_panel_split->move_child(p_control, 0); left_panel_split->move_child(p_control, 0);
@ -8393,6 +8357,10 @@ void Node3DEditor::_toggle_maximize_view(Object *p_viewport) {
} }
} }
void Node3DEditor::_viewport_clicked(int p_viewport_idx) {
last_used_viewport = p_viewport_idx;
}
void Node3DEditor::_node_added(Node *p_node) { void Node3DEditor::_node_added(Node *p_node) {
if (EditorNode::get_singleton()->get_scene_root()->is_ancestor_of(p_node)) { if (EditorNode::get_singleton()->get_scene_root()->is_ancestor_of(p_node)) {
if (Object::cast_to<WorldEnvironment>(p_node)) { if (Object::cast_to<WorldEnvironment>(p_node)) {
@ -8684,8 +8652,6 @@ Node3DEditor::Node3DEditor() {
snap_key_enabled = false; snap_key_enabled = false;
tool_mode = TOOL_MODE_SELECT; tool_mode = TOOL_MODE_SELECT;
camera_override_viewport_id = 0;
// Add some margin to the sides for better aesthetics. // Add some margin to the sides for better aesthetics.
// This prevents the first button's hover/pressed effect from "touching" the panel's border, // This prevents the first button's hover/pressed effect from "touching" the panel's border,
// which looks ugly. // which looks ugly.
@ -8802,16 +8768,6 @@ Node3DEditor::Node3DEditor() {
tool_option_button[TOOL_OPT_USE_SNAP]->set_shortcut(ED_SHORTCUT("spatial_editor/snap", TTR("Use Snap"), Key::Y)); tool_option_button[TOOL_OPT_USE_SNAP]->set_shortcut(ED_SHORTCUT("spatial_editor/snap", TTR("Use Snap"), Key::Y));
tool_option_button[TOOL_OPT_USE_SNAP]->set_shortcut_context(this); tool_option_button[TOOL_OPT_USE_SNAP]->set_shortcut_context(this);
main_menu_hbox->add_child(memnew(VSeparator));
tool_option_button[TOOL_OPT_OVERRIDE_CAMERA] = memnew(Button);
main_menu_hbox->add_child(tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]);
tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_toggle_mode(true);
tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_theme_type_variation("FlatButton");
tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_disabled(true);
tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->connect(SceneStringName(toggled), callable_mp(this, &Node3DEditor::_menu_item_toggled).bind(MENU_TOOL_OVERRIDE_CAMERA));
_update_camera_override_button(false);
main_menu_hbox->add_child(memnew(VSeparator)); main_menu_hbox->add_child(memnew(VSeparator));
sun_button = memnew(Button); sun_button = memnew(Button);
sun_button->set_tooltip_text(TTR("Toggle preview sunlight.\nIf a DirectionalLight3D node is added to the scene, preview sunlight is disabled.")); sun_button->set_tooltip_text(TTR("Toggle preview sunlight.\nIf a DirectionalLight3D node is added to the scene, preview sunlight is disabled."));
@ -8955,7 +8911,7 @@ Node3DEditor::Node3DEditor() {
for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) {
viewports[i] = memnew(Node3DEditorViewport(this, i)); viewports[i] = memnew(Node3DEditorViewport(this, i));
viewports[i]->connect("toggle_maximize_view", callable_mp(this, &Node3DEditor::_toggle_maximize_view)); viewports[i]->connect("toggle_maximize_view", callable_mp(this, &Node3DEditor::_toggle_maximize_view));
viewports[i]->connect("clicked", callable_mp(this, &Node3DEditor::_update_camera_override_viewport)); viewports[i]->connect("clicked", callable_mp(this, &Node3DEditor::_viewport_clicked).bind(i));
viewports[i]->assign_pending_data_pointers(preview_node, &preview_bounds, accept); viewports[i]->assign_pending_data_pointers(preview_node, &preview_bounds, accept);
viewport_base->add_child(viewports[i]); viewport_base->add_child(viewports[i]);
} }

View File

@ -622,7 +622,6 @@ public:
enum ToolOptions { enum ToolOptions {
TOOL_OPT_LOCAL_COORDS, TOOL_OPT_LOCAL_COORDS,
TOOL_OPT_USE_SNAP, TOOL_OPT_USE_SNAP,
TOOL_OPT_OVERRIDE_CAMERA,
TOOL_OPT_MAX TOOL_OPT_MAX
}; };
@ -632,6 +631,8 @@ private:
Node3DEditorViewportContainer *viewport_base = nullptr; Node3DEditorViewportContainer *viewport_base = nullptr;
Node3DEditorViewport *viewports[VIEWPORTS_COUNT]; Node3DEditorViewport *viewports[VIEWPORTS_COUNT];
int last_used_viewport = 0;
VSplitContainer *shader_split = nullptr; VSplitContainer *shader_split = nullptr;
HSplitContainer *left_panel_split = nullptr; HSplitContainer *left_panel_split = nullptr;
HSplitContainer *right_panel_split = nullptr; HSplitContainer *right_panel_split = nullptr;
@ -704,7 +705,6 @@ private:
MENU_TOOL_LIST_SELECT, MENU_TOOL_LIST_SELECT,
MENU_TOOL_LOCAL_COORDS, MENU_TOOL_LOCAL_COORDS,
MENU_TOOL_USE_SNAP, MENU_TOOL_USE_SNAP,
MENU_TOOL_OVERRIDE_CAMERA,
MENU_TRANSFORM_CONFIGURE_SNAP, MENU_TRANSFORM_CONFIGURE_SNAP,
MENU_TRANSFORM_DIALOG, MENU_TRANSFORM_DIALOG,
MENU_VIEW_USE_1_VIEWPORT, MENU_VIEW_USE_1_VIEWPORT,
@ -759,8 +759,6 @@ private:
void _menu_item_pressed(int p_option); void _menu_item_pressed(int p_option);
void _menu_item_toggled(bool pressed, int p_option); void _menu_item_toggled(bool pressed, int p_option);
void _menu_gizmo_toggled(int p_option); void _menu_gizmo_toggled(int p_option);
void _update_camera_override_button(bool p_game_running);
void _update_camera_override_viewport(Object *p_viewport);
// Used for secondary menu items which are displayed depending on the currently selected node // Used for secondary menu items which are displayed depending on the currently selected node
// (such as MeshInstance's "Mesh" menu). // (such as MeshInstance's "Mesh" menu).
PanelContainer *context_toolbar_panel = nullptr; PanelContainer *context_toolbar_panel = nullptr;
@ -771,8 +769,6 @@ private:
void _generate_selection_boxes(); void _generate_selection_boxes();
int camera_override_viewport_id;
void _init_indicators(); void _init_indicators();
void _update_gizmos_menu(); void _update_gizmos_menu();
void _update_gizmos_menu_theme(); void _update_gizmos_menu_theme();
@ -781,6 +777,7 @@ private:
void _finish_grid(); void _finish_grid();
void _toggle_maximize_view(Object *p_viewport); void _toggle_maximize_view(Object *p_viewport);
void _viewport_clicked(int p_viewport_idx);
Node *custom_camera = nullptr; Node *custom_camera = nullptr;
@ -967,6 +964,7 @@ public:
ERR_FAIL_INDEX_V(p_idx, static_cast<int>(VIEWPORTS_COUNT), nullptr); ERR_FAIL_INDEX_V(p_idx, static_cast<int>(VIEWPORTS_COUNT), nullptr);
return viewports[p_idx]; return viewports[p_idx];
} }
Node3DEditorViewport *get_last_used_viewport();
void add_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin); void add_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin);
void remove_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin); void remove_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin);

View File

@ -1857,6 +1857,12 @@ void EditorThemeManager::_populate_editor_styles(const Ref<EditorTheme> &p_theme
p_theme->set_stylebox("ScriptEditorPanelFloating", EditorStringName(EditorStyles), make_empty_stylebox(0, 0, 0, 0)); p_theme->set_stylebox("ScriptEditorPanelFloating", EditorStringName(EditorStyles), make_empty_stylebox(0, 0, 0, 0));
p_theme->set_stylebox("ScriptEditor", EditorStringName(EditorStyles), make_empty_stylebox(0, 0, 0, 0)); p_theme->set_stylebox("ScriptEditor", EditorStringName(EditorStyles), make_empty_stylebox(0, 0, 0, 0));
// Game view.
p_theme->set_type_variation("GamePanel", "Panel");
Ref<StyleBoxFlat> game_panel = p_theme->get_stylebox(SNAME("panel"), SNAME("Panel"))->duplicate();
game_panel->set_corner_radius_all(0);
p_theme->set_stylebox(SceneStringName(panel), "GamePanel", game_panel);
// Main menu. // Main menu.
Ref<StyleBoxFlat> menu_transparent_style = p_config.button_style->duplicate(); Ref<StyleBoxFlat> menu_transparent_style = p_config.button_style->duplicate();
menu_transparent_style->set_bg_color(Color(1, 1, 1, 0)); menu_transparent_style->set_bg_color(Color(1, 1, 1, 0));

View File

@ -7,7 +7,6 @@ should instead be used to justify these changes and describe how users should wo
Add new entries at the end of the file. Add new entries at the end of the file.
## Changes between 4.3-stable and 4.4-stable ## Changes between 4.3-stable and 4.4-stable
GH-95374 GH-95374
-------- --------
Validate extension JSON: Error: Field 'classes/ShapeCast2D/properties/collision_result': getter changed value in new API, from "_get_collision_result" to &"get_collision_result". Validate extension JSON: Error: Field 'classes/ShapeCast2D/properties/collision_result': getter changed value in new API, from "_get_collision_result" to &"get_collision_result".
@ -102,3 +101,10 @@ GH-97020
Validate extension JSON: Error: Field 'classes/AnimationNode/methods/_process': is_const changed value in new API, from true to false. Validate extension JSON: Error: Field 'classes/AnimationNode/methods/_process': is_const changed value in new API, from true to false.
`_process` virtual method fixed to be non const instead. `_process` virtual method fixed to be non const instead.
GH-97257
--------
Validate extension JSON: Error: Field 'classes/EditorFeatureProfile/enums/Feature/values/FEATURE_MAX': value changed value in new API, from 8.0 to 9.
New entry to the `EditorFeatureProfile.Feature` enum added. Those need to go before `FEATURE_MAX`, which will always cause a compatibility break.

View File

@ -302,6 +302,7 @@ void Camera2D::_notification(int p_what) {
_interpolation_data.xform_prev = _interpolation_data.xform_curr; _interpolation_data.xform_prev = _interpolation_data.xform_curr;
} break; } break;
case NOTIFICATION_SUSPENDED:
case NOTIFICATION_PAUSED: { case NOTIFICATION_PAUSED: {
if (is_physics_interpolated_and_enabled()) { if (is_physics_interpolated_and_enabled()) {
_update_scroll(); _update_scroll();

View File

@ -696,6 +696,8 @@ void GPUParticles2D::_notification(int p_what) {
RS::get_singleton()->particles_set_subemitter(particles, RID()); RS::get_singleton()->particles_set_subemitter(particles, RID());
} break; } break;
case NOTIFICATION_SUSPENDED:
case NOTIFICATION_UNSUSPENDED:
case NOTIFICATION_PAUSED: case NOTIFICATION_PAUSED:
case NOTIFICATION_UNPAUSED: { case NOTIFICATION_UNPAUSED: {
if (is_inside_tree()) { if (is_inside_tree()) {

View File

@ -253,12 +253,20 @@ void NavigationAgent2D::_notification(int p_what) {
#endif // DEBUG_ENABLED #endif // DEBUG_ENABLED
} break; } break;
case NOTIFICATION_SUSPENDED:
case NOTIFICATION_PAUSED: { case NOTIFICATION_PAUSED: {
if (agent_parent) { if (agent_parent) {
NavigationServer2D::get_singleton()->agent_set_paused(get_rid(), !agent_parent->can_process()); NavigationServer2D::get_singleton()->agent_set_paused(get_rid(), !agent_parent->can_process());
} }
} break; } break;
case NOTIFICATION_UNSUSPENDED: {
if (get_tree()->is_paused()) {
break;
}
[[fallthrough]];
}
case NOTIFICATION_UNPAUSED: { case NOTIFICATION_UNPAUSED: {
if (agent_parent) { if (agent_parent) {
NavigationServer2D::get_singleton()->agent_set_paused(get_rid(), !agent_parent->can_process()); NavigationServer2D::get_singleton()->agent_set_paused(get_rid(), !agent_parent->can_process());

View File

@ -104,6 +104,7 @@ void NavigationObstacle2D::_notification(int p_what) {
#endif // DEBUG_ENABLED #endif // DEBUG_ENABLED
} break; } break;
case NOTIFICATION_SUSPENDED:
case NOTIFICATION_PAUSED: { case NOTIFICATION_PAUSED: {
if (!can_process()) { if (!can_process()) {
map_before_pause = map_current; map_before_pause = map_current;
@ -115,6 +116,13 @@ void NavigationObstacle2D::_notification(int p_what) {
NavigationServer2D::get_singleton()->obstacle_set_paused(obstacle, !can_process()); NavigationServer2D::get_singleton()->obstacle_set_paused(obstacle, !can_process());
} break; } break;
case NOTIFICATION_UNSUSPENDED: {
if (get_tree()->is_paused()) {
break;
}
[[fallthrough]];
}
case NOTIFICATION_UNPAUSED: { case NOTIFICATION_UNPAUSED: {
if (!can_process()) { if (!can_process()) {
map_before_pause = map_current; map_before_pause = map_current;

View File

@ -185,6 +185,7 @@ void TouchScreenButton::_notification(int p_what) {
} }
} break; } break;
case NOTIFICATION_SUSPENDED:
case NOTIFICATION_PAUSED: { case NOTIFICATION_PAUSED: {
if (is_pressed()) { if (is_pressed()) {
_release(); _release();

View File

@ -234,6 +234,7 @@ void Camera3D::_notification(int p_what) {
} }
} break; } break;
case NOTIFICATION_SUSPENDED:
case NOTIFICATION_PAUSED: { case NOTIFICATION_PAUSED: {
if (is_physics_interpolated_and_enabled() && is_inside_tree() && is_visible_in_tree()) { if (is_physics_interpolated_and_enabled() && is_inside_tree() && is_visible_in_tree()) {
_physics_interpolation_ensure_transform_calculated(true); _physics_interpolation_ensure_transform_calculated(true);

View File

@ -511,6 +511,8 @@ void GPUParticles3D::_notification(int p_what) {
RS::get_singleton()->particles_set_subemitter(particles, RID()); RS::get_singleton()->particles_set_subemitter(particles, RID());
} break; } break;
case NOTIFICATION_SUSPENDED:
case NOTIFICATION_UNSUSPENDED:
case NOTIFICATION_PAUSED: case NOTIFICATION_PAUSED:
case NOTIFICATION_UNPAUSED: { case NOTIFICATION_UNPAUSED: {
if (is_inside_tree()) { if (is_inside_tree()) {

View File

@ -272,12 +272,20 @@ void NavigationAgent3D::_notification(int p_what) {
#endif // DEBUG_ENABLED #endif // DEBUG_ENABLED
} break; } break;
case NOTIFICATION_SUSPENDED:
case NOTIFICATION_PAUSED: { case NOTIFICATION_PAUSED: {
if (agent_parent) { if (agent_parent) {
NavigationServer3D::get_singleton()->agent_set_paused(get_rid(), !agent_parent->can_process()); NavigationServer3D::get_singleton()->agent_set_paused(get_rid(), !agent_parent->can_process());
} }
} break; } break;
case NOTIFICATION_UNSUSPENDED: {
if (get_tree()->is_paused()) {
break;
}
[[fallthrough]];
}
case NOTIFICATION_UNPAUSED: { case NOTIFICATION_UNPAUSED: {
if (agent_parent) { if (agent_parent) {
NavigationServer3D::get_singleton()->agent_set_paused(get_rid(), !agent_parent->can_process()); NavigationServer3D::get_singleton()->agent_set_paused(get_rid(), !agent_parent->can_process());

View File

@ -119,6 +119,7 @@ void NavigationObstacle3D::_notification(int p_what) {
#endif // DEBUG_ENABLED #endif // DEBUG_ENABLED
} break; } break;
case NOTIFICATION_SUSPENDED:
case NOTIFICATION_PAUSED: { case NOTIFICATION_PAUSED: {
if (!can_process()) { if (!can_process()) {
map_before_pause = map_current; map_before_pause = map_current;
@ -130,6 +131,13 @@ void NavigationObstacle3D::_notification(int p_what) {
NavigationServer3D::get_singleton()->obstacle_set_paused(obstacle, !can_process()); NavigationServer3D::get_singleton()->obstacle_set_paused(obstacle, !can_process());
} break; } break;
case NOTIFICATION_UNSUSPENDED: {
if (get_tree()->is_paused()) {
break;
}
[[fallthrough]];
}
case NOTIFICATION_UNPAUSED: { case NOTIFICATION_UNPAUSED: {
if (!can_process()) { if (!can_process()) {
map_before_pause = map_current; map_before_pause = map_current;

View File

@ -112,6 +112,7 @@ void AudioStreamPlayerInternal::notification(int p_what) {
stream_playbacks.clear(); stream_playbacks.clear();
} break; } break;
case Node::NOTIFICATION_SUSPENDED:
case Node::NOTIFICATION_PAUSED: { case Node::NOTIFICATION_PAUSED: {
if (!node->can_process()) { if (!node->can_process()) {
// Node can't process so we start fading out to silence // Node can't process so we start fading out to silence
@ -119,6 +120,13 @@ void AudioStreamPlayerInternal::notification(int p_what) {
} }
} break; } break;
case Node::NOTIFICATION_UNSUSPENDED: {
if (node->get_tree()->is_paused()) {
break;
}
[[fallthrough]];
}
case Node::NOTIFICATION_UNPAUSED: { case Node::NOTIFICATION_UNPAUSED: {
set_stream_paused(false); set_stream_paused(false);
} break; } break;

File diff suppressed because it is too large Load Diff

View File

@ -31,19 +31,21 @@
#ifndef SCENE_DEBUGGER_H #ifndef SCENE_DEBUGGER_H
#define SCENE_DEBUGGER_H #define SCENE_DEBUGGER_H
#include "core/object/class_db.h" #include "core/input/shortcut.h"
#include "core/object/ref_counted.h" #include "core/object/ref_counted.h"
#include "core/string/ustring.h" #include "core/string/ustring.h"
#include "core/templates/pair.h" #include "core/templates/pair.h"
#include "core/variant/array.h" #include "core/variant/array.h"
#include "scene/gui/view_panner.h"
#include "scene/resources/mesh.h"
class PopupMenu;
class Script; class Script;
class Node; class Node;
class SceneDebugger { class SceneDebugger {
public:
private: private:
static SceneDebugger *singleton; inline static SceneDebugger *singleton = nullptr;
SceneDebugger(); SceneDebugger();
@ -59,6 +61,7 @@ private:
static void _set_node_owner_recursive(Node *p_node, Node *p_owner); static void _set_node_owner_recursive(Node *p_node, Node *p_owner);
static void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value); static void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value);
static void _send_object_id(ObjectID p_id, int p_max_size = 1 << 20); static void _send_object_id(ObjectID p_id, int p_max_size = 1 << 20);
static void _next_frame();
public: public:
static Error parse_message(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured); static Error parse_message(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured);
@ -160,11 +163,161 @@ private:
live_edit_root = NodePath("/root"); live_edit_root = NodePath("/root");
} }
static LiveEditor *singleton; inline static LiveEditor *singleton = nullptr;
public: public:
static LiveEditor *get_singleton(); static LiveEditor *get_singleton();
}; };
class RuntimeNodeSelect : public Object {
GDCLASS(RuntimeNodeSelect, Object);
public:
enum NodeType {
NODE_TYPE_NONE,
NODE_TYPE_2D,
NODE_TYPE_3D,
NODE_TYPE_MAX
};
enum SelectMode {
SELECT_MODE_SINGLE,
SELECT_MODE_LIST,
SELECT_MODE_MAX
};
private:
friend class SceneDebugger;
struct SelectResult {
Node *item = nullptr;
real_t order = 0;
_FORCE_INLINE_ bool operator<(const SelectResult &p_rr) const { return p_rr.order < order; }
};
bool has_selection = false;
Node *selected_node = nullptr;
PopupMenu *selection_list = nullptr;
bool selection_visible = true;
bool selection_update_queued = false;
bool camera_override = false;
// Values taken from EditorZoomWidget.
const float VIEW_2D_MIN_ZOOM = 1.0 / 128;
const float VIEW_2D_MAX_ZOOM = 128;
Ref<ViewPanner> panner;
Vector2 view_2d_offset;
real_t view_2d_zoom = 1.0;
RID sbox_2d_canvas;
RID sbox_2d_ci;
Transform2D sbox_2d_xform;
Rect2 sbox_2d_rect;
#ifndef _3D_DISABLED
struct Cursor {
Vector3 pos;
real_t x_rot, y_rot, distance, fov_scale;
Vector3 eye_pos; // Used in freelook mode.
Cursor() {
// These rotations place the camera in +X +Y +Z, aka south east, facing north west.
x_rot = 0.5;
y_rot = -0.5;
distance = 4;
fov_scale = 1.0;
}
};
Cursor cursor;
// Values taken from Node3DEditor.
const float VIEW_3D_MIN_ZOOM = 0.01;
#ifdef REAL_T_IS_DOUBLE
const double VIEW_3D_MAX_ZOOM = 1'000'000'000'000;
#else
const float VIEW_3D_MAX_ZOOM = 10'000;
#endif
const float CAMERA_ZNEAR = 0.05;
const float CAMERA_ZFAR = 4'000;
const float CAMERA_BASE_FOV = 75;
const float CAMERA_MIN_FOV_SCALE = 0.1;
const float CAMERA_MAX_FOV_SCALE = 2.5;
const float FREELOOK_BASE_SPEED = 4;
const float RADS_PER_PIXEL = 0.004;
bool camera_first_override = true;
bool camera_freelook = false;
Vector2 previous_mouse_position;
Ref<ArrayMesh> sbox_3d_mesh;
Ref<ArrayMesh> sbox_3d_mesh_xray;
RID sbox_3d_instance;
RID sbox_3d_instance_ofs;
RID sbox_3d_instance_xray;
RID sbox_3d_instance_xray_ofs;
Transform3D sbox_3d_xform;
AABB sbox_3d_bounds;
#endif
Point2 selection_position = Point2(INFINITY, INFINITY);
bool list_shortcut_pressed = false;
NodeType node_select_type = NODE_TYPE_2D;
SelectMode node_select_mode = SELECT_MODE_SINGLE;
void _setup();
void _node_set_type(NodeType p_type);
void _select_set_mode(SelectMode p_mode);
void _set_camera_override_enabled(bool p_enabled);
void _root_window_input(const Ref<InputEvent> &p_event);
void _items_popup_index_pressed(int p_index, PopupMenu *p_popup);
void _update_input_state();
void _process_frame();
void _physics_frame();
void _click_point();
void _select_node(Node *p_node);
void _queue_selection_update();
void _update_selection();
void _clear_selection();
void _set_selection_visible(bool p_visible);
void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<SelectResult> &r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event);
void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event);
void _reset_camera_2d();
void _update_view_2d();
#ifndef _3D_DISABLED
void _find_3d_items_at_pos(const Point2 &p_pos, Vector<SelectResult> &r_items);
bool _handle_3d_input(const Ref<InputEvent> &p_event);
void _set_camera_freelook_enabled(bool p_enabled);
void _cursor_scale_distance(real_t p_scale);
void _cursor_look(Ref<InputEventWithModifiers> p_event);
void _cursor_pan(Ref<InputEventWithModifiers> p_event);
void _cursor_orbit(Ref<InputEventWithModifiers> p_event);
Transform3D _get_cursor_transform();
void _reset_camera_3d();
#endif
RuntimeNodeSelect() { singleton = this; }
inline static RuntimeNodeSelect *singleton = nullptr;
public:
static RuntimeNodeSelect *get_singleton();
~RuntimeNodeSelect();
};
#endif #endif
#endif // SCENE_DEBUGGER_H #endif // SCENE_DEBUGGER_H

View File

@ -178,6 +178,7 @@ void VideoStreamPlayer::_notification(int p_notification) {
draw_texture_rect(texture, Rect2(Point2(), s), false); draw_texture_rect(texture, Rect2(Point2(), s), false);
} break; } break;
case NOTIFICATION_SUSPENDED:
case NOTIFICATION_PAUSED: { case NOTIFICATION_PAUSED: {
if (is_playing() && !is_paused()) { if (is_playing() && !is_paused()) {
paused_from_tree = true; paused_from_tree = true;
@ -189,6 +190,13 @@ void VideoStreamPlayer::_notification(int p_notification) {
} }
} break; } break;
case NOTIFICATION_UNSUSPENDED: {
if (get_tree()->is_paused()) {
break;
}
[[fallthrough]];
}
case NOTIFICATION_UNPAUSED: { case NOTIFICATION_UNPAUSED: {
if (paused_from_tree) { if (paused_from_tree) {
paused_from_tree = false; paused_from_tree = false;

View File

@ -184,6 +184,7 @@ void Node::_notification(int p_notification) {
} }
} break; } break;
case NOTIFICATION_SUSPENDED:
case NOTIFICATION_PAUSED: { case NOTIFICATION_PAUSED: {
if (is_physics_interpolated_and_enabled() && is_inside_tree()) { if (is_physics_interpolated_and_enabled() && is_inside_tree()) {
reset_physics_interpolation(); reset_physics_interpolation();
@ -695,6 +696,16 @@ void Node::_propagate_pause_notification(bool p_enable) {
data.blocked--; data.blocked--;
} }
void Node::_propagate_suspend_notification(bool p_enable) {
notification(p_enable ? NOTIFICATION_SUSPENDED : NOTIFICATION_UNSUSPENDED);
data.blocked++;
for (KeyValue<StringName, Node *> &KV : data.children) {
KV.value->_propagate_suspend_notification(p_enable);
}
data.blocked--;
}
Node::ProcessMode Node::get_process_mode() const { Node::ProcessMode Node::get_process_mode() const {
return data.process_mode; return data.process_mode;
} }
@ -850,7 +861,7 @@ bool Node::can_process_notification(int p_what) const {
bool Node::can_process() const { bool Node::can_process() const {
ERR_FAIL_COND_V(!is_inside_tree(), false); ERR_FAIL_COND_V(!is_inside_tree(), false);
return _can_process(get_tree()->is_paused()); return !get_tree()->is_suspended() && _can_process(get_tree()->is_paused());
} }
bool Node::_can_process(bool p_paused) const { bool Node::_can_process(bool p_paused) const {

View File

@ -300,6 +300,7 @@ private:
void _set_tree(SceneTree *p_tree); void _set_tree(SceneTree *p_tree);
void _propagate_pause_notification(bool p_enable); void _propagate_pause_notification(bool p_enable);
void _propagate_suspend_notification(bool p_enable);
_FORCE_INLINE_ bool _can_process(bool p_paused) const; _FORCE_INLINE_ bool _can_process(bool p_paused) const;
_FORCE_INLINE_ bool _is_enabled() const; _FORCE_INLINE_ bool _is_enabled() const;
@ -439,6 +440,8 @@ public:
// Editor specific node notifications // Editor specific node notifications
NOTIFICATION_EDITOR_PRE_SAVE = 9001, NOTIFICATION_EDITOR_PRE_SAVE = 9001,
NOTIFICATION_EDITOR_POST_SAVE = 9002, NOTIFICATION_EDITOR_POST_SAVE = 9002,
NOTIFICATION_SUSPENDED = 9003,
NOTIFICATION_UNSUSPENDED = 9004
}; };
/* NODE/TREE */ /* NODE/TREE */

View File

@ -954,11 +954,14 @@ Ref<ArrayMesh> SceneTree::get_debug_contact_mesh() {
void SceneTree::set_pause(bool p_enabled) { void SceneTree::set_pause(bool p_enabled) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Pause can only be set from the main thread."); ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Pause can only be set from the main thread.");
ERR_FAIL_COND_MSG(suspended, "Pause state cannot be modified while suspended.");
if (p_enabled == paused) { if (p_enabled == paused) {
return; return;
} }
paused = p_enabled; paused = p_enabled;
#ifndef _3D_DISABLED #ifndef _3D_DISABLED
PhysicsServer3D::get_singleton()->set_active(!p_enabled); PhysicsServer3D::get_singleton()->set_active(!p_enabled);
#endif // _3D_DISABLED #endif // _3D_DISABLED
@ -972,6 +975,30 @@ bool SceneTree::is_paused() const {
return paused; return paused;
} }
void SceneTree::set_suspend(bool p_enabled) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Suspend can only be set from the main thread.");
if (p_enabled == suspended) {
return;
}
suspended = p_enabled;
Engine::get_singleton()->set_freeze_time_scale(p_enabled);
#ifndef _3D_DISABLED
PhysicsServer3D::get_singleton()->set_active(!p_enabled && !paused);
#endif // _3D_DISABLED
PhysicsServer2D::get_singleton()->set_active(!p_enabled && !paused);
if (get_root()) {
get_root()->_propagate_suspend_notification(p_enabled);
}
}
bool SceneTree::is_suspended() const {
return suspended;
}
void SceneTree::_process_group(ProcessGroup *p_group, bool p_physics) { void SceneTree::_process_group(ProcessGroup *p_group, bool p_physics) {
// When reading this function, keep in mind that this code must work in a way where // When reading this function, keep in mind that this code must work in a way where
// if any node is removed, this needs to continue working. // if any node is removed, this needs to continue working.

View File

@ -143,6 +143,7 @@ private:
bool debug_navigation_hint = false; bool debug_navigation_hint = false;
#endif #endif
bool paused = false; bool paused = false;
bool suspended = false;
HashMap<StringName, Group> group_map; HashMap<StringName, Group> group_map;
bool _quit = false; bool _quit = false;
@ -343,6 +344,8 @@ public:
void set_pause(bool p_enabled); void set_pause(bool p_enabled);
bool is_paused() const; bool is_paused() const;
void set_suspend(bool p_enabled);
bool is_suspended() const;
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
void set_debug_collisions_hint(bool p_enabled); void set_debug_collisions_hint(bool p_enabled);

View File

@ -3123,7 +3123,7 @@ void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) {
ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND(!is_inside_tree());
ERR_FAIL_COND(p_event.is_null()); ERR_FAIL_COND(p_event.is_null());
if (disable_input) { if (disable_input || disable_input_override) {
return; return;
} }
@ -3195,7 +3195,7 @@ void Viewport::push_unhandled_input(const Ref<InputEvent> &p_event, bool p_local
local_input_handled = false; local_input_handled = false;
if (disable_input || !_can_consume_input_events()) { if (disable_input || disable_input_override || !_can_consume_input_events()) {
return; return;
} }
@ -3298,7 +3298,7 @@ void Viewport::set_disable_input(bool p_disable) {
if (p_disable == disable_input) { if (p_disable == disable_input) {
return; return;
} }
if (p_disable) { if (p_disable && !disable_input_override) {
_drop_mouse_focus(); _drop_mouse_focus();
_mouse_leave_viewport(); _mouse_leave_viewport();
_gui_cancel_tooltip(); _gui_cancel_tooltip();
@ -3311,6 +3311,19 @@ bool Viewport::is_input_disabled() const {
return disable_input; return disable_input;
} }
void Viewport::set_disable_input_override(bool p_disable) {
ERR_MAIN_THREAD_GUARD;
if (p_disable == disable_input_override) {
return;
}
if (p_disable && !disable_input) {
_drop_mouse_focus();
_mouse_leave_viewport();
_gui_cancel_tooltip();
}
disable_input_override = p_disable;
}
Variant Viewport::gui_get_drag_data() const { Variant Viewport::gui_get_drag_data() const {
ERR_READ_THREAD_GUARD_V(Variant()); ERR_READ_THREAD_GUARD_V(Variant());
return get_section_root_viewport()->gui.drag_data; return get_section_root_viewport()->gui.drag_data;
@ -4237,6 +4250,22 @@ void Viewport::set_camera_3d_override_orthogonal(real_t p_size, real_t p_z_near,
} }
} }
HashMap<StringName, real_t> Viewport::get_camera_3d_override_properties() const {
HashMap<StringName, real_t> props;
props["size"] = 0;
props["fov"] = 0;
props["z_near"] = 0;
props["z_far"] = 0;
ERR_READ_THREAD_GUARD_V(props);
props["size"] = camera_3d_override.size;
props["fov"] = camera_3d_override.fov;
props["z_near"] = camera_3d_override.z_near;
props["z_far"] = camera_3d_override.z_far;
return props;
}
void Viewport::set_disable_3d(bool p_disable) { void Viewport::set_disable_3d(bool p_disable) {
ERR_MAIN_THREAD_GUARD; ERR_MAIN_THREAD_GUARD;
disable_3d = p_disable; disable_3d = p_disable;
@ -4270,6 +4299,54 @@ Transform3D Viewport::get_camera_3d_override_transform() const {
return Transform3D(); return Transform3D();
} }
Vector3 Viewport::camera_3d_override_project_ray_normal(const Point2 &p_pos) const {
ERR_READ_THREAD_GUARD_V(Vector3());
Vector3 ray = camera_3d_override_project_local_ray_normal(p_pos);
return camera_3d_override.transform.basis.xform(ray).normalized();
}
Vector3 Viewport::camera_3d_override_project_local_ray_normal(const Point2 &p_pos) const {
ERR_READ_THREAD_GUARD_V(Vector3());
Size2 viewport_size = get_camera_rect_size();
Vector2 cpos = get_camera_coords(p_pos);
Vector3 ray;
if (camera_3d_override.projection == Camera3DOverrideData::PROJECTION_ORTHOGONAL) {
ray = Vector3(0, 0, -1);
} else {
Projection cm;
cm.set_perspective(camera_3d_override.fov, get_visible_rect().size.aspect(), camera_3d_override.z_near, camera_3d_override.z_far, false);
Vector2 screen_he = cm.get_viewport_half_extents();
ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -camera_3d_override.z_near).normalized();
}
return ray;
}
Vector3 Viewport::camera_3d_override_project_ray_origin(const Point2 &p_pos) const {
ERR_READ_THREAD_GUARD_V(Vector3());
Size2 viewport_size = get_camera_rect_size();
Vector2 cpos = get_camera_coords(p_pos);
ERR_FAIL_COND_V(viewport_size.y == 0, Vector3());
if (camera_3d_override.projection == Camera3DOverrideData::PROJECTION_ORTHOGONAL) {
Vector2 pos = cpos / viewport_size;
real_t vsize, hsize;
hsize = camera_3d_override.size * viewport_size.aspect();
vsize = camera_3d_override.size;
Vector3 ray;
ray.x = pos.x * (hsize)-hsize / 2;
ray.y = (1.0 - pos.y) * (vsize)-vsize / 2;
ray.z = -camera_3d_override.z_near;
ray = camera_3d_override.transform.xform(ray);
return ray;
} else {
return camera_3d_override.transform.origin;
};
}
Ref<World3D> Viewport::get_world_3d() const { Ref<World3D> Viewport::get_world_3d() const {
ERR_READ_THREAD_GUARD_V(Ref<World3D>()); ERR_READ_THREAD_GUARD_V(Ref<World3D>());
return world_3d; return world_3d;

View File

@ -401,6 +401,7 @@ private:
DefaultCanvasItemTextureRepeat default_canvas_item_texture_repeat = DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; DefaultCanvasItemTextureRepeat default_canvas_item_texture_repeat = DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
bool disable_input = false; bool disable_input = false;
bool disable_input_override = false;
void _gui_call_input(Control *p_control, const Ref<InputEvent> &p_input); void _gui_call_input(Control *p_control, const Ref<InputEvent> &p_input);
void _gui_call_notification(Control *p_control, int p_what); void _gui_call_notification(Control *p_control, int p_what);
@ -580,6 +581,8 @@ public:
void set_disable_input(bool p_disable); void set_disable_input(bool p_disable);
bool is_input_disabled() const; bool is_input_disabled() const;
void set_disable_input_override(bool p_disable);
Vector2 get_mouse_position() const; Vector2 get_mouse_position() const;
void warp_mouse(const Vector2 &p_position); void warp_mouse(const Vector2 &p_position);
virtual void update_mouse_cursor_state(); virtual void update_mouse_cursor_state();
@ -770,6 +773,11 @@ public:
void set_camera_3d_override_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far); void set_camera_3d_override_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far);
void set_camera_3d_override_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far); void set_camera_3d_override_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far);
HashMap<StringName, real_t> get_camera_3d_override_properties() const;
Vector3 camera_3d_override_project_ray_normal(const Point2 &p_pos) const;
Vector3 camera_3d_override_project_ray_origin(const Point2 &p_pos) const;
Vector3 camera_3d_override_project_local_ray_normal(const Point2 &p_pos) const;
void set_disable_3d(bool p_disable); void set_disable_3d(bool p_disable);
bool is_3d_disabled() const; bool is_3d_disabled() const;