Filling exterior of control with a fixed aspect ratio

Godot Version

v4.3.stable.official [77dcf97d8]

Question

I want to make a responsive scope UI.
The texture I use is a square that must keep its ratio, which I do by setting “Stretch Mode” to “Keep Aspect Centered”.

Now I want to black out the exterior parts (marked in red).

Using a ColorRect on the whole screen is not an option, because I need to keep the transparent parts without any background so that the player can see what he’s shooting.

I tried a lot of things, and I can’t do it. Using containers didn’t help. In every case, the node in charge of keeping the ratio takes all the space, so trying to patch the rest with ColorRects it useless.

Do also tell me if my approach of using UI and transparency is totally wrong.
Thanks,

I can script dynamic borders that way.
I works well.

image

extends Control

@onready var visor: TextureRect = $Visor
@onready var top_color_rect: ColorRect = $TopColorRect
@onready var bottom_color_rect: ColorRect = $BottomColorRect
@onready var left_color_rect: ColorRect = $LeftColorRect
@onready var right_color_rect: ColorRect = $RightColorRect

var last_size: Vector2 = Vector2.ZERO

func _process(_delta: float) -> void:
	# Only update the border if the window has been resized
	if visor.size != self.last_size:
		self.update_borders()
		self.last_size = visor.size

## Update the transform of the colored rects on each size of the screen, so that the visor is
## always tightly surrounded.
func update_borders() -> void:
	var size: Vector2 = visor.size
	
	if (size.x >= size.y):  # Landscape mode
		var border_width: float = (size.x - size.y) / 2
		top_color_rect.size = Vector2.ZERO
		bottom_color_rect.size = Vector2.ZERO
		left_color_rect.size = Vector2(border_width, size.y)
		right_color_rect.size = Vector2(border_width, size.y)
		right_color_rect.position = Vector2(size.x - border_width, 0)
	else: # Portrait mode
		var border_height: float = (size.y - size.x) / 2
		top_color_rect.size = Vector2(size.x, border_height)
		bottom_color_rect.size = Vector2(size.x, border_height)
		bottom_color_rect.position = Vector2(0, size.y - border_height)
		left_color_rect.size = Vector2.ZERO
		right_color_rect.size = Vector2.ZERO

But it’s a bit sad having to script the UI when it’s this powerful in Godot.

Just add black sections to both sides of your texture image (so much that the resulting ratio is not useful at all, this helps the edges never remain blank) and apply these settings:
image
For me this worked, the image always keeps its height the same as the page and the corners are filled with extra space.

There’s two problems with this approach :

  • The aspect ratio is not kept in portrait resolutions.
  • I don’t like the idea that if you grow the window too much, you could grow out of the size of the image thus not having borders anymore.