2026-07-14 11:14:13 +03:00

291 lines
10 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class TextInput : MonoBehaviour
{
[Header("Ключ prefs")]
[SerializeField] private string key;
[SerializeField] private bool loadData;
[SerializeField] private bool saveDataOnClose;
[Header("Настройки генерации поля")]
[Tooltip("Якорь лево-низ")][SerializeField] private GameObject SlotPrefab;
[SerializeField] private RectTransform slotsRoot;
[SerializeField] private int slotsCount = 10;
[SerializeField] private float rootPaddingX = 10;
[SerializeField] private float rootPaddingY = 10;
[SerializeField] private float gapSize = 10;
[Header("Настройки анимации")]
[SerializeField] private float animationSpeed;
[SerializeField] private float fontSizeNormal = 20;
[SerializeField] private float fontSizeHighlighted = 25;
[SerializeField] private Color fontColorNormal;
[SerializeField] private Color fontColorHighlighted;
[SerializeField] private float slotSizeMultiplier = 1.1f;
[SerializeField] private Color slotColorNormal;
[SerializeField] private Color slotColorHighlighted;
[Header("Звуки")]
[SerializeField] private AudioClip switchSlotSound;
[SerializeField] private AudioClip switchLetterSound;
[SerializeField] private AudioClip acceptSound;
[SerializeField] private AudioClip openSound;
private Vector2 slotSizeNormal; //расчет при генерации
[Header("Алфавит")]
[SerializeField] private string alphabetString = "0123456789"; //из него генерирует массив символов
[Header("Скорость переключения букв")]
[SerializeField] private float MaxSpeedLetterPerSecond = 8;
[SerializeField] private float MinSpeedLetterPerSecond = 3;
[SerializeField] private float MaxSpeedTime = 3;
[SerializeField] private float MinSpeedTime = 0.5f;
private float switchButtonPressedTime = 0;
private float letterSwitchSpeed = 0;
private float switchDelayRest = 1;
//тут массивы для доступа
private string[] alphabet; // не забыь добавить пустую строку при генерации (пробел лучше)
private RectTransform[] slotRects;
private Graphic[] slotGraphics;
private TextMeshProUGUI[] slotLabels;
private Coroutine animationCoroutine;
private int currentSlotIndex = 0;
private int currentLetterindex = 0;
private TextMeshProUGUI currentLabel => slotLabels[currentSlotIndex];
public void Initialize()
{
Generate();
LoadSavedData();
SoundManager.instance.playClip(openSound);
animationCoroutine = StartCoroutine(SlotsAnimation());
}
public string AcceptAndClose()
{
string result = GetText();
if (!string.IsNullOrEmpty(key) && saveDataOnClose)
{
PlayerPrefs.SetString(key, result);
SettingsManager.OnSettingsChanged?.Invoke();
}
StopCoroutine(animationCoroutine);
SoundManager.instance.playClip(acceptSound);
Clear();
return result;
}
private void Generate()
{
//Считаем размер одного слота
Vector2 spaceForSlotsWithGaps = slotsRoot.sizeDelta
- Vector2.right * rootPaddingX * 2
- Vector2.up * rootPaddingY * 2;
int gapsCount = slotsCount - 1;
float horizontalSpaceForSlots = spaceForSlotsWithGaps.x - gapsCount * gapSize;
float slotWidth = horizontalSpaceForSlots / slotsCount;
slotSizeNormal = new Vector2(slotWidth,
spaceForSlotsWithGaps.y);
//Расставляем слоты в root
//у префаба будет якорь лево/низ
Vector2 startLocalPoint = new Vector2(rootPaddingX, rootPaddingY);
slotRects = new RectTransform[slotsCount];
slotGraphics = new Graphic[slotsCount];
slotLabels = new TextMeshProUGUI[slotsCount];
Vector2 slotOffset = (slotSizeNormal.x + gapSize) * Vector2.right;//на сколько сдвигать каждый след. слот
for (int i = 0; i < slotsCount; i++)
{
//Создаем слот
GameObject slot = Instantiate(SlotPrefab, slotsRoot);
//филлим массивы ссылок
slotRects[i] = slot.GetComponent<RectTransform>();
slotGraphics[i] = slot.GetComponent<Graphic>();
slotLabels[i] = slot.transform.GetComponentInChildren<TextMeshProUGUI>();
//Выставляем размеры, координаты, цвета, шрифты
slotRects[i].sizeDelta = slotSizeNormal;
slotRects[i].anchoredPosition = startLocalPoint + slotOffset * i + slotSizeNormal / 2;
slotGraphics[i].color = slotColorNormal;
slotLabels[i].color = fontColorNormal;
slotLabels[i].text = "";
slotLabels[i].fontSize = fontSizeNormal;
}
//Собираем алфавит
alphabet = new string[alphabetString.Length + 1];//+1 для пробела
for (int i = 0; i < alphabetString.Length; i++)
{
alphabet[i] = alphabetString[i].ToString();
}
alphabet[^1] = " ";
//Настраиваем индексы
currentSlotIndex = 0;
}
private void LoadSavedData()
{
string data = "";
if (loadData)
data = PlayerPrefs.GetString(key, "");
// Очищаем все слоты
for (int i = 0; i < slotsCount; i++)
{
slotLabels[i].text = " ";
}
// Заполняем сохраненными данными
if (!string.IsNullOrEmpty(data))
{
int minLength = Mathf.Min(data.Length, slotsCount);
for (int i = 0; i < minLength; i++)
{
slotLabels[i].text = data[i].ToString();
}
}
}
public void SwitchLetter(int continiousDirection)
{
continiousDirection = Mathf.Clamp(continiousDirection, -1, 1);
if (continiousDirection == 0)//сброс переключения
{
switchDelayRest = 0;
switchButtonPressedTime = 0;
return;
}
if (switchDelayRest < 0)
{
//переключение
currentLetterindex = (currentLetterindex + continiousDirection + alphabet.Length) % alphabet.Length;
currentLabel.text = alphabet[currentLetterindex];
recalculateRest();
Debug.Log($"Переключение остаток: {switchDelayRest}");
}
switchDelayRest -= Time.deltaTime;
switchButtonPressedTime += Time.deltaTime;
void recalculateRest()
{
float k = (MaxSpeedLetterPerSecond - MinSpeedLetterPerSecond)
/ (MaxSpeedTime - MinSpeedTime);
float b = MinSpeedLetterPerSecond - k * MinSpeedTime;
letterSwitchSpeed = k * switchButtonPressedTime + b;
letterSwitchSpeed = Mathf.Clamp(letterSwitchSpeed,
MinSpeedLetterPerSecond,
MaxSpeedLetterPerSecond);
switchDelayRest = 1 / letterSwitchSpeed;
}
}
public void SwitchSlot(int discreteDirection)
{
if (discreteDirection == 0) return;
currentSlotIndex = (currentSlotIndex + discreteDirection + slotsCount) % slotsCount;
string letter = currentLabel.text;
currentLetterindex = Array.IndexOf(alphabet, letter);
}
public void Clear()
{
foreach (RectTransform rect in slotRects)
{
Destroy(rect.gameObject);
}
}
public string GetText()
{
System.Text.StringBuilder sb = new();
foreach (TextMeshProUGUI label in slotLabels)
{
// Если текст пустой строка, считаем это пробелом
if (!string.IsNullOrEmpty(label.text))
{
sb.Append(label.text);
}
}
return sb.ToString().Trim();
}
IEnumerator SlotsAnimation()
{
float scaleSpeed = (slotSizeMultiplier - 1) / animationSpeed;
float slotColorSpeed = Vector4.Distance(slotColorNormal, slotColorHighlighted) / animationSpeed;
float fontColorSpeed = Vector4.Distance(fontColorNormal, fontColorHighlighted) / animationSpeed;
float fontSizeSpeed = (fontSizeHighlighted - fontSizeNormal) / animationSpeed;
while (true)
{
for (int i = 0; i < slotsCount; i++)
{
if (i == currentSlotIndex) //подсвечиваем
{
slotRects[i].localScale = Vector3.MoveTowards(slotRects[i].localScale, Vector3.one * slotSizeMultiplier, scaleSpeed);
slotGraphics[i].color = Vector4.MoveTowards(slotGraphics[i].color, slotColorHighlighted, slotColorSpeed);
slotLabels[i].color = Vector4.MoveTowards(slotLabels[i].color, fontColorHighlighted, fontColorSpeed);
slotLabels[i].fontSize = Mathf.MoveTowards(slotLabels[i].fontSize, fontSizeHighlighted, fontSizeSpeed);
}
else //убираем подсветку
{
slotRects[i].localScale = Vector3.MoveTowards(slotRects[i].localScale, Vector3.one, scaleSpeed);
slotGraphics[i].color = Vector4.MoveTowards(slotGraphics[i].color, slotColorNormal, slotColorSpeed);
slotLabels[i].color = Vector4.MoveTowards(slotLabels[i].color, fontColorNormal, fontColorSpeed);
slotLabels[i].fontSize = Mathf.MoveTowards(slotLabels[i].fontSize, fontSizeNormal, fontSizeSpeed);
}
}
yield return null;
}
}
}