Окна города: содержимое в безопасной зоне

На iPhone левый край окон («Войска» и др.) уходил под вырез камеры.
BasePanel переносит Content в контейнер SafeArea; фон окна остаётся как был.
SafeArea.fitInsideParent: зона пересчитывается в координаты родителя, если он не на весь экран.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Alexander
2026-10-27 17:02:04 +01:00
co-authored by Claude Opus 5.5
parent b11b97607f
commit 99b7d38b6e
3 changed files with 56 additions and 5 deletions
+12
View File
@@ -64,11 +64,23 @@ public abstract class BasePanel : MonoBehaviour
t = UIKit.Stretch(UIKit.Make("Content", transform), 24, 130, 24, 24);
}
content = (RectTransform)t;
WrapInSafeArea(content);
}
return content;
}
}
// Фон окна -- на весь экран, а содержимое -- в безопасной зоне: на iPhone левый край уходил под вырез камеры.
// Content переезжает в растянутый контейнер с SafeArea и сохраняет свои отступы.
void WrapInSafeArea(RectTransform rt)
{
if (rt.parent != transform) return; // уже обёрнут
var safe = UIKit.Stretch(UIKit.Make("SafeArea", transform));
safe.SetSiblingIndex(rt.GetSiblingIndex());
safe.gameObject.AddComponent<SafeArea>().fitInsideParent = true; // окно -- не на весь экран
rt.SetParent(safe, false);
}
void Rebuild()
{
float pos = (scroll != null && !resetScroll) ? scroll.verticalNormalizedPosition : 1f;
+43 -5
View File
@@ -2,13 +2,19 @@ using UnityEngine;
// Подгоняет RectTransform под безопасную зону экрана (Screen.safeArea): вырез камеры, скруглённые углы,
// полоска "домой" на iPhone. Следит за поворотом и сменой разрешения (и в Device Simulator).
// Вешать на растянутый на весь Canvas объект; всё, что внутри, не залезет под вырез.
// Вешать на растянутый объект; всё, что внутри, не залезет под вырез.
// - fitInsideParent = false: объект растянут на весь Canvas (HUD);
// - fitInsideParent = true: родитель не на весь экран (окно между верхней и нижней панелью) --
// зона пересчитывается в координаты родителя, отступ появляется только там, где родитель заходит под вырез.
[RequireComponent(typeof(RectTransform))]
public class SafeArea : MonoBehaviour
{
public bool fitInsideParent;
private RectTransform rect;
private Rect appliedArea;
private Vector2Int appliedScreen;
private Rect appliedParent;
void Awake()
{
@@ -18,7 +24,8 @@ public class SafeArea : MonoBehaviour
void Update()
{
if (Screen.safeArea != appliedArea || Screen.width != appliedScreen.x || Screen.height != appliedScreen.y)
if (Screen.safeArea != appliedArea || Screen.width != appliedScreen.x || Screen.height != appliedScreen.y
|| (fitInsideParent && ParentScreenRect() != appliedParent))
Apply();
}
@@ -29,10 +36,41 @@ public class SafeArea : MonoBehaviour
appliedScreen = new Vector2Int(Screen.width, Screen.height);
if (Screen.width <= 0 || Screen.height <= 0) return;
// Якоря в долях экрана: Canvas в режиме Screen Space Overlay покрывает экран целиком
rect.anchorMin = new Vector2(area.xMin / Screen.width, area.yMin / Screen.height);
rect.anchorMax = new Vector2(area.xMax / Screen.width, area.yMax / Screen.height);
if (!fitInsideParent)
{
// Якоря в долях экрана: Canvas в режиме Screen Space Overlay покрывает экран целиком
rect.anchorMin = new Vector2(area.xMin / Screen.width, area.yMin / Screen.height);
rect.anchorMax = new Vector2(area.xMax / Screen.width, area.yMax / Screen.height);
}
else
{
Rect parent = ParentScreenRect();
appliedParent = parent;
if (parent.width <= 0 || parent.height <= 0) return;
// Пересечение родителя с безопасной зоной -- в долях родителя
float xMin = Mathf.Max(parent.xMin, area.xMin), xMax = Mathf.Min(parent.xMax, area.xMax);
float yMin = Mathf.Max(parent.yMin, area.yMin), yMax = Mathf.Min(parent.yMax, area.yMax);
if (xMax <= xMin || yMax <= yMin) return;
rect.anchorMin = new Vector2((xMin - parent.xMin) / parent.width, (yMin - parent.yMin) / parent.height);
rect.anchorMax = new Vector2((xMax - parent.xMin) / parent.width, (yMax - parent.yMin) / parent.height);
}
rect.offsetMin = Vector2.zero;
rect.offsetMax = Vector2.zero;
}
// Прямоугольник родителя в пикселях экрана
Rect ParentScreenRect()
{
var parent = transform.parent as RectTransform;
if (parent == null) return new Rect(0, 0, Screen.width, Screen.height);
var corners = new Vector3[4];
parent.GetWorldCorners(corners);
Canvas canvas = parent.GetComponentInParent<Canvas>();
Camera cam = canvas != null && canvas.renderMode != RenderMode.ScreenSpaceOverlay ? canvas.worldCamera : null;
Vector2 min = RectTransformUtility.WorldToScreenPoint(cam, corners[0]);
Vector2 max = RectTransformUtility.WorldToScreenPoint(cam, corners[2]);
return Rect.MinMaxRect(min.x, min.y, max.x, max.y);
}
}