/**************************************************************************/ /* 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 "core/debugger/debugger_marshalls.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 p_session) { Array setup_data; Dictionary settings; settings["editors/panning/2d_editor_panning_scheme"] = EDITOR_GET("editors/panning/2d_editor_panning_scheme"); settings["editors/panning/simple_panning"] = EDITOR_GET("editors/panning/simple_panning"); settings["editors/panning/warped_mouse_panning"] = EDITOR_GET("editors/panning/warped_mouse_panning"); settings["editors/panning/2d_editor_pan_speed"] = EDITOR_GET("editors/panning/2d_editor_pan_speed"); settings["canvas_item_editor/pan_view"] = DebuggerMarshalls::serialize_key_shortcut(ED_GET_SHORTCUT("canvas_item_editor/pan_view")); setup_data.append(settings); p_session->send_message("scene:runtime_node_select_setup", setup_data); 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 &I : sessions) { if (I->is_active()) { I->send_message("scene:suspend_changed", message); } } } void GameViewDebugger::next_frame() { for (Ref &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 &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 &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 &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 &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 &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 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(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 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() { }