chip-gameV3/Assets/Scripts/UI/Menu/Rules/RulesController.cs
sega db3a0fd0bb Добавить правила игры и титры с управляемой прокруткой
Сверстать страницы правил и подключить открытие из главного меню. Добавить управление обоими джойстиками, выход по бездействию через 60 секунд и автоматический расчёт высоты титров.
2026-09-18 14:42:41 +03:00

162 lines
5.3 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
public class RulesController : MonoBehaviour, IControlable
{
[FormerlySerializedAs("pages")]
[SerializeField] private List<GameObject> _pages;
[SerializeField] private RectTransform _creditsContent;
[SerializeField] private RectTransform _creditsViewport;
[SerializeField] private TMPro.TMP_Text _sectionLabel;
[SerializeField] private float _timeout = 60f;
[SerializeField] private float _autoScrollSpeed = 35f;
[SerializeField] private float _manualScrollSpeed = 300f;
[SerializeField] private float _autoResumeDelay = 1f;
private Menu _menu;
private int _currentIndex;
private float _lastActivityAt;
private float _lastInputAt;
private float _scrollOffset;
public bool IsOpen { get; private set; }
public bool ControlsEnabled { get; set; }
private void Update()
{
if (!IsOpen)
return;
if (In.AnyButton())
_lastActivityAt = Time.unscaledTime;
if (Time.unscaledTime - _lastActivityAt >= _timeout)
Close();
}
private void OnDisable()
{
if (IsOpen)
Close();
}
public void Open()
{
if (IsOpen || _pages == null || _pages.Count == 0)
return;
_menu = FindFirstObjectByType<Menu>(FindObjectsInactive.Include);
if (_menu == null || !_menu.ControlsEnabled)
return;
_menu.ControlsEnabled = false;
_lastActivityAt = Time.unscaledTime;
_lastInputAt = _lastActivityAt;
_currentIndex = 0;
IsOpen = true;
ControlsEnabled = true;
gameObject.SetActive(true);
transform.SetAsLastSibling();
OpenPage(_currentIndex);
ControlsPerformer.Instance.SetControlable(this);
}
public void Close()
{
if (!IsOpen)
return;
IsOpen = false;
ControlsEnabled = false;
CloseAllPages();
gameObject.SetActive(false);
if (_menu != null)
{
_menu.ControlsEnabled = true;
if (ControlsPerformer.Instance != null)
ControlsPerformer.Instance.SetControlable(_menu);
}
}
public void UpdateControls()
{
if (In.GetAction(JoystickSide.Both, InteractMode.Discrete))
{
Close();
return;
}
SwitchPage(In.GetHorizontalAxis(JoystickSide.Both, InteractMode.Discrete));
if (_currentIndex != _pages.Count - 1 || _creditsContent == null)
return;
int direction = In.GetVerticalAxis(JoystickSide.Both, InteractMode.Continious);
if (In.AnyButton())
_lastInputAt = Time.unscaledTime;
float speed = direction != 0 ? -direction * _manualScrollSpeed
: Time.unscaledTime - _lastInputAt >= _autoResumeDelay ? _autoScrollSpeed : 0f;
float limit = Mathf.Max(0f, _creditsContent.rect.height - _creditsViewport.rect.height);
if (direction == 0 && speed > 0f && _scrollOffset >= limit)
_scrollOffset = 0f;
_scrollOffset = Mathf.Clamp(_scrollOffset + speed * Time.unscaledDeltaTime, 0f, limit);
_creditsContent.anchoredPosition = Vector2.up * _scrollOffset;
}
public void SwitchPage(int direction)
{
if (!IsOpen || direction == 0)
return;
_lastActivityAt = Time.unscaledTime;
_currentIndex = (_currentIndex + (direction > 0 ? 1 : -1) + _pages.Count) % _pages.Count;
OpenPage(_currentIndex);
}
private void OpenPage(int index)
{
CloseAllPages();
_pages[index].SetActive(true);
if (_sectionLabel != null)
_sectionLabel.text = index == _pages.Count - 1 ? "КОНЕЦ ИГРЫ" : "ПРАВИЛА";
_scrollOffset = 0f;
_lastInputAt = Time.unscaledTime;
if (_creditsContent != null)
_creditsContent.anchoredPosition = Vector2.zero;
if (index == _pages.Count - 1)
RefreshCreditsLayout();
}
private void RefreshCreditsLayout()
{
if (_creditsContent == null || _creditsViewport == null)
return;
Canvas.ForceUpdateCanvases();
float bottom = 0f;
Vector3[] corners = new Vector3[4];
foreach (TMPro.TMP_Text text in _creditsContent.GetComponentsInChildren<TMPro.TMP_Text>())
{
RectTransform rect = text.rectTransform;
float height = text.GetPreferredValues(text.text, rect.rect.width, Mathf.Infinity).y;
rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height);
text.ForceMeshUpdate();
rect.GetWorldCorners(corners);
for (int i = 0; i < corners.Length; i++)
bottom = Mathf.Max(bottom, _creditsContent.rect.yMax
- _creditsContent.InverseTransformPoint(corners[i]).y);
}
// Нижний отступ даёт последним строкам пройти через всю видимую область до повтора.
_creditsContent.SetSizeWithCurrentAnchors(
RectTransform.Axis.Vertical, bottom + _creditsViewport.rect.height);
}
private void CloseAllPages()
{
foreach (GameObject page in _pages)
page.SetActive(false);
}
}