Размеры Control: привязки, отступы и ограничения
Рабочий HUD у правого и нижнего края, формула привязки, minimum/maximum и причина сброса размера.
Четыре края вместо одной позиции
Каждый край Control вычисляется относительно области привязки родителя: край = anchor × размер_родителя + offset. Для левого/правого края используется ширина, для верхнего/нижнего — высота. Anchor 0 соответствует началу оси, 1 — концу. Offset задаётся в логических пикселях интерфейса. После расчёта действуют ограничения размеров и направление роста.
| Размещение | Anchors | Offsets |
|---|---|---|
| Правый верхний HUD шириной 160 | Left = Right = 1, Top = Bottom = 0 | Left -176, Right -16, Top 16, Bottom 80 |
| Нижняя растянутая панель | Left 0, Right 1, Top = Bottom = 1 | Left 16, Right -16, Top -80, Bottom -16 |
| Заполнить родителя | FullRect | Нулевые отступы |
В первом случае при ширине родителя 960 левый край равен 784, правый 944: до края остаётся 16. При ширине 390 левый край становится 214, а ширина остаётся 160. Это не масштабирование содержимого.
Запустить пример
Добавьте UiSampleStyle.cs. Прикрепите AnchorsDemo.cs ниже к корню Control отдельного уровня, соберите и запустите F6. Меняйте размер окна: HP остаётся сверху справа, нижняя панель растягивается, синий прямоугольник сохраняет заданные пределы размера.
AnchorsDemo.cs
using Reckfell;
public partial class AnchorsDemo : Control
{
public PanelContainer Hud { get; private set; } = null!;
public PanelContainer Bottom { get; private set; } = null!;
public Control Bounded { get; private set; } = null!;
public override void _Ready()
{
UiSampleStyle.Prepare(this);
Hud = new PanelContainer { Name = "Hud" }; AddChild(Hud);
Hud.AddChild(new Label { Text = "HP 100", HorizontalAlignment = HorizontalAlignment.Center });
Hud.SetAnchorsPreset(LayoutPreset.TopRight);
Hud.SetOffset(Side.Left, -176); Hud.SetOffset(Side.Right, -16);
Hud.SetOffset(Side.Top, 16); Hud.SetOffset(Side.Bottom, 80);
Bottom = new PanelContainer { Name = "Bottom" }; AddChild(Bottom);
Bottom.AddChild(new Label { Text = UiSampleStyle.Text("Панель у нижнего края", "Bottom-edge panel") });
Bottom.SetAnchorsPreset(LayoutPreset.BottomWide);
Bottom.SetOffset(Side.Left, 16); Bottom.SetOffset(Side.Right, -16);
Bottom.SetOffset(Side.Top, -80); Bottom.SetOffset(Side.Bottom, -16);
Bounded = new ColorRect
{
Name = "Bounded", Color = new Color(.15f, .35f, 1), Position = new Vector2(16, 110),
CustomMinimumSize = new Vector2(64, 32), CustomMaximumSize = new Vector2(160, 90),
MouseFilter = MouseFilterEnum.Ignore
};
AddChild(Bounded); Bounded.Size = new Vector2(999, 999);
}
}Сначала назначается готовый набор anchors, затем offsets. Это избавляет от промежуточного конфликта противоположных привязок. У SetAnchor/SetAnchorAndOffset есть параметры сохранения offsets и сдвига противоположной привязки; порядок отдельных присваиваний может влиять на результат. Не заменяйте ими preset, если нужен стандартный вариант.
SetAnchorsPreset(FullRect) не означает нулевые offsets: с keepOffsets = false привязки меняются с пересчётом offsets для сохранения текущего прямоугольника; true сохраняет численные offsets. Для заполнения родителя с согласованными offsets используйте SetAnchorsAndOffsetsPreset(FullRect) и нужный margin, как у корня примера. Проверьте итоговый Size после применения minimum/maximum.
Minimum и Maximum
Встроенный минимум зависит от текста, шрифта, темы и содержимого контейнера. CustomMinimumSize задаёт дополнительный минимум, а не окончательный размер. Смотрите GetCombinedMinimumSize() для результирующего требования.
CustomMaximumSize ограничивает размер по осям. В примере попытка задать 999 × 999 даёт 160 × 90; попытка задать ноль даёт 64 × 32. Отрицательная компонента maximum означает отсутствие ограничения на этой оси; ноль не означает «без ограничения». Не задавайте minimum больше maximum. Вложенные элементы и контейнеры также могут влиять на ограничения; проверяйте итоговую раскладку, а не только введённое поле.
GrowHorizontal и GrowVertical определяют, в какую сторону меняется прямоугольник при ограничениях: Begin, End или Both. Это не коэффициент увеличения рисунка. Scale тоже не заменяет корректный размер и может мешать ожиданиям от контейнера.
Где привязки не решают задачу
Прямым дочерним Control контейнера управляет контейнер. Настраивайте minimum/maximum и SizeFlags, а не Position каждый кадр. FullRect на родителе не заполняет автоматически всю остальную иерархию. Если интерфейс ниже viewport, ему нужен прокручиваемый контейнер или другая компоновка, а не отрицательный размер.
Этот снимок относится к ResponsiveMenu: карточка стала уже, надпись перенеслась, размер шрифта сохранился. Изменение логической области UI, растяжение рендера и системный DPI — разные настройки.