Files
BABA_YAGA/Assets/Scripts/UI/SettingsController.cs

324 lines
12 KiB
C#
Raw Normal View History

2026-04-25 18:20:16 +07:00
using UnityEngine;
using UnityEngine.UIElements;
2026-04-29 01:04:28 +07:00
using UnityEngine.InputSystem;
2026-04-28 00:07:42 +07:00
using PrimeTween;
2026-04-29 01:04:28 +07:00
using System;
using System.Collections.Generic;
2026-04-28 00:07:42 +07:00
using System.Threading.Tasks;
2026-04-29 01:04:28 +07:00
using UnityEngine.SceneManagement;
2026-04-29 02:31:15 +07:00
using OnlyScove.Scripts;
2026-04-25 18:20:16 +07:00
2026-04-28 00:07:42 +07:00
namespace Hallucinate.UI
2026-04-25 18:20:16 +07:00
{
2026-04-28 00:07:42 +07:00
public class SettingsController : BaseUIController
2026-04-25 18:20:16 +07:00
{
2026-04-28 00:07:42 +07:00
private VisualElement _sidebar;
private Label _tabTitle;
private ScrollView _content;
2026-04-29 01:04:28 +07:00
private InputActionAsset _inputActions;
private Dictionary<string, Button> _tabButtons = new Dictionary<string, Button>();
private string _activeTab = "GENERAL";
2026-04-25 18:20:16 +07:00
2026-04-28 00:07:42 +07:00
public override void Initialize(VisualElement uxmlRoot, UIManager manager)
2026-04-25 18:20:16 +07:00
{
2026-04-28 00:07:42 +07:00
base.Initialize(uxmlRoot, manager);
2026-04-25 18:20:16 +07:00
2026-04-28 00:07:42 +07:00
_sidebar = root.Q<VisualElement>("Sidebar");
_tabTitle = root.Q<Label>("TabTitle");
_content = root.Q<ScrollView>("SettingsContent");
2026-04-25 18:20:16 +07:00
2026-04-29 01:04:28 +07:00
_inputActions = uiManager.InputReader?.InputActions;
if (_inputActions == null)
{
_inputActions = GameObject.FindAnyObjectByType<PlayerInput>()?.actions;
}
2026-04-28 18:49:05 +07:00
root.RegisterCallback<PointerDownEvent>(evt => {
2026-04-28 11:35:49 +07:00
if (evt.target == root)
{
uiManager.ToggleSettings();
}
});
2026-04-29 01:04:28 +07:00
SetupTab("GeneralTab", "GENERAL");
SetupTab("VideoTab", "VIDEO");
SetupTab("SoundTab", "SOUND");
SetupTab("ControlTab", "CONTROL");
var closeBtn = root.Q<Button>("CloseSettingsBtn");
if (closeBtn != null) closeBtn.clicked += () => uiManager.ToggleSettings();
SwitchTab("GENERAL");
}
private void SetupTab(string btnName, string tabId)
{
var btn = root.Q<Button>(btnName);
if (btn != null)
{
_tabButtons[tabId] = btn;
btn.clicked += () => SwitchTab(tabId);
}
2026-04-25 18:20:16 +07:00
}
2026-04-29 01:04:28 +07:00
private void SwitchTab(string tabId)
2026-04-25 18:20:16 +07:00
{
2026-04-29 01:04:28 +07:00
_activeTab = tabId;
_tabTitle.text = tabId;
foreach (var kvp in _tabButtons)
{
if (kvp.Key == tabId) kvp.Value.AddToClassList("active-tab");
else kvp.Value.RemoveFromClassList("active-tab");
}
2026-04-28 00:07:42 +07:00
_content.Clear();
2026-04-29 01:04:28 +07:00
switch (tabId)
{
case "GENERAL":
RenderGeneralSettings();
break;
case "CONTROL":
RenderControlSettings();
break;
default:
var comingSoon = new Label($"Settings for {tabId} coming soon...");
comingSoon.AddToClassList("text-body");
_content.Add(comingSoon);
break;
}
}
private void RenderGeneralSettings()
{
2026-04-29 02:31:15 +07:00
// --- ACCOUNT ---
_content.Add(CreateSection(GetLoc("settings_general")));
2026-04-29 01:04:28 +07:00
var wipeBtn = new Button { text = "WIPE ALL USER DATA (TEST ONLY)" };
wipeBtn.AddToClassList("button-spring");
wipeBtn.AddToClassList("btn-exit");
2026-04-29 02:31:15 +07:00
wipeBtn.style.marginTop = 10;
2026-04-29 01:04:28 +07:00
wipeBtn.style.backgroundColor = new Color(0.8f, 0.2f, 0.2f, 0.8f);
wipeBtn.clicked += () => {
bool confirm = true;
#if UNITY_EDITOR
confirm = UnityEditor.EditorUtility.DisplayDialog("Wipe Data", "This will delete your local username and restart the game. Proceed?", "Yes", "Cancel");
#endif
if (confirm)
{
PlayerPrefs.DeleteAll();
PlayerPrefs.Save();
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
};
_content.Add(wipeBtn);
2026-04-29 02:31:15 +07:00
// --- LANGUAGE ---
_content.Add(CreateSection(GetLoc("label_language")));
var langContainer = new VisualElement();
langContainer.style.flexDirection = FlexDirection.Row;
langContainer.style.alignItems = Align.Center;
langContainer.style.marginTop = 10;
var langLabel = new Label(GetLoc("label_language"));
langLabel.AddToClassList("text-body");
langLabel.style.width = Length.Percent(40);
var langDropdown = new DropdownField(new List<string> { "English", "Tiếng Việt" }, LocalizationManager.Instance?.CurrentLanguage == "vi" ? 1 : 0);
langDropdown.style.flexGrow = 1;
langDropdown.RegisterValueChangedCallback(evt => {
string code = evt.newValue == "Tiếng Việt" ? "vi" : "en";
LocalizationManager.Instance?.LoadLanguage(code);
// Refresh current tab to update text
SwitchTab("GENERAL");
});
langContainer.Add(langLabel);
langContainer.Add(langDropdown);
_content.Add(langContainer);
// --- INTERFACE ---
_content.Add(CreateSection("INTERFACE"));
float currentScale = PlayerPrefs.GetFloat("UIScale", 1.0f);
var scaleRow = CreateSliderWithInput("UI SCALE", 0.5f, 2.0f, currentScale, (val) => {
uiManager.SetUIScale(val);
});
_content.Add(scaleRow);
2026-04-29 01:04:28 +07:00
2026-04-29 02:31:15 +07:00
_content.Add(new Label("\nNote: Some elements may require restart to align perfectly.") {
style = { fontSize = 12, color = new Color(0.6f, 0.6f, 0.6f), whiteSpace = WhiteSpace.Normal, marginTop = 20 }
2026-04-29 01:04:28 +07:00
});
}
2026-04-29 02:31:15 +07:00
private VisualElement CreateSliderWithInput(string labelText, float min, float max, float startVal, Action<float> OnValueChanged)
{
var row = new VisualElement();
row.style.flexDirection = FlexDirection.Row;
row.style.alignItems = Align.Center;
row.style.marginTop = 10;
row.style.marginBottom = 10;
var label = new Label(labelText);
label.AddToClassList("text-body");
label.style.width = Length.Percent(35);
var slider = new Slider(min, max);
slider.style.flexGrow = 1;
slider.value = startVal;
var input = new TextField();
input.style.width = 60;
input.style.marginLeft = 15;
input.value = startVal.ToString("F1");
input.AddToClassList("input-field");
input.style.marginBottom = 0; // Override default margin
input.style.height = 30;
input.style.fontSize = 14;
// Sync Slider -> Input
slider.RegisterValueChangedCallback(evt => {
float val = Mathf.Round(evt.newValue * 10f) / 10f;
// Kiểm tra xem input có đang được focus không để tránh ghi đè khi người dùng đang nhập
bool isInputFocused = input.panel?.focusController?.focusedElement == input.ElementAt(0);
if (!isInputFocused) input.value = val.ToString("F1");
OnValueChanged?.Invoke(val);
});
// Sync Input -> Slider
input.RegisterValueChangedCallback(evt => {
if (float.TryParse(evt.newValue, out float val))
{
val = Mathf.Clamp(val, min, max);
slider.value = val;
OnValueChanged?.Invoke(val);
}
});
// Format on blur
input.RegisterCallback<BlurEvent>(evt => {
if (float.TryParse(input.value, out float val))
{
input.value = Mathf.Clamp(val, min, max).ToString("F1");
}
});
row.Add(label);
row.Add(slider);
row.Add(input);
return row;
}
2026-04-29 01:04:28 +07:00
private void RenderControlSettings()
{
if (_inputActions == null)
{
var errorLabel = new Label("Input Actions not found. Cannot rebind.");
errorLabel.AddToClassList("text-body");
_content.Add(errorLabel);
return;
}
var playerMap = _inputActions.FindActionMap("Player");
if (playerMap == null) return;
RenderSection("MOVEMENT", playerMap, new[] { "Move", "Jump", "Sprint", "Crouch" });
RenderSection("COMBAT", playerMap, new[] { "Attack" });
RenderSection("INTERACTION", playerMap, new[] { "Interact", "Next", "Previous" });
RenderSection("VIEW", playerMap, new[] { "Look", "ToggleView", "Scroll" });
}
private void RenderSection(string header, InputActionMap map, string[] actionNames)
{
_content.Add(CreateSection(header));
foreach (var name in actionNames)
{
var action = map.FindAction(name);
2026-04-29 02:31:15 +07:00
if (action != null) _content.Add(CreateRebindRow(action));
2026-04-29 01:04:28 +07:00
}
}
private VisualElement CreateSection(string title)
{
var header = new Label(title);
header.AddToClassList("setting-section-header");
return header;
}
private VisualElement CreateRebindRow(InputAction action)
{
var row = new VisualElement();
row.AddToClassList("rebind-row");
var label = new Label(action.name.ToUpper());
label.AddToClassList("rebind-label");
var rebindBtn = new Button();
rebindBtn.AddToClassList("rebind-button");
UpdateBindingText(action, rebindBtn);
rebindBtn.clicked += () => StartRebind(action, rebindBtn);
row.Add(label);
row.Add(rebindBtn);
return row;
}
private void UpdateBindingText(InputAction action, Button btn)
{
int bindingIndex = action.GetBindingIndexForControl(action.controls[0]);
btn.text = action.GetBindingDisplayString(bindingIndex);
}
private void StartRebind(InputAction action, Button btn)
{
string oldText = btn.text;
btn.text = "...";
btn.style.color = Color.yellow;
action.Disable();
var rebindOperation = action.PerformInteractiveRebinding()
2026-04-29 02:31:15 +07:00
.WithControlsExcluding("<Mouse>/delta")
2026-04-29 01:04:28 +07:00
.WithControlsExcluding("<Mouse>/scroll")
.OnMatchWaitForAnother(0.1f)
.OnComplete(operation => {
2026-04-29 02:31:15 +07:00
btn.style.color = new Color(0f, 1f, 0.8f);
2026-04-29 01:04:28 +07:00
UpdateBindingText(action, btn);
action.Enable();
operation.Dispose();
})
.OnCancel(operation => {
btn.style.color = new Color(0f, 1f, 0.8f);
btn.text = oldText;
action.Enable();
operation.Dispose();
});
rebindOperation.Start();
2026-04-28 00:07:42 +07:00
}
2026-04-26 05:02:49 +07:00
2026-04-28 00:07:42 +07:00
public override async Task PlayTransitionIn()
{
2026-04-28 11:35:49 +07:00
if (root != null)
{
root.style.translate = new StyleTranslate(new Translate(0, 0));
root.style.display = DisplayStyle.Flex;
2026-04-29 01:04:28 +07:00
root.style.opacity = 1;
2026-04-28 11:35:49 +07:00
}
2026-04-28 00:07:42 +07:00
_sidebar.style.translate = new StyleTranslate(new Translate(Length.Percent(-100), 0));
await Tween.Custom(-100f, 0f, duration: 0.4f, ease: Ease.OutQuad,
onValueChange: val => _sidebar.style.translate = new StyleTranslate(new Translate(Length.Percent(val), 0)));
}
2026-04-26 05:02:49 +07:00
2026-04-28 00:07:42 +07:00
public override async Task PlayTransitionOut()
{
await Tween.Custom(0f, -100f, duration: 0.4f, ease: Ease.InQuad,
onValueChange: val => _sidebar.style.translate = new StyleTranslate(new Translate(Length.Percent(val), 0)));
Hide();
2026-04-25 18:20:16 +07:00
}
}
}