chip-game/Assets/Scripts/Settings/SettingsScreen.cs
2026-01-12 09:36:17 +03:00

458 lines
14 KiB
C#

using System.Collections.Generic;
using System.Data;
using System.Linq;
using TMPro;
using UnityEngine;
public class SettingsScreen : MonoBehaviour
{
[SerializeField] RectTransform pointer;
[SerializeField] List<SettingAction> settingActions;
private List<Setting> settings;
[SerializeField] private List<string> excludedIds;
Dictionary<string, Group> groups;
List<string> groupsOrder;
List<SettingItem> actionItems;
int currentGroupIndex = 0;
int currentItemIndex = 0;
SettingItem activeSettingItem;
public ViewPrefabsProvider viewProvider;
[Header("Настройки генерации")]
[SerializeField] private RectTransform SettingsContent;
[SerializeField] private Vector2 GeneralOffset;
[SerializeField] private GameObject GroupHeaderPrefab;
[SerializeField] private float GroupHeaderLeftMargin = 0;
[SerializeField] private GameObject GroupItemPrefab;
[SerializeField] private float GroupItemLeftMargin = 20;
[SerializeField] private Vector2 pointerOffset = new Vector2(-40, -25);
[Header("Описания настроек")]
public Transform DescriptionContainer;
public SettingDescriptionProvider DescriptionProvider;
public HeaderIconsProvider IconsProvider;
private GameObject CurrentDescription;
[Header("Настройки scroll view")]
float minScrollY = 0;
Vector2 scrollVelocity = Vector2.zero;
public AudioClip clip;
bool Generated = false;
private void OnEnable()
{
Generate();
SetupActiveItem();
}
private void Update()
{
if (!Generated) return;
Controls();
Vector2 targetContentPosition = new Vector2(0, ContentYPos(activeSettingItem.ItemRect.anchoredPosition.y));
SettingsContent.anchoredPosition = Vector2.SmoothDamp(current: SettingsContent.anchoredPosition,
target: targetContentPosition,
currentVelocity: ref scrollVelocity,
smoothTime: 0.1f);
foreach (SettingItem action in actionItems)
{
action.ActionUpdate();
}
}
private void Controls()
{
int direction = 0;
//if (In.GetButtonDownAnyPlayer(PlayerAction.left)) direction -= 1;
//if (In.GetButtonDownAnyPlayer(PlayerAction.right)) direction += 1;
if (In.GetButtonAnyPlayer(PlayerAction.left)) direction -= 1;
if (In.GetButtonAnyPlayer(PlayerAction.right)) direction += 1;
if (direction != 0)
{
activeSettingItem.Interact(direction);
}
bool changeItem = In.GetButtonDown(ButtonPinMapping.down1);
bool changeGroup = In.GetButtonDown(ButtonPinMapping.down2);
if (changeItem)
{
string currentGroup = groupsOrder[currentGroupIndex];
int ItemsCount = groups[currentGroup].Items.Count;
currentItemIndex = (currentItemIndex + 1) % ItemsCount;
}
if (changeGroup)
{
currentGroupIndex = (currentGroupIndex + 1 + groupsOrder.Count) % groupsOrder.Count;
currentItemIndex = 0;
}
if (changeItem || changeGroup)
{
SetupActiveItem();
SoundManager.instance.playClip(clip);
}
}
private float ContentYPos(float itemY)
{
float baseOffset = -350f;//вынести в поле
float contentPos = baseOffset + Mathf.Abs(itemY);
contentPos = Mathf.Max(contentPos, minScrollY);
return contentPos;
}
void SetupActiveItem()
{
string groupName = groupsOrder[currentGroupIndex];
Group group = groups[groupName];
SettingItem item = group.Items[currentItemIndex];
activeSettingItem = item;
pointer.anchoredPosition = item.ItemRect.anchoredPosition + pointerOffset;
if (CurrentDescription != null) Destroy(CurrentDescription);
CurrentDescription = Instantiate(DescriptionProvider.GetDescription(item.GetID()), DescriptionContainer);
}
public void RefreshViews()
{
foreach (var group in groupsOrder)
{
foreach (SettingItem item in groups[group].Items)
{
item.RefreshView();
}
}
}
private void Generate()
{
if (Generated) return;
settings = SettingsManager.Container.Settings;
groupsOrder = new List<string>();
groups = new Dictionary<string, Group>();
actionItems = new();
viewProvider.Initialize();
List<string> groupNames = GetGroupNames(settings, settingActions);
//создаю группы.
foreach (string groupName in groupNames)
{
List<Setting> groupSettings = GetSettingsByGroup(settings, groupName);
List<SettingAction> groupActions = GetActionsByGroup(settingActions, groupName);
if (AllGroupItemsExcluded(groupSettings, groupActions)) continue;
var group = new Group();
//заполнить Items
foreach (Setting setting in groupSettings)
{
if (IsExcludedId(setting.Id)) continue;
SettingItem item = new SettingItem(setting);
group.Items.Add(item);
}
foreach (SettingAction action in groupActions)
{
if (IsExcludedId(action.Name)) continue;
SettingItem item = new SettingItem(action);
actionItems.Add(item);
group.Items.Add(item);
}
groupsOrder.Add(groupName);
groups.Add(groupName, group);
}
//генерит вьюхи
float NextYPosition = GeneralOffset.y;
foreach (string groupName in groupsOrder)
{
//Заголовок группы
Vector2 anchoredPosition = new Vector2(GeneralOffset.x + GroupHeaderLeftMargin, NextYPosition);
GenerateHeader(prefab: GroupHeaderPrefab,
parent: SettingsContent,
anchoredPosition: anchoredPosition,
text: groupName,
NextYPosition: ref NextYPosition);
//Элементы группы
foreach (var item in groups[groupName].Items)
{
anchoredPosition = new Vector2(GeneralOffset.x + GroupItemLeftMargin, NextYPosition);
//item.SetView(viewProvider.GetViewPrefab(setting.Id));
GenerateItem(prefab: GroupItemPrefab,
parent: SettingsContent,
Item: item,
anchoredPosition: anchoredPosition,
NextYPosition: ref NextYPosition);
}
}
Generated = true;
}
private bool IsExcludedId(string id) => excludedIds.Contains(id);
/// <summary>
/// Определяет есть ли в группе хоть один неисключенный элемент
/// </summary>
/// <param name="settings">Настройки группы</param>
/// <param name="actions">Действия группы</param>
/// <returns></returns>
private bool AllGroupItemsExcluded(List<Setting> settings, List<SettingAction> actions)
{
foreach (var item in settings)
{
if (!excludedIds.Contains(item.Id)) return false;
}
foreach (var item in actions)
{
if (!excludedIds.Contains(item.Name)) return false;
}
return true;
}
private List<string> GetGroupNames(List<Setting> settings, List<SettingAction> actions)
{
// HashSet обеспечивает O(1) для Contains
var uniqueNames = new HashSet<string>();
foreach (var setting in settings)
{
if (!string.IsNullOrEmpty(setting.Group))
uniqueNames.Add(setting.Group);
}
foreach (var action in actions)
{
if (!string.IsNullOrEmpty(action.Group))
uniqueNames.Add(action.Group);
}
return uniqueNames.ToList();
}
private List<Setting> GetSettingsByGroup(List<Setting> settings, string group)
{
return settings
.Where(s => s.Group == group)
.ToList();
}
private List<SettingAction> GetActionsByGroup(List<SettingAction> actions, string group)
{
return actions
.Where(a => a.Group == group)
.ToList();
}
private void GenerateHeader(GameObject prefab, RectTransform parent, Vector2 anchoredPosition, string text, ref float NextYPosition)
{
RectTransform groupHeaderRect = Instantiate(prefab, parent).GetComponent<RectTransform>();
TextMeshProUGUI headerLabel = groupHeaderRect.GetComponent<TextMeshProUGUI>();
groupHeaderRect.anchoredPosition = anchoredPosition;
NextYPosition -= groupHeaderRect.sizeDelta.y;
headerLabel.text = text;
//Иконка
RectTransform iconRect = Instantiate(IconsProvider.GetIcon(text), parent).GetComponent<RectTransform>();
float headerWidth = headerLabel.GetPreferredValues().x;
iconRect.anchoredPosition = anchoredPosition + new Vector2(headerWidth + 10, 0);
}
private void GenerateItem(GameObject prefab, RectTransform parent, SettingItem Item, Vector2 anchoredPosition, ref float NextYPosition)
{
RectTransform groupItemRect = Instantiate(prefab, parent).GetComponent<RectTransform>();
TextMeshProUGUI itemLabel = groupItemRect.GetComponent<TextMeshProUGUI>();
groupItemRect.anchoredPosition = anchoredPosition;
NextYPosition -= groupItemRect.sizeDelta.y;
itemLabel.text = Item.GetDisplayName();
Item.ItemRect = groupItemRect;
Item.SetupView(viewProvider);
}
#region NESTED
class Group
{
public List<SettingItem> Items;
public Group() => Items = new();
}
class SettingItem
{
public SettingItemType Type;
public RectTransform ItemRect; //Отрисованный rect. Для навигации поинтера.
public IView view;
private Setting setting;
private SettingAction action;
float timeToInvoke = 1f;
float elapsedTime = 0f;
public float interactDelay = 0;
public float lastInteract;
public void ActionUpdate()
{
elapsedTime = Mathf.Clamp(elapsedTime - Time.deltaTime, 0, timeToInvoke);
view?.ChangeValue((elapsedTime / timeToInvoke).ToString());
}
public SettingItem(Setting setting)
{
this.setting = setting;
Type = SettingItemType.Setting;
if (setting.Type == SettingType.String) interactDelay = 0.5f;
}
public SettingItem(SettingAction action)
{
this.action = action;
Type = SettingItemType.Action;
//вью сделать
}
public string GetDisplayName()
{
return Type switch
{
SettingItemType.Setting => setting.Alias,
SettingItemType.Action => action.Name,
_ => "<ошибка получения имени SettingItem>"
};
}
public string GetID()
{
return Type switch
{
SettingItemType.Setting => setting.Id,
SettingItemType.Action => action.Name,
_ => "defaultId"
};
}
public void Interact(int direction)
{
if (Type == SettingItemType.Setting)
{
if (Time.time - lastInteract > interactDelay)
{
SettingsManager.ChangeSetting(setting, direction);
view.ChangeValue(setting.Id);
lastInteract = Time.time;
}
}
if (Type == SettingItemType.Action)
{
elapsedTime = Mathf.Clamp(elapsedTime + 2 * Time.deltaTime, 0, timeToInvoke);
if (elapsedTime >= timeToInvoke)
{
elapsedTime = 0f;
action.OnPressed?.Invoke();
view.OnChangeReady?.Invoke();
}
}
}
public void SetupView(ViewPrefabsProvider viewProvider)
{
if (Type == SettingItemType.Setting)
{
GameObject viewPrefab = viewProvider.GetViewPrefab(setting.Id);
RectTransform rect = Instantiate(viewPrefab, ItemRect).GetComponent<RectTransform>();
rect.anchoredPosition = ItemRect.rect.max;//тут мб другое
view = rect.GetComponent<IView>();
if (view == null)
Debug.LogError($"Нет вью на {viewPrefab}");
else
view.Initialize(setting.Id);
}
if (Type == SettingItemType.Action)
{
GameObject viewPrefab = viewProvider.GetViewPrefab("Action");
RectTransform rect = Instantiate(viewPrefab, ItemRect).GetComponent<RectTransform>();
rect.anchoredPosition = ItemRect.rect.max;//тут мб другое
view = rect.GetComponent<IView>();
if (view == null)
Debug.LogError($"Нет вью на {viewPrefab}");
else
view.Initialize("0");
}
}
public void RefreshView()
{
if (view == null)
{
print($"Вью {GetDisplayName()} null");
return;
}
if (Type == SettingItemType.Setting) view.Initialize(setting.Id);
else view.Initialize("0");
}
}
enum SettingItemType { Setting, Action }
#endregion
}