455 lines
16 KiB
C#
455 lines
16 KiB
C#
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.InputSystem.UI;
|
|
using UnityEngine.UI;
|
|
|
|
namespace Vanguard.UI
|
|
{
|
|
/// <summary>
|
|
/// Минимальная фабрика uGUI. Экраны собираются кодом, чтобы каркас
|
|
/// запускался без префабов и ручной сцены; позже экраны можно перевести
|
|
/// на prefab/UI Toolkit без изменения сервисов.
|
|
/// </summary>
|
|
public static class UiKit
|
|
{
|
|
private static Font _font;
|
|
|
|
public static Font DefaultFont
|
|
{
|
|
get
|
|
{
|
|
if (_font != null)
|
|
return _font;
|
|
|
|
_font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
|
|
if (_font == null)
|
|
_font = Resources.GetBuiltinResource<Font>("Arial.ttf");
|
|
|
|
return _font;
|
|
}
|
|
}
|
|
|
|
public static Canvas CreateCanvas(string name, bool persistent = false)
|
|
{
|
|
EnsureEventSystem();
|
|
|
|
var go = new GameObject(name, typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
|
|
if (Application.isPlaying && persistent)
|
|
Object.DontDestroyOnLoad(go);
|
|
|
|
var canvas = go.GetComponent<Canvas>();
|
|
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
|
canvas.sortingOrder = 0;
|
|
|
|
var scaler = go.GetComponent<CanvasScaler>();
|
|
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
|
scaler.referenceResolution = new Vector2(1920f, 1080f);
|
|
scaler.matchWidthOrHeight = 0.5f;
|
|
|
|
return canvas;
|
|
}
|
|
|
|
public static void EnsureEventSystem()
|
|
{
|
|
if (Object.FindAnyObjectByType<EventSystem>() != null)
|
|
return;
|
|
|
|
var go = new GameObject("EventSystem", typeof(EventSystem));
|
|
if (Application.isPlaying)
|
|
Object.DontDestroyOnLoad(go);
|
|
|
|
var module = go.AddComponent<InputSystemUIInputModule>();
|
|
module.AssignDefaultActions();
|
|
}
|
|
|
|
public static RectTransform CreatePanel(Transform parent, string name, Color color)
|
|
{
|
|
var go = new GameObject(name, typeof(RectTransform), typeof(Image));
|
|
var rect = (RectTransform)go.transform;
|
|
rect.SetParent(parent, false);
|
|
Stretch(rect);
|
|
|
|
var image = go.GetComponent<Image>();
|
|
image.color = color;
|
|
image.raycastTarget = true;
|
|
return rect;
|
|
}
|
|
|
|
public static RectTransform CreateCenteredCard(
|
|
Transform parent,
|
|
string name,
|
|
Vector2 size,
|
|
Color color)
|
|
{
|
|
var rect = CreatePanel(parent, name, color);
|
|
rect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
rect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
rect.pivot = new Vector2(0.5f, 0.5f);
|
|
rect.anchoredPosition = Vector2.zero;
|
|
rect.sizeDelta = size;
|
|
return rect;
|
|
}
|
|
|
|
public static VerticalLayoutGroup AddVerticalLayout(
|
|
RectTransform rect,
|
|
int padding,
|
|
int spacing)
|
|
{
|
|
var layout = rect.gameObject.AddComponent<VerticalLayoutGroup>();
|
|
layout.padding = new RectOffset(padding, padding, padding, padding);
|
|
layout.spacing = spacing;
|
|
layout.childAlignment = TextAnchor.UpperCenter;
|
|
layout.childControlWidth = true;
|
|
layout.childControlHeight = true;
|
|
layout.childForceExpandWidth = true;
|
|
layout.childForceExpandHeight = false;
|
|
return layout;
|
|
}
|
|
|
|
public static HorizontalLayoutGroup AddHorizontalLayout(
|
|
RectTransform rect,
|
|
int padding,
|
|
int spacing)
|
|
{
|
|
var layout = rect.gameObject.AddComponent<HorizontalLayoutGroup>();
|
|
layout.padding = new RectOffset(padding, padding, padding, padding);
|
|
layout.spacing = spacing;
|
|
layout.childAlignment = TextAnchor.MiddleCenter;
|
|
layout.childControlWidth = true;
|
|
layout.childControlHeight = true;
|
|
layout.childForceExpandWidth = true;
|
|
layout.childForceExpandHeight = false;
|
|
return layout;
|
|
}
|
|
|
|
public static Text CreateText(
|
|
Transform parent,
|
|
string name,
|
|
string content,
|
|
int fontSize,
|
|
Color color,
|
|
TextAnchor alignment = TextAnchor.MiddleCenter)
|
|
{
|
|
var go = new GameObject(name, typeof(RectTransform), typeof(Text));
|
|
var rect = (RectTransform)go.transform;
|
|
rect.SetParent(parent, false);
|
|
|
|
var text = go.GetComponent<Text>();
|
|
text.font = DefaultFont;
|
|
text.text = content;
|
|
text.fontSize = fontSize;
|
|
text.color = color;
|
|
text.alignment = alignment;
|
|
text.horizontalOverflow = HorizontalWrapMode.Wrap;
|
|
text.verticalOverflow = VerticalWrapMode.Overflow;
|
|
text.raycastTarget = false;
|
|
text.supportRichText = true;
|
|
text.gameObject.AddComponent<LocalizedText>().Initialize(content);
|
|
|
|
return text;
|
|
}
|
|
|
|
public static void MakeLiteral(Text text)
|
|
{
|
|
if (text == null)
|
|
return;
|
|
|
|
var localized = text.GetComponent<LocalizedText>();
|
|
if (localized != null)
|
|
localized.SetLiteral(text.text);
|
|
}
|
|
|
|
public static Button CreateButton(
|
|
Transform parent,
|
|
string name,
|
|
string label,
|
|
UnityEngine.Events.UnityAction onClick,
|
|
int fontSize = 24)
|
|
{
|
|
var go = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(Button));
|
|
var rect = (RectTransform)go.transform;
|
|
rect.SetParent(parent, false);
|
|
|
|
var image = go.GetComponent<Image>();
|
|
image.color = new Color(0.20f, 0.42f, 0.78f, 1f);
|
|
image.raycastTarget = true;
|
|
|
|
var button = go.GetComponent<Button>();
|
|
button.targetGraphic = image;
|
|
if (onClick != null)
|
|
button.onClick.AddListener(onClick);
|
|
|
|
var text = CreateText(rect, "Label", label, fontSize, Color.white);
|
|
Stretch(text.rectTransform);
|
|
|
|
return button;
|
|
}
|
|
|
|
public static Button CreateLanguageButton(Transform parent, string name = "Language")
|
|
{
|
|
Button button = null;
|
|
button = CreateButton(
|
|
parent,
|
|
name,
|
|
Loc.IsEnglish ? "RU" : "EN",
|
|
() =>
|
|
{
|
|
Loc.Toggle();
|
|
var label = button != null ? button.GetComponentInChildren<Text>() : null;
|
|
if (label != null)
|
|
label.text = Loc.IsEnglish ? "RU" : "EN";
|
|
},
|
|
17);
|
|
|
|
var text = button.GetComponentInChildren<Text>();
|
|
if (text != null)
|
|
MakeLiteral(text);
|
|
|
|
return button;
|
|
}
|
|
|
|
public static InputField CreateInput(
|
|
Transform parent,
|
|
string name,
|
|
string placeholder,
|
|
bool password = false)
|
|
{
|
|
var go = new GameObject(
|
|
name,
|
|
typeof(RectTransform),
|
|
typeof(Image),
|
|
typeof(InputField));
|
|
var rect = (RectTransform)go.transform;
|
|
rect.SetParent(parent, false);
|
|
|
|
var background = go.GetComponent<Image>();
|
|
background.color = new Color(0.10f, 0.12f, 0.16f, 1f);
|
|
background.raycastTarget = true;
|
|
|
|
var input = go.GetComponent<InputField>();
|
|
input.targetGraphic = background;
|
|
input.lineType = InputField.LineType.SingleLine;
|
|
input.contentType = password
|
|
? InputField.ContentType.Password
|
|
: InputField.ContentType.Standard;
|
|
input.characterLimit = password ? 128 : 64;
|
|
input.caretColor = Color.white;
|
|
input.customCaretColor = true;
|
|
input.selectionColor = new Color(0.20f, 0.42f, 0.78f, 0.6f);
|
|
|
|
var text = CreateText(rect, "Text", string.Empty, 22, Color.white, TextAnchor.MiddleLeft);
|
|
MakeLiteral(text);
|
|
Stretch(text.rectTransform);
|
|
var textRect = text.rectTransform;
|
|
textRect.offsetMin = new Vector2(16f, 0f);
|
|
textRect.offsetMax = new Vector2(-16f, 0f);
|
|
text.supportRichText = false;
|
|
|
|
var hint = CreateText(rect, "Placeholder", placeholder, 22, new Color(1f, 1f, 1f, 0.45f), TextAnchor.MiddleLeft);
|
|
Stretch(hint.rectTransform);
|
|
hint.rectTransform.offsetMin = new Vector2(16f, 0f);
|
|
hint.rectTransform.offsetMax = new Vector2(-16f, 0f);
|
|
hint.supportRichText = false;
|
|
|
|
input.textComponent = text;
|
|
input.placeholder = hint;
|
|
|
|
return input;
|
|
}
|
|
|
|
public static LayoutElement SetPreferredHeight(
|
|
Component component,
|
|
float height,
|
|
float flexibleWidth = 1f)
|
|
{
|
|
var element = component.gameObject.GetComponent<LayoutElement>();
|
|
if (element == null)
|
|
element = component.gameObject.AddComponent<LayoutElement>();
|
|
|
|
element.preferredHeight = height;
|
|
element.flexibleWidth = flexibleWidth;
|
|
return element;
|
|
}
|
|
|
|
public static RectTransform CreateScrollView(
|
|
Transform parent,
|
|
string name,
|
|
out RectTransform content,
|
|
float spacing = 8f)
|
|
{
|
|
var root = CreateScrollRoot(parent, name);
|
|
var viewport = CreateViewport(root);
|
|
content = CreateVerticalContent(viewport, spacing);
|
|
ConfigureScroll(root, viewport, content, horizontal: false);
|
|
return root;
|
|
}
|
|
|
|
public static RectTransform CreateHorizontalScrollView(
|
|
Transform parent,
|
|
string name,
|
|
out RectTransform content,
|
|
float spacing = 8f)
|
|
{
|
|
var root = CreateScrollRoot(parent, name);
|
|
var viewport = CreateViewport(root);
|
|
|
|
var contentGo = new GameObject(
|
|
"Content",
|
|
typeof(RectTransform),
|
|
typeof(HorizontalLayoutGroup),
|
|
typeof(ContentSizeFitter));
|
|
content = (RectTransform)contentGo.transform;
|
|
content.SetParent(viewport, false);
|
|
content.anchorMin = new Vector2(0f, 0.5f);
|
|
content.anchorMax = new Vector2(0f, 0.5f);
|
|
content.pivot = new Vector2(0f, 0.5f);
|
|
content.anchoredPosition = Vector2.zero;
|
|
content.sizeDelta = Vector2.zero;
|
|
|
|
var layout = contentGo.GetComponent<HorizontalLayoutGroup>();
|
|
layout.padding = new RectOffset(4, 4, 4, 4);
|
|
layout.spacing = spacing;
|
|
layout.childAlignment = TextAnchor.MiddleLeft;
|
|
layout.childControlWidth = true;
|
|
layout.childControlHeight = true;
|
|
layout.childForceExpandWidth = false;
|
|
layout.childForceExpandHeight = false;
|
|
|
|
var fitter = contentGo.GetComponent<ContentSizeFitter>();
|
|
fitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
|
|
fitter.verticalFit = ContentSizeFitter.FitMode.Unconstrained;
|
|
|
|
ConfigureScroll(root, viewport, content, horizontal: true);
|
|
return root;
|
|
}
|
|
|
|
public static RectTransform CreateGridScrollView(
|
|
Transform parent,
|
|
string name,
|
|
Vector2 cellSize,
|
|
Vector2 spacing,
|
|
out RectTransform content)
|
|
{
|
|
var root = CreateScrollRoot(parent, name);
|
|
var viewport = CreateViewport(root);
|
|
|
|
var contentGo = new GameObject(
|
|
"Content",
|
|
typeof(RectTransform),
|
|
typeof(GridLayoutGroup),
|
|
typeof(ContentSizeFitter));
|
|
content = (RectTransform)contentGo.transform;
|
|
content.SetParent(viewport, false);
|
|
content.anchorMin = new Vector2(0f, 1f);
|
|
content.anchorMax = new Vector2(1f, 1f);
|
|
content.pivot = new Vector2(0.5f, 1f);
|
|
content.anchoredPosition = Vector2.zero;
|
|
content.sizeDelta = Vector2.zero;
|
|
|
|
var layout = contentGo.GetComponent<GridLayoutGroup>();
|
|
layout.cellSize = cellSize;
|
|
layout.spacing = spacing;
|
|
layout.padding = new RectOffset(4, 4, 4, 4);
|
|
layout.startCorner = GridLayoutGroup.Corner.UpperLeft;
|
|
layout.startAxis = GridLayoutGroup.Axis.Horizontal;
|
|
layout.childAlignment = TextAnchor.UpperLeft;
|
|
layout.constraint = GridLayoutGroup.Constraint.Flexible;
|
|
layout.constraintCount = 1;
|
|
|
|
var fitter = contentGo.GetComponent<ContentSizeFitter>();
|
|
fitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
|
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
|
|
|
ConfigureScroll(root, viewport, content, horizontal: false);
|
|
return root;
|
|
}
|
|
|
|
private static RectTransform CreateScrollRoot(Transform parent, string name)
|
|
{
|
|
var root = new GameObject(
|
|
name,
|
|
typeof(RectTransform),
|
|
typeof(Image),
|
|
typeof(ScrollRect));
|
|
var rect = (RectTransform)root.transform;
|
|
rect.SetParent(parent, false);
|
|
Stretch(rect);
|
|
|
|
var image = root.GetComponent<Image>();
|
|
image.color = new Color(0f, 0f, 0f, 0.01f);
|
|
image.raycastTarget = true;
|
|
return rect;
|
|
}
|
|
|
|
private static RectTransform CreateViewport(RectTransform root)
|
|
{
|
|
var go = new GameObject("Viewport", typeof(RectTransform), typeof(RectMask2D));
|
|
var viewport = (RectTransform)go.transform;
|
|
viewport.SetParent(root, false);
|
|
Stretch(viewport);
|
|
return viewport;
|
|
}
|
|
|
|
private static RectTransform CreateVerticalContent(RectTransform viewport, float spacing)
|
|
{
|
|
var go = new GameObject(
|
|
"Content",
|
|
typeof(RectTransform),
|
|
typeof(VerticalLayoutGroup),
|
|
typeof(ContentSizeFitter));
|
|
var content = (RectTransform)go.transform;
|
|
content.SetParent(viewport, false);
|
|
content.anchorMin = new Vector2(0f, 1f);
|
|
content.anchorMax = new Vector2(1f, 1f);
|
|
content.pivot = new Vector2(0.5f, 1f);
|
|
content.anchoredPosition = Vector2.zero;
|
|
content.sizeDelta = Vector2.zero;
|
|
|
|
var layout = go.GetComponent<VerticalLayoutGroup>();
|
|
layout.padding = new RectOffset(4, 4, 4, 4);
|
|
layout.spacing = spacing;
|
|
layout.childAlignment = TextAnchor.UpperCenter;
|
|
layout.childControlWidth = true;
|
|
layout.childControlHeight = true;
|
|
layout.childForceExpandWidth = true;
|
|
layout.childForceExpandHeight = false;
|
|
|
|
var fitter = go.GetComponent<ContentSizeFitter>();
|
|
fitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
|
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
|
return content;
|
|
}
|
|
|
|
private static void ConfigureScroll(
|
|
RectTransform root,
|
|
RectTransform viewport,
|
|
RectTransform content,
|
|
bool horizontal)
|
|
{
|
|
var scroll = root.GetComponent<ScrollRect>();
|
|
scroll.viewport = viewport;
|
|
scroll.content = content;
|
|
scroll.horizontal = horizontal;
|
|
scroll.vertical = !horizontal;
|
|
scroll.movementType = ScrollRect.MovementType.Clamped;
|
|
scroll.scrollSensitivity = 30f;
|
|
scroll.inertia = true;
|
|
scroll.decelerationRate = 0.135f;
|
|
}
|
|
|
|
public static void Stretch(RectTransform rect)
|
|
{
|
|
rect.anchorMin = Vector2.zero;
|
|
rect.anchorMax = Vector2.one;
|
|
rect.offsetMin = Vector2.zero;
|
|
rect.offsetMax = Vector2.zero;
|
|
}
|
|
|
|
public static void SetActive(Component component, bool active)
|
|
{
|
|
if (component != null)
|
|
component.gameObject.SetActive(active);
|
|
}
|
|
}
|
|
}
|