Hello. I’d like to replicate how Ball x Pit manages its UI, i.e. it uses 2 themes, one for resolutions strictly under 1080p, and another one for 1080p and more. At least that’s how I understood it.
It seems to be impossible to do in Godot, I’ve spent the whole week on it.
Here’s what I would like to do :
the UI elements are drawn at 360p resolution, so that they can integer scale 2× or 3× for 720p and 1080p respectively.
at 720p, some texts use a pixel resolution of 1:1 and others 2:1, at 1080p, they respectively become 2:1 and 3:1.
Any help would be appreciated.
My current takeaways
upscaling from 360p would work wonderfully for the drawn UI elements, guaranteeing that what takes 2 pixels at 720p takes 3 at 1080p.
sadly, upscaling from 360p is out of the equation, because there is no way to triple the resolution and obtain text with a 2:1 resolution for 1080p.
hotswapping theme seems possible in order to scale the text sizes, but no idea how to affect the look of the UI elements.
I am building an app that adjust to the various resolutions of mobile devices. At startup, I copy a base theme and merge it with the default theme, then iterate through every relevant item that requires scaling (fonts, constants, icons, style-boxes, …).
Your base theme needs to include every Control type you need affected.
var _base_theme : Theme = ... # Contains _all_ the types you require
var _ui_scale : float = ... # your scaling factor
func init_theme():
var default_theme = ThemeDB.get_default_theme()
default_theme.merge_with(_base_theme)
scale_theme(default_theme)
default_theme.emit_changed()
func scale_theme(theme: Theme):
const SCALABLES := [
"separation", "h_separation", "v_separation",
"margin_top", "margin_bottom", "margin_left", "margin_right"
]
var types: PackedStringArray = theme.get_type_list()
for type_name: String in types:
var font_sizes: PackedStringArray = theme.get_font_size_list(type_name)
for font_name: String in font_sizes:
if theme.has_font_size(font_name, type_name):
var value: int = theme.get_font_size(font_name, type_name)
var scaled: int = floori(value * _ui_scale)
theme.set_font_size(font_name, type_name, scaled)
var constants: PackedStringArray = theme.get_constant_list(type_name)
for constant_name: String in constants:
if constant_name in SCALABLES and theme.has_constant(constant_name, type_name):
var constant_value: int = theme.get_constant(constant_name, type_name)
var scaled_constant: int = floori(constant_value * _ui_scale)
theme.set_constant(constant_name, type_name, scaled_constant)
var styleboxes: PackedStringArray = theme.get_stylebox_list(type_name)
for stylebox_name: String in styleboxes:
if not theme.has_stylebox(stylebox_name, type_name):
continue
var stylebox: StyleBox = theme.get_stylebox(stylebox_name, type_name)
if stylebox is StyleBoxFlat:
var stylebox_flat: StyleBoxFlat = stylebox
var scaled_stylebox: StyleBoxFlat = stylebox_flat.duplicate() as StyleBoxFlat
scaled_stylebox.border_width_top = floori(stylebox_flat.border_width_top * _ui_scale)
scaled_stylebox.border_width_bottom = floori(stylebox_flat.border_width_bottom * _ui_scale)
scaled_stylebox.border_width_left = floori(stylebox_flat.border_width_left * _ui_scale)
scaled_stylebox.border_width_right = floori(stylebox_flat.border_width_right * _ui_scale)
scaled_stylebox.corner_radius_top_left = floori(stylebox_flat.corner_radius_top_left * _ui_scale)
scaled_stylebox.corner_radius_top_right = floori(stylebox_flat.corner_radius_top_right * _ui_scale)
scaled_stylebox.corner_radius_bottom_left = floori(stylebox_flat.corner_radius_bottom_left * _ui_scale)
scaled_stylebox.corner_radius_bottom_right = floori(stylebox_flat.corner_radius_bottom_right * _ui_scale)
scaled_stylebox.content_margin_top = stylebox_flat.content_margin_top * _ui_scale
scaled_stylebox.content_margin_bottom = stylebox_flat.content_margin_bottom * _ui_scale
scaled_stylebox.content_margin_left = stylebox_flat.content_margin_left * _ui_scale
scaled_stylebox.content_margin_right = stylebox_flat.content_margin_right * _ui_scale
scaled_stylebox.shadow_size = floori(stylebox_flat.shadow_size * _ui_scale)
scaled_stylebox.shadow_offset = stylebox_flat.shadow_offset * _ui_scale
theme.set_stylebox(stylebox_name, type_name, scaled_stylebox)
var icons: PackedStringArray = theme.get_icon_list(type_name)
for icon_name: String in icons:
if theme.has_icon(icon_name, type_name):
var image: Texture2D = get_icon(icon_name)
if image != null:
theme.set_icon(icon_name, type_name, image)