На iPhone левый край окон («Войска» и др.) уходил под вырез камеры. BasePanel переносит Content в контейнер SafeArea; фон окна остаётся как был. SafeArea.fitInsideParent: зона пересчитывается в координаты родителя, если он не на весь экран. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
114 lines
4.3 KiB
C#
114 lines
4.3 KiB
C#
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
// Общая основа всех панелей HUD.
|
|
// Каждая панель (Profile, Quests, Shop...) наследуется от BasePanel и реализует Build():
|
|
// "нарисуй своё содержимое в root". Перерисовка происходит сама:
|
|
// - при открытии панели,
|
|
// - когда GameProgress сообщает об изменениях (получил награду, обучил войска и т.д.).
|
|
// Tick() вызывается 2 раза в секунду -- для таймеров, без полной перерисовки.
|
|
public abstract class BasePanel : MonoBehaviour
|
|
{
|
|
protected GameProgress GP { get { return GameProgress.Instance; } }
|
|
|
|
RectTransform content;
|
|
ScrollRect scroll;
|
|
bool needRebuild;
|
|
bool resetScroll;
|
|
float nextTick;
|
|
|
|
// false -- панель НЕ перерисовывается от чужих изменений (нужно для ползунков в Настройках)
|
|
protected virtual bool RebuildOnChange { get { return true; } }
|
|
|
|
protected virtual void OnEnable()
|
|
{
|
|
if (RebuildOnChange) GP.OnChanged += RequestRebuild;
|
|
needRebuild = true;
|
|
resetScroll = true;
|
|
}
|
|
|
|
protected virtual void OnDisable()
|
|
{
|
|
if (GameProgress.Instance != null) GameProgress.Instance.OnChanged -= RequestRebuild;
|
|
}
|
|
|
|
public void RequestRebuild() { needRebuild = true; }
|
|
|
|
void Update()
|
|
{
|
|
if (needRebuild)
|
|
{
|
|
needRebuild = false;
|
|
Rebuild();
|
|
}
|
|
if (Time.unscaledTime >= nextTick)
|
|
{
|
|
nextTick = Time.unscaledTime + 0.5f;
|
|
Tick();
|
|
}
|
|
Frame();
|
|
}
|
|
|
|
RectTransform Content
|
|
{
|
|
get
|
|
{
|
|
if (content == null)
|
|
{
|
|
var t = transform.Find("Content");
|
|
if (t == null)
|
|
{
|
|
// запасной вариант для старых панелей без Content
|
|
var body = transform.Find("Body");
|
|
if (body != null) body.gameObject.SetActive(false);
|
|
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;
|
|
resetScroll = false;
|
|
scroll = null;
|
|
UIKit.Clear(Content);
|
|
Build(Content);
|
|
if (scroll != null)
|
|
{
|
|
Canvas.ForceUpdateCanvases();
|
|
scroll.verticalNormalizedPosition = pos;
|
|
}
|
|
}
|
|
|
|
// Прокручиваемый список на всю панель (позиция прокрутки сохраняется между перерисовками)
|
|
protected RectTransform ScrollList(Transform parent, float spacing = 14)
|
|
{
|
|
return UIKit.ScrollList(parent, out scroll, spacing);
|
|
}
|
|
|
|
protected abstract void Build(RectTransform root);
|
|
protected virtual void Tick() { }
|
|
protected virtual void Frame() { } // каждый кадр -- для плавного движения (армии на карте)
|
|
|
|
// Открыть другую панель (например, из "Боя" перейти в "Войска")
|
|
protected void OpenPanel(string id)
|
|
{
|
|
if (UIPanelManager.Instance != null) UIPanelManager.Instance.Open(id);
|
|
}
|
|
}
|