├── screenshot ├── UI_Menu.png └── UI_CustomPopup.png ├── .gitignore ├── addons └── resolutionChanger │ ├── plugin.cfg │ ├── resolutionMenu.tscn │ ├── icon_GUI_dropdown.svg │ ├── icon_GUI_dropdown.svg.import │ ├── icon_rectangle_shape_2d.svg.import │ ├── Resolutions.txt │ ├── AddResolution.tscn │ └── init.gd ├── LICENSE └── README.md /screenshot/UI_Menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grevius5/godot-ResolutionChanger/HEAD/screenshot/UI_Menu.png -------------------------------------------------------------------------------- /screenshot/UI_CustomPopup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grevius5/godot-ResolutionChanger/HEAD/screenshot/UI_CustomPopup.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Godot-specific ignores 3 | .import/ 4 | export.cfg 5 | export_presets.cfg 6 | 7 | # Mono-specific ignores 8 | .mono/ 9 | -------------------------------------------------------------------------------- /addons/resolutionChanger/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="Resolution Changer" 4 | description="Add-ons for Godot that allow you to change test resolution for game with 2 clicks." 5 | author="Federico Vitali" 6 | version="1.0" 7 | script="init.gd" 8 | -------------------------------------------------------------------------------- /addons/resolutionChanger/resolutionMenu.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene format=2] 2 | 3 | [node name="Control" type="Control"] 4 | anchor_right = 1.0 5 | anchor_bottom = 1.0 6 | 7 | [node name="Resolutions" type="MenuButton" parent="."] 8 | margin_right = 12.0 9 | margin_bottom = 20.0 10 | text = "Resolutions" 11 | align = 0 12 | 13 | -------------------------------------------------------------------------------- /addons/resolutionChanger/icon_GUI_dropdown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /addons/resolutionChanger/icon_GUI_dropdown.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_GUI_dropdown.svg-8f1278c4ae9b3b81385419e5c55c7bb8.stex" 6 | 7 | [deps] 8 | 9 | source_file="res://addons/resolutionChanger/icon_GUI_dropdown.svg" 10 | dest_files=[ "res://.import/icon_GUI_dropdown.svg-8f1278c4ae9b3b81385419e5c55c7bb8.stex" ] 11 | 12 | [params] 13 | 14 | compress/mode=0 15 | compress/lossy_quality=0.7 16 | compress/hdr_mode=0 17 | compress/bptc_ldr=0 18 | compress/normal_map=0 19 | flags/repeat=0 20 | flags/filter=true 21 | flags/mipmaps=false 22 | flags/anisotropic=false 23 | flags/srgb=2 24 | process/fix_alpha_border=true 25 | process/premult_alpha=false 26 | process/HDR_as_SRGB=false 27 | process/invert_color=false 28 | stream=false 29 | size_limit=0 30 | detect_3d=true 31 | svg/scale=1.0 32 | -------------------------------------------------------------------------------- /addons/resolutionChanger/icon_rectangle_shape_2d.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_rectangle_shape_2d.svg-725e690f682c60b6de0d12ea5a7d9adc.stex" 6 | 7 | [deps] 8 | 9 | source_file="res://addons/resolutionChanger/icon_rectangle_shape_2d.svg" 10 | dest_files=[ "res://.import/icon_rectangle_shape_2d.svg-725e690f682c60b6de0d12ea5a7d9adc.stex" ] 11 | 12 | [params] 13 | 14 | compress/mode=0 15 | compress/lossy_quality=0.7 16 | compress/hdr_mode=0 17 | compress/bptc_ldr=0 18 | compress/normal_map=0 19 | flags/repeat=0 20 | flags/filter=true 21 | flags/mipmaps=false 22 | flags/anisotropic=false 23 | flags/srgb=2 24 | process/fix_alpha_border=true 25 | process/premult_alpha=false 26 | process/HDR_as_SRGB=false 27 | process/invert_color=false 28 | stream=false 29 | size_limit=0 30 | detect_3d=true 31 | svg/scale=1.0 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Federico Vitali 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /addons/resolutionChanger/Resolutions.txt: -------------------------------------------------------------------------------- 1 | [Desktop] 2 | 3 | Desktop_4:3=Vector2( 1024, 768 ) 4 | Desktop_FWXGA=Vector2( 1366, 768 ) 5 | Desktop_HD=Vector2( 1280, 720 ) 6 | Desktop_FHD=Vector2( 1920, 1080 ) 7 | 8 | [Android] 9 | 10 | FWVGA_Landscape=Vector2( 854, 480 ) 11 | FWVGA_Portrait=Vector2( 480, 854 ) 12 | HVGA_Landscape=Vector2( 480, 320 ) 13 | HVGA_Portrait=Vector2( 320, 480 ) 14 | QHD=Vector2( 480, 270 ) 15 | WSVGA_Landscape=Vector2( 1024, 600 ) 16 | WSVGA_Portrait=Vector2( 600, 1024 ) 17 | WVGA_Landscape=Vector2( 800, 480 ) 18 | WVGA_Portrait=Vector2( 480, 800 ) 19 | WXGA_Landscape=Vector2( 1280, 800 ) 20 | WXGA_Portrait=Vector2( 800, 1280 ) 21 | A6_Landscape=Vector2( 1480, 720 ) 22 | A6_Portrait=Vector2( 720, 1480 ) 23 | 24 | [iPhone] 25 | 26 | iPhone_4/4S_Landscape=Vector2( 960, 640 ) 27 | iPhone_4/4S_Portrait=Vector2( 640, 960 ) 28 | iPhone_5_Landscape=Vector2( 1136, 640 ) 29 | iPhone_5_Portrait=Vector2( 640, 1136 ) 30 | 31 | [iPad] 32 | 33 | iPad_1/2/Mini_Landscape=Vector2( 1024, 768 ) 34 | iPad_1/2/Mini_Portrait=Vector2( 768, 1024 ) 35 | iPad_3/4_Landscape=Vector2( 2048, 1536 ) 36 | iPad_3/4_Portrait=Vector2( 1536, 2048 ) 37 | 38 | [Other] 39 | 40 | Square=Vector2( 400, 400 ) 41 | Square_200x200=Vector2( 200, 200 ) 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Resolution Changer 2 | 3 | A fast and easy way to change the test resolution of your game with only 2 clicks. 4 | 5 | **This addon is only tested with Godot 3.1 beta. Godot 3.0.6 is not supported!** 6 | 7 | ## Description 8 | 9 | Resolution Changer is the fastest way to test your game with different resolutions. 10 | Simply to click the menu button and pick one resolution. 11 | There are almost all common resolutions available, but you can also add your own 12 | resolutions with the **Add custom...** menu. 13 | All resolutions are stored in categories and are reorganizable. 14 | 15 | ## Getting Started 16 | 17 | ### Installation 18 | 19 | Copy the `addons/resolutionChanger` folder in your project's `addons` folder 20 | (create it if it doesn't exist). Once you've done this, open your project in Godot, 21 | go to the **Project Settings**, click the **Plugins** tab and enable **Resolution Changer**. 22 | 23 | ### How to use 24 | 25 | Click on the **Resolutions** menu button and pick one of the available resolutions. 26 | 27 | ![UI_Menu](screenshot/UI_Menu.png?raw=true "UI Menu") 28 | 29 | If you need a custom resolution, click on the **Resolutions** menu button 30 | and pick the last item **Add custom...**. A popup will appear: 31 | 32 | ![UI_CustomPopup](screenshot/UI_CustomPopup.png?raw=true "UI Custom Popup") 33 | 34 | Define the device's type, name, screen width and height then click **Save**. 35 | The newly-added resolution will be listed with the other ones. 36 | 37 | ### Documentation 38 | 39 | All resolutions are stored in the `Resolutions.txt` file which is located 40 | in the addon folder. If you want to delete some resolutions, you have to do it 41 | manually by removing the corresponding record from `Resolutions.txt`. 42 | -------------------------------------------------------------------------------- /addons/resolutionChanger/AddResolution.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://addons/resolutionChanger/icon_GUI_dropdown.svg" type="Texture" id=1] 4 | 5 | [node name="AddResolution" type="Control"] 6 | anchor_right = 1.0 7 | anchor_bottom = 1.0 8 | 9 | [node name="WindowDialog" type="WindowDialog" parent="."] 10 | visible = true 11 | anchor_left = 0.5 12 | anchor_top = 0.5 13 | anchor_right = 0.5 14 | anchor_bottom = 0.5 15 | margin_left = -100.0 16 | margin_top = -100.0 17 | margin_right = 150.0 18 | margin_bottom = 60.0 19 | rect_min_size = Vector2( 250, 160 ) 20 | size_flags_horizontal = 3 21 | size_flags_vertical = 3 22 | window_title = "Custom Resolution" 23 | 24 | [node name="MarginContainer" type="MarginContainer" parent="WindowDialog"] 25 | anchor_right = 1.0 26 | anchor_bottom = 1.0 27 | custom_constants/margin_right = 8 28 | custom_constants/margin_top = 8 29 | custom_constants/margin_left = 8 30 | custom_constants/margin_bottom = 8 31 | 32 | [node name="VerticalContainer" type="VBoxContainer" parent="WindowDialog/MarginContainer"] 33 | margin_left = 8.0 34 | margin_top = 8.0 35 | margin_right = 242.0 36 | margin_bottom = 152.0 37 | custom_constants/separation = 8 38 | 39 | [node name="DeviceContainer" type="HBoxContainer" parent="WindowDialog/MarginContainer/VerticalContainer"] 40 | margin_right = 234.0 41 | margin_bottom = 20.0 42 | 43 | [node name="Label" type="Label" parent="WindowDialog/MarginContainer/VerticalContainer/DeviceContainer"] 44 | margin_top = 3.0 45 | margin_right = 48.0 46 | margin_bottom = 17.0 47 | text = "Device:" 48 | 49 | [node name="Device" type="MenuButton" parent="WindowDialog/MarginContainer/VerticalContainer/DeviceContainer"] 50 | margin_left = 52.0 51 | margin_right = 128.0 52 | margin_bottom = 20.0 53 | text = "Setions" 54 | icon = ExtResource( 1 ) 55 | 56 | [node name="LabelContainer" type="HBoxContainer" parent="WindowDialog/MarginContainer/VerticalContainer"] 57 | editor/display_folded = true 58 | margin_top = 28.0 59 | margin_right = 234.0 60 | margin_bottom = 52.0 61 | 62 | [node name="Label" type="Label" parent="WindowDialog/MarginContainer/VerticalContainer/LabelContainer"] 63 | margin_top = 5.0 64 | margin_right = 50.0 65 | margin_bottom = 19.0 66 | rect_min_size = Vector2( 50, 0 ) 67 | text = "Label:" 68 | valign = 1 69 | 70 | [node name="LineEdit" type="LineEdit" parent="WindowDialog/MarginContainer/VerticalContainer/LabelContainer"] 71 | margin_left = 54.0 72 | margin_right = 234.0 73 | margin_bottom = 24.0 74 | size_flags_horizontal = 3 75 | size_flags_vertical = 3 76 | placeholder_text = "Custom" 77 | caret_blink = true 78 | 79 | [node name="WidthContainer" type="HBoxContainer" parent="WindowDialog/MarginContainer/VerticalContainer"] 80 | margin_top = 60.0 81 | margin_right = 234.0 82 | margin_bottom = 84.0 83 | 84 | [node name="Width" type="Label" parent="WindowDialog/MarginContainer/VerticalContainer/WidthContainer"] 85 | margin_top = 5.0 86 | margin_right = 50.0 87 | margin_bottom = 19.0 88 | rect_min_size = Vector2( 50, 0 ) 89 | text = "Width:" 90 | valign = 1 91 | 92 | [node name="SpinBox" type="SpinBox" parent="WindowDialog/MarginContainer/VerticalContainer/WidthContainer"] 93 | margin_left = 54.0 94 | margin_right = 234.0 95 | margin_bottom = 24.0 96 | size_flags_horizontal = 3 97 | size_flags_vertical = 3 98 | min_value = 1.0 99 | max_value = 10000.0 100 | value = 1024.0 101 | 102 | [node name="HeightContainer" type="HBoxContainer" parent="WindowDialog/MarginContainer/VerticalContainer"] 103 | margin_top = 92.0 104 | margin_right = 234.0 105 | margin_bottom = 116.0 106 | 107 | [node name="Height" type="Label" parent="WindowDialog/MarginContainer/VerticalContainer/HeightContainer"] 108 | margin_top = 5.0 109 | margin_right = 50.0 110 | margin_bottom = 19.0 111 | rect_min_size = Vector2( 50, 0 ) 112 | text = "Height:" 113 | valign = 1 114 | 115 | [node name="SpinBox" type="SpinBox" parent="WindowDialog/MarginContainer/VerticalContainer/HeightContainer"] 116 | margin_left = 54.0 117 | margin_right = 234.0 118 | margin_bottom = 24.0 119 | size_flags_horizontal = 3 120 | size_flags_vertical = 3 121 | min_value = 1.0 122 | max_value = 10000.0 123 | value = 768.0 124 | 125 | [node name="Buttons" type="HBoxContainer" parent="WindowDialog/MarginContainer/VerticalContainer"] 126 | editor/display_folded = true 127 | margin_top = 124.0 128 | margin_right = 234.0 129 | margin_bottom = 144.0 130 | size_flags_horizontal = 3 131 | custom_constants/separation = 16 132 | alignment = 2 133 | 134 | [node name="Cancel" type="Button" parent="WindowDialog/MarginContainer/VerticalContainer/Buttons"] 135 | margin_left = 123.0 136 | margin_right = 177.0 137 | margin_bottom = 20.0 138 | text = "Cancel" 139 | 140 | [node name="Save" type="Button" parent="WindowDialog/MarginContainer/VerticalContainer/Buttons"] 141 | margin_left = 193.0 142 | margin_right = 234.0 143 | margin_bottom = 20.0 144 | text = "Save" 145 | 146 | -------------------------------------------------------------------------------- /addons/resolutionChanger/init.gd: -------------------------------------------------------------------------------- 1 | tool extends EditorPlugin 2 | 3 | # Constant 4 | const CONFIG_PATH = "res://addons/resolutionChanger/Resolutions.txt" 5 | 6 | # UI elements 7 | var dock : Control 8 | var custom_resolution : Control 9 | 10 | # Resolution data 11 | var resolutions_data : Dictionary = {} 12 | var config = ConfigFile.new() 13 | 14 | # UI elements 15 | var popup : PopupMenu 16 | var menu : MenuButton 17 | 18 | # Custom data vars 19 | var custom_sections : MenuButton 20 | var custom_label : LineEdit 21 | var custom_width : SpinBox 22 | var custom_height : SpinBox 23 | 24 | func _enter_tree() -> void: 25 | # Preload needed scenes 26 | dock = preload("res://addons/resolutionChanger/resolutionMenu.tscn").instance() as Control 27 | custom_resolution = preload("res://addons/resolutionChanger/AddResolution.tscn").instance() as Control 28 | 29 | # Get all data for custom resolution 30 | custom_sections = custom_resolution.get_node("WindowDialog/MarginContainer/VerticalContainer/DeviceContainer/Device") as MenuButton 31 | custom_label = custom_resolution.get_node("WindowDialog/MarginContainer/VerticalContainer/LabelContainer/LineEdit") as LineEdit 32 | custom_width = custom_resolution.get_node("WindowDialog/MarginContainer/VerticalContainer/WidthContainer/SpinBox") as SpinBox 33 | custom_height = custom_resolution.get_node("WindowDialog/MarginContainer/VerticalContainer/HeightContainer/SpinBox") as SpinBox 34 | 35 | # Connect device selection button 36 | custom_sections.get_popup().connect("id_pressed", self, "custom_device_selection") 37 | 38 | # Connect the save and cancel button 39 | (custom_resolution.get_node("WindowDialog/MarginContainer/VerticalContainer/Buttons/Cancel") as Button).connect("pressed", self, "close_popup") 40 | (custom_resolution.get_node("WindowDialog/MarginContainer/VerticalContainer/Buttons/Save") as Button).connect("pressed", self, "save_popup") 41 | 42 | # Load config file 43 | config.load(CONFIG_PATH) 44 | 45 | # Populate all resolution data 46 | populateResolution() 47 | popup.connect("id_pressed", self, "switched") 48 | 49 | # Add elements to editor ui 50 | add_control_to_container(EditorPlugin.CONTAINER_CANVAS_EDITOR_MENU, dock) 51 | add_control_to_container(EditorPlugin.CONTAINER_CANVAS_EDITOR_MENU, custom_resolution) 52 | 53 | # Trick to have the popup of right size 54 | (custom_resolution.get_node("WindowDialog") as WindowDialog).popup_centered() 55 | (custom_resolution.get_node("WindowDialog") as WindowDialog).hide() 56 | 57 | check_selected_resolution() 58 | 59 | 60 | # Check if one of the existent resolution were used 61 | func check_selected_resolution() -> void: 62 | var width : int = ProjectSettings.get("display/window/size/test_width") as int 63 | var height : int = ProjectSettings.get("display/window/size/test_height") as int 64 | 65 | for i in resolutions_data: 66 | var res : ResolutionData = resolutions_data[i] as ResolutionData 67 | 68 | # check if resolution are the same 69 | if res.size.x == width && res.size.y == height: 70 | menu.text = create_label_name(res.label, res.size) 71 | 72 | 73 | 74 | # Function to clear all existent resolution (used befor reloading) 75 | func clear_resolution() -> void: 76 | (dock.get_node("Resolutions") as MenuButton).get_popup().clear() 77 | resolutions_data.clear() 78 | 79 | 80 | func populateResolution() -> void: 81 | clear_resolution() 82 | 83 | # Get menu button 84 | menu = dock.get_node("Resolutions") as MenuButton 85 | popup = menu.get_popup() as PopupMenu 86 | 87 | var id : int = 0 88 | for section in config.get_sections(): 89 | # Create section item 90 | popup.add_separator(section) 91 | custom_sections.get_popup().add_radio_check_item(section, id) 92 | 93 | id = id + 1 94 | 95 | for label in config.get_section_keys(section): 96 | # Create resolution item 97 | var data = ResolutionData.new() 98 | var resolution : Vector2 = config.get_value(section, label) as Vector2 99 | data.label = label 100 | data.size = resolution 101 | data.id = id 102 | 103 | resolutions_data[id] = data 104 | 105 | popup.add_item(create_label_name(label, resolution), data.id) 106 | popup.set_item_tooltip(data.id, "Rateo: %.2f" % (resolution.x / resolution.y) ) 107 | 108 | id = id + 1 109 | 110 | popup.add_separator("") 111 | id = id + 1 112 | # We use id + 1 because separator get one id position 113 | popup.add_item("Add custom...", id) 114 | 115 | 116 | func create_label_name(label : String, resolution: Vector2) -> String: 117 | return (label as String).replace("_", " ") + " (" + str(resolution.x) + "x" + str(resolution.y) + ")" 118 | 119 | 120 | func save_popup() -> void: 121 | var section : String = get_selected_device() 122 | var label : String = custom_label.text.replace(" ", "_") 123 | var width : int = custom_width.value 124 | var height : int = custom_height.value 125 | 126 | if label != "": 127 | config.set_value(section, label, Vector2(width, height)) 128 | config.save(CONFIG_PATH) 129 | 130 | populateResolution() 131 | 132 | close_popup() 133 | 134 | 135 | func close_popup() -> void: 136 | (custom_resolution.get_node("WindowDialog") as WindowDialog).hide() 137 | 138 | 139 | func switched(id : int) -> void: 140 | var label = menu.get_popup().get_item_text(id) 141 | 142 | # Check for resolution selection or creation 143 | if label != "Add custom...": 144 | var data : ResolutionData = resolutions_data[id] as ResolutionData 145 | menu.text = create_label_name(data.label, data.size) 146 | 147 | ProjectSettings.set("display/window/size/test_width", data.size.x) 148 | ProjectSettings.set("display/window/size/test_height", data.size.y) 149 | ProjectSettings.save() 150 | else: 151 | # Show custom popup 152 | deselect_all_devices() 153 | 154 | custom_label.text = "" 155 | custom_width.value = 1024 156 | custom_height.value = 768 157 | custom_device_selection(0) 158 | 159 | var size : Vector2 = (custom_resolution.get_node("WindowDialog/MarginContainer") as MarginContainer).rect_size 160 | (custom_resolution.get_node("WindowDialog") as WindowDialog).popup_centered_minsize(size) 161 | 162 | 163 | # Select device from button menu by it's id 164 | func custom_device_selection(id: int) -> void: 165 | deselect_all_devices() 166 | 167 | # Check the selected one 168 | var index = custom_sections.get_popup().get_item_index(id) 169 | custom_sections.get_popup().set_item_checked(index, true) 170 | 171 | custom_sections.text = custom_sections.get_popup().get_item_text(index) 172 | 173 | 174 | # Get the selected device or "Others" as default 175 | func get_selected_device() -> String: 176 | # Return the selected device 177 | for i in custom_sections.get_popup().get_item_count(): 178 | if custom_sections.get_popup().is_item_checked(i): 179 | return custom_sections.get_popup().get_item_text(i) 180 | 181 | return "Others" 182 | 183 | 184 | # Function to deselect all devices from the button menu 185 | func deselect_all_devices() -> void: 186 | # Uncheck all devices 187 | for i in custom_sections.get_popup().get_item_count(): 188 | custom_sections.get_popup().set_item_checked(i, false) 189 | 190 | 191 | func _exit_tree() -> void: 192 | # Remove dock 193 | remove_control_from_container(EditorPlugin.CONTAINER_CANVAS_EDITOR_MENU, dock) 194 | dock.queue_free() 195 | 196 | # Remove custom resolution 197 | remove_control_from_container(EditorPlugin.CONTAINER_CANVAS_EDITOR_MENU, custom_resolution) 198 | custom_resolution.queue_free() 199 | 200 | 201 | # Usefull class for resolution data 202 | class ResolutionData: 203 | var id : int 204 | var label : String 205 | var size : Vector2 206 | 207 | func _init() -> void: 208 | pass --------------------------------------------------------------------------------