VanguardReMaster/client/Assets/Vanguard/Runtime/UI/UiKit.cs

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(SmoothScrollRect));
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);
}
}
}