200 lines
9.4 KiB
C#
200 lines
9.4 KiB
C#
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.InputSystem;
|
|
using UnityEngine.UI;
|
|
|
|
namespace DepthsOfGreed.HandbookUI
|
|
{
|
|
public sealed class HandbookController : MonoBehaviour
|
|
{
|
|
[SerializeField] private GameObject closedBook;
|
|
[SerializeField] private GameObject openBook;
|
|
[SerializeField] private Button bookButton;
|
|
[SerializeField] private Button closeButton;
|
|
[SerializeField] private RectTransform closedTabsRoot;
|
|
[SerializeField] private RectTransform openTabsRoot;
|
|
[SerializeField] private Button[] closedTabs;
|
|
[SerializeField] private Button[] openTabs;
|
|
[SerializeField] private GameObject[] pages;
|
|
[SerializeField] private GameObject testTowerPrefab;
|
|
[SerializeField] private HandbookTowerCatalog towerCatalog;
|
|
[Tooltip("Wartezeit nach erfolgreichem Platzieren, bevor das Buch hochfährt.")]
|
|
[SerializeField, Min(0)] private float successfulPlacementDelay = 0.5f;
|
|
[SerializeField] private float hoverDistance = 115f;
|
|
[SerializeField] private float animationSpeed = 14f;
|
|
|
|
private readonly string[] titles = { "Bauen", "Statistik", "Upgrades", "Index" };
|
|
private Vector2[] closedPositions, openPositions;
|
|
private TMP_Text[] closedLabels, openLabels;
|
|
private RectTransform activeLayer;
|
|
private int selected, hoveredClosed = -1, hoveredOpen = -1;
|
|
private bool isOpen, bookHovered;
|
|
private Vector3 bookScale;
|
|
private BuildMenuController buildMenu;
|
|
private TestPlacementController placement;
|
|
private HandbookIndexController indexMenu;
|
|
|
|
private void Awake()
|
|
{
|
|
if (!closedBook || !openBook || !bookButton || !closeButton ||
|
|
!openTabsRoot || !closedTabsRoot || closedTabs.Length != 4 ||
|
|
openTabs.Length != 4 || pages.Length != 4)
|
|
{
|
|
Debug.LogError("Handbuch: Inspector-Verbindungen fehlen.", this);
|
|
enabled = false;
|
|
return;
|
|
}
|
|
closedPositions = PrepareTabs(closedTabs, out closedLabels, false);
|
|
openPositions = PrepareTabs(openTabs, out openLabels, true);
|
|
bookScale = bookButton.transform.localScale;
|
|
var layer = new GameObject("ActiveTabLayer", typeof(RectTransform));
|
|
activeLayer = layer.GetComponent<RectTransform>();
|
|
activeLayer.SetParent(openTabsRoot.parent, false);
|
|
activeLayer.anchorMin = openTabsRoot.anchorMin;
|
|
activeLayer.anchorMax = openTabsRoot.anchorMax;
|
|
activeLayer.pivot = openTabsRoot.pivot;
|
|
activeLayer.offsetMin = openTabsRoot.offsetMin;
|
|
activeLayer.offsetMax = openTabsRoot.offsetMax;
|
|
var background = openBook.transform.Find("Background");
|
|
if (background) activeLayer.SetSiblingIndex(background.GetSiblingIndex() + 1);
|
|
bookButton.onClick.AddListener(() => Open(0));
|
|
closeButton.onClick.AddListener(Close);
|
|
AddHover(bookButton.gameObject, () => bookHovered = true, () => bookHovered = false);
|
|
buildMenu = pages[0].AddComponent<BuildMenuController>();
|
|
buildMenu.Initialize(towerCatalog);
|
|
placement = gameObject.AddComponent<TestPlacementController>();
|
|
placement.Initialize(this, buildMenu, (RectTransform)openBook.transform, testTowerPrefab);
|
|
placement.SuccessfulPlacementDelay = successfulPlacementDelay;
|
|
buildMenu.Placement = placement;
|
|
indexMenu = pages[3].AddComponent<HandbookIndexController>();
|
|
indexMenu.Initialize(towerCatalog);
|
|
Close();
|
|
}
|
|
|
|
private Vector2[] PrepareTabs(Button[] tabs, out TMP_Text[] labels, bool opened)
|
|
{
|
|
var positions = new Vector2[tabs.Length];
|
|
labels = new TMP_Text[tabs.Length];
|
|
for (int i = 0; i < tabs.Length; i++)
|
|
{
|
|
int index = i;
|
|
if (!opened)
|
|
{
|
|
var marker = (RectTransform)tabs[i].transform;
|
|
marker.sizeDelta = new Vector2(marker.sizeDelta.x + 25f, marker.sizeDelta.y);
|
|
marker.anchoredPosition += Vector2.right * 25f;
|
|
}
|
|
positions[i] = ((RectTransform)tabs[i].transform).anchoredPosition;
|
|
labels[i] = tabs[i].GetComponentInChildren<TMP_Text>(true);
|
|
if (labels[i])
|
|
{
|
|
var text = labels[i];
|
|
text.text = titles[i];
|
|
text.raycastTarget = false;
|
|
text.fontSize = opened ? 20 : 18;
|
|
text.enableAutoSizing = true;
|
|
text.fontSizeMin = 14;
|
|
text.fontSizeMax = opened ? 20 : 18;
|
|
text.alignment = TextAlignmentOptions.MidlineLeft;
|
|
var rect = text.rectTransform;
|
|
rect.anchorMin = Vector2.zero;
|
|
rect.anchorMax = Vector2.one;
|
|
rect.offsetMin = new Vector2(opened ? 65 : 48, 4);
|
|
rect.offsetMax = new Vector2(-6, -4);
|
|
text.gameObject.SetActive(false);
|
|
}
|
|
tabs[i].onClick.AddListener(() => Open(index));
|
|
AddHover(tabs[i].gameObject,
|
|
() => { if (opened) hoveredOpen = index; else hoveredClosed = index; },
|
|
() => { if (opened) { if (hoveredOpen == index) hoveredOpen = -1; }
|
|
else if (hoveredClosed == index) hoveredClosed = -1; });
|
|
}
|
|
return positions;
|
|
}
|
|
|
|
private static void AddHover(GameObject target, UnityEngine.Events.UnityAction enter,
|
|
UnityEngine.Events.UnityAction exit)
|
|
{
|
|
var trigger = target.GetComponent<EventTrigger>();
|
|
if (!trigger) trigger = target.AddComponent<EventTrigger>();
|
|
var entry = new EventTrigger.Entry { eventID = EventTriggerType.PointerEnter };
|
|
entry.callback.AddListener(_ => enter());
|
|
trigger.triggers.Add(entry);
|
|
var leave = new EventTrigger.Entry { eventID = EventTriggerType.PointerExit };
|
|
leave.callback.AddListener(_ => exit());
|
|
trigger.triggers.Add(leave);
|
|
}
|
|
|
|
public void Open(int index)
|
|
{
|
|
if (index < 0 || index >= pages.Length) return;
|
|
bool samePage = isOpen && selected == index;
|
|
selected = index;
|
|
isOpen = true;
|
|
hoveredClosed = -1;
|
|
bookHovered = false;
|
|
closedBook.SetActive(false);
|
|
openBook.SetActive(true);
|
|
for (int i = 0; i < pages.Length; i++)
|
|
{
|
|
pages[i].SetActive(i == index);
|
|
var rect = (RectTransform)openTabs[i].transform;
|
|
rect.SetParent(i == selected ? activeLayer : openTabsRoot, false);
|
|
}
|
|
if (index == 3 && !samePage && indexMenu) indexMenu.ResetForEntry();
|
|
if (samePage)
|
|
foreach (var scroll in pages[index].GetComponentsInChildren<ScrollRect>(true))
|
|
scroll.verticalNormalizedPosition = 1f;
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
if (placement && placement.IsDragging) placement.Cancel();
|
|
if (placement) placement.ResetBookPresentation();
|
|
if (buildMenu) buildMenu.ResetForClose();
|
|
if (indexMenu) indexMenu.ClearSelection();
|
|
isOpen = false;
|
|
hoveredOpen = hoveredClosed = -1;
|
|
bookHovered = false;
|
|
for (int i = 0; i < openTabs.Length; i++)
|
|
{
|
|
((RectTransform)openTabs[i].transform).anchoredPosition = openPositions[i];
|
|
if (openLabels[i]) openLabels[i].gameObject.SetActive(false);
|
|
}
|
|
openBook.SetActive(false);
|
|
closedBook.SetActive(true);
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (isOpen && (!placement || !placement.ConsumedCancelThisFrame) &&
|
|
(!placement || !placement.IsDragging) && Keyboard.current != null && Keyboard.current.escapeKey.wasPressedThisFrame)
|
|
Close();
|
|
float blend = 1f - Mathf.Exp(-animationSpeed * Time.unscaledDeltaTime);
|
|
if (!isOpen)
|
|
{
|
|
bookButton.transform.localScale = Vector3.Lerp(bookButton.transform.localScale,
|
|
bookScale * (bookHovered ? 1.04f : 1f), blend);
|
|
AnimateTabs(closedTabs, closedPositions, closedLabels, hoveredClosed, -1, blend);
|
|
}
|
|
else AnimateTabs(openTabs, openPositions, openLabels, hoveredOpen, selected, blend);
|
|
}
|
|
|
|
private void AnimateTabs(Button[] tabs, Vector2[] positions, TMP_Text[] labels,
|
|
int hovered, int active, float blend)
|
|
{
|
|
for (int i = 0; i < tabs.Length; i++)
|
|
{
|
|
bool show = i == hovered || i == active;
|
|
var rect = (RectTransform)tabs[i].transform;
|
|
// Reveal the full marker while retaining a small overlap with the page.
|
|
float distance = Mathf.Clamp(hoverDistance, 0f, Mathf.Max(0f, positions[i].x - 10f));
|
|
var target = positions[i] + (show ? Vector2.left * distance : Vector2.zero);
|
|
rect.anchoredPosition = Vector2.Lerp(rect.anchoredPosition, target, blend);
|
|
if (labels[i]) labels[i].gameObject.SetActive(show);
|
|
}
|
|
}
|
|
}
|
|
}
|