UI框架开发中

This commit is contained in:
2025-09-29 07:45:07 +08:00
parent 423ad80303
commit 71edbb6088
39 changed files with 1191 additions and 397 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2c613363e91e7c64f86144bb2d1170ed
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class UIBasePanel : MonoBehaviour
{
public virtual void OnShow() { }
public virtual void OnHide() { }
public virtual void Refresh() { }
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2bf3cc6e9e21be7419df9ca4faedb67f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class UIBaseWindow : MonoBehaviour
{
public virtual void Show()
{
gameObject.SetActive(true);
OnShow();
}
public virtual void Hide()
{
gameObject.SetActive(true);
OnHide();
}
protected virtual void OnShow() { }
protected virtual void OnHide() { }
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e3f28a4ac0387cf41a8955b532a5fd19
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 865cfb005c2e42a4783a9f7eaa74c930
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d1669278f047d12459ab7558d17707b0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,37 @@
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Audio;
public class AudioPanel : UIBasePanel, ISettingsPanel
{
[SerializeField] private Slider masterVolumeSlider;
[SerializeField] private AudioMixer audioMixer;
private void OnEnable()
{
masterVolumeSlider.value = SettingsManager.Inst.CurrentSettings.masterVolume;
if (audioMixer == null) audioMixer = FindAnyObjectByType<AudioMixer>();
}
public void OnMasterVolumeChanged(float value)
{
SettingsManager.Inst.CurrentSettings.masterVolume = value;
SetVolume(value);
}
public void ApplySettings()
{
SetVolume(SettingsManager.Inst.CurrentSettings.masterVolume);
}
private void SetVolume(float volume)
{
if (audioMixer)
audioMixer.SetFloat("MasterVolume", Mathf.Log10(Mathf.Max(volume, 0.0001f)) * 20);
}
public void ResetToDefault()
{
throw new System.NotImplementedException();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 71d4a15b9c9ec1e4d91d6be9e66bd865
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,141 @@
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using TMPro;
using System.Collections;
public class DisplayPanel : UIBasePanel, ISettingsPanel
{
[Header("UI References")]
[SerializeField] private TMP_Dropdown resolutionDropdown;
[SerializeField] private Toggle fullscreenToggle;
[SerializeField] private Toggle borderlessToggle;
[SerializeField] private TMP_Dropdown displayDropdown;
private Resolution[] resolutions;
private void Awake()
{
InitializeResolutionDropdown();
InitializeDisplayDropdown();
}
private void OnEnable()
{
StartCoroutine(InitializeDelayed());
}
IEnumerator InitializeDelayed()
{
yield return null;
var settings = SettingsManager.Inst.CurrentSettings;
resolutionDropdown.SetValueWithoutNotify(settings.resolutionIndex);
fullscreenToggle.SetIsOnWithoutNotify(settings.fullscreen);
borderlessToggle.SetIsOnWithoutNotify(settings.borderless);
displayDropdown.SetValueWithoutNotify(settings.displayIndex);
UpdateBorderlessToggleState();
}
private void InitializeResolutionDropdown()
{
resolutions = Screen.resolutions;
resolutionDropdown.ClearOptions();
var options = new List<string>();
for (int i = 0; i < resolutions.Length; i++)
{
int refreshRate = Mathf.RoundToInt((float)resolutions[i].refreshRateRatio.value);
options.Add($"{resolutions[i].width}x{resolutions[i].height} {refreshRate}Hz");
}
resolutionDropdown.AddOptions(options);
}
private void InitializeDisplayDropdown()
{
displayDropdown.ClearOptions();
var options = new List<string>();
for (int i = 0; i < Display.displays.Length; i++)
{
options.Add($"Display {i + 1}");
}
displayDropdown.AddOptions(options);
}
public void OnResolutionChanged(int index)
{
SettingsManager.Inst.CurrentSettings.resolutionIndex = index;
ApplySetResolution();
}
public void OnFullscreenChanged(bool value)
{
SettingsManager.Inst.CurrentSettings.fullscreen = value;
UpdateBorderlessToggleState();
ApplySetResolution();
}
public void OnBorderlessChanged(bool value)
{
SettingsManager.Inst.CurrentSettings.borderless = value;
ApplySetResolution();
}
public void OnDisplayChanged(int index)
{
SettingsManager.Inst.CurrentSettings.displayIndex = index;
ApplySetDisplay();
}
private void UpdateBorderlessToggleState()
{
borderlessToggle.interactable = fullscreenToggle.isOn;
if (!fullscreenToggle.isOn)
borderlessToggle.isOn = false;
ApplySetResolution();
}
public void ApplySettings()
{
ApplySetResolution();
ApplySetDisplay();
}
void ApplySetResolution()
{
if (resolutions == null) return;
var settings = SettingsManager.Inst.CurrentSettings;
Resolution res = resolutions[resolutions.Length-1];
if (settings.resolutionIndex < resolutions.Length && settings.resolutionIndex>=0)
{
res = resolutions[settings.resolutionIndex];
}
FullScreenMode mode = settings.borderless ?
FullScreenMode.FullScreenWindow :
(settings.fullscreen ? FullScreenMode.ExclusiveFullScreen : FullScreenMode.Windowed);
Screen.SetResolution(
res.width,
res.height,
mode,
res.refreshRateRatio
);
}
void ApplySetDisplay()
{
var settings = SettingsManager.Inst.CurrentSettings;
if (settings.displayIndex > 0 && settings.displayIndex < Display.displays.Length)
{
Display.displays[settings.displayIndex].Activate();
}
}
public void ResetToDefault()
{
throw new System.NotImplementedException();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b16c03c5d0a387641a3c978840f52037
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class GeneralPanel : UIBasePanel,ISettingsPanel
{
[SerializeField] private TMP_InputField serverAddressInput;
public override void OnShow()
{
serverAddressInput.text = SettingsManager.Inst.CurrentSettings.serverAddress;
base.Refresh();
}
public override void Refresh()
{
base.Refresh();
}
public void OnServerAddressChanged(string value)
{
SettingsManager.Inst.CurrentSettings.serverAddress = value;
}
public void ApplySettings()
{
// SettingsManager.Inst.CurrentSettings.serverAddress = value;
}
public void ResetToDefault()
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4949deb4d1315ae4790fc0068c036c04
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,178 @@
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using TMPro;
using System.Collections;
public class GraphicsPanel : UIBasePanel, ISettingsPanel
{
[Header("UI References")]
[SerializeField] private TMP_Dropdown qualityDropdown;
[SerializeField] private Toggle vsyncToggle;
[SerializeField] private Slider shadowDistanceSlider;
[SerializeField] private TMP_Dropdown antiAliasingDropdown;
[SerializeField] private Toggle bloomToggle;
[SerializeField] private Slider renderScaleSlider;
[Header("Graphics References")]
[SerializeField] private VolumeProfile urpVolumeProfile;
[Header("Quality Presets")]
[SerializeField]
private QualityPreset[] qualityPresets = new QualityPreset[4]
{
new QualityPreset("Low", 0, false, 20f, 0, false, 0.8f),
new QualityPreset("Medium", 1, false, 40f, 1, true, 1.0f),
new QualityPreset("High", 2, true, 60f, 2, true, 1.2f),
new QualityPreset("Ultra", 3, true, 100f, 2, true, 1.5f)
};
[System.Serializable]
public class QualityPreset
{
public string name;
public int qualityLevel;
public bool vsyncEnabled;
public float shadowDistance;
public int antiAliasing;
public bool bloomEnabled;
public float renderScale;
public QualityPreset(string name, int qualityLevel, bool vsyncEnabled, float shadowDistance,
int antiAliasing, bool bloomEnabled, float renderScale)
{
this.name = name;
this.qualityLevel = qualityLevel;
this.vsyncEnabled = vsyncEnabled;
this.shadowDistance = shadowDistance;
this.antiAliasing = antiAliasing;
this.bloomEnabled = bloomEnabled;
this.renderScale = renderScale;
}
}
private UniversalRenderPipelineAsset _urpAsset;
private Bloom _bloom;
private void OnEnable()
{
StartCoroutine(InitializeDelayed());
}
private IEnumerator InitializeDelayed()
{
yield return null;
_urpAsset = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
if (_urpAsset == null)
{
Debug.LogError("URP Asset null");
yield break;
}
if (urpVolumeProfile != null && !urpVolumeProfile.TryGet(out _bloom))
{
Debug.LogWarning("Volume Profile Bloom null");
}
//InitializeQualityDropdown();
var settings = SettingsManager.Inst.CurrentSettings;
qualityDropdown.SetValueWithoutNotify(settings.qualityLevel);
vsyncToggle.SetIsOnWithoutNotify(settings.vsyncEnabled);
shadowDistanceSlider.SetValueWithoutNotify(settings.shadowDistance);
antiAliasingDropdown.SetValueWithoutNotify(settings.antiAliasing);
bloomToggle.SetIsOnWithoutNotify(settings.bloomEnabled);
renderScaleSlider.SetValueWithoutNotify(settings.renderScale);
ApplySettings();
}
//private void InitializeQualityDropdown()
//{
// qualityDropdown.ClearOptions();
// foreach (var preset in qualityPresets)
// {
// qualityDropdown.options.Add(new TMP_Dropdown.OptionData(preset.name));
// }
//}
public void OnQualityChanged(int index)
{
SettingsManager.Inst.CurrentSettings.qualityLevel = index;
var settings = SettingsManager.Inst.CurrentSettings;
if (index >= 0 && index < qualityPresets.Length)
{
var preset = qualityPresets[index];
// Ӧ<><D3A6>Ԥ<EFBFBD><D4A4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
vsyncToggle.SetIsOnWithoutNotify(preset.vsyncEnabled);
shadowDistanceSlider.SetValueWithoutNotify(preset.shadowDistance);
antiAliasingDropdown.SetValueWithoutNotify(preset.antiAliasing);
bloomToggle.SetIsOnWithoutNotify(preset.bloomEnabled);
renderScaleSlider.SetValueWithoutNotify(preset.renderScale);
}
// <20><><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
OnVSyncChanged(settings.vsyncEnabled);
OnShadowDistanceChanged(settings.shadowDistance);
OnAntiAliasingChanged(settings.antiAliasing);
OnBloomChanged(settings.bloomEnabled);
OnRenderScaleChanged(settings.renderScale);
}
public void OnVSyncChanged(bool value)
{
SettingsManager.Inst.CurrentSettings.vsyncEnabled = value;
QualitySettings.vSyncCount = value ? 1 : 0;
}
public void OnShadowDistanceChanged(float value)
{
SettingsManager.Inst.CurrentSettings.shadowDistance = value;
if (_urpAsset != null)
_urpAsset.shadowDistance = value;
}
public void OnAntiAliasingChanged(int index)
{
SettingsManager.Inst.CurrentSettings.antiAliasing = index;
if (_urpAsset != null)
_urpAsset.msaaSampleCount = (int)Mathf.Pow(2, index);
}
public void OnBloomChanged(bool value)
{
SettingsManager.Inst.CurrentSettings.bloomEnabled = value;
if (_bloom != null)
_bloom.active = value;
}
public void OnRenderScaleChanged(float value)
{
SettingsManager.Inst.CurrentSettings.renderScale = value;
if (_urpAsset != null)
_urpAsset.renderScale = value;
}
public void ApplySettings()
{
if (_urpAsset == null) return;
var settings = SettingsManager.Inst.CurrentSettings;
if (settings.qualityLevel != 4)
QualitySettings.SetQualityLevel(settings.qualityLevel);
QualitySettings.vSyncCount = settings.vsyncEnabled ? 1 : 0;
_urpAsset.shadowDistance = settings.shadowDistance;
_urpAsset.msaaSampleCount = (int)Mathf.Pow(2, settings.antiAliasing);
_urpAsset.renderScale = settings.renderScale;
if (_bloom != null) _bloom.active = settings.bloomEnabled;
}
public void ResetToDefault()
{
throw new System.NotImplementedException();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0a15301d059dcf34ea494f21cef46548
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
using UnityEngine;
using System.IO;
using YooAsset;
using System;
public class SettingsManager: Singleton<SettingsManager>
{
private GameSettings _currentSettings;
public GameSettings CurrentSettings => _currentSettings;
private string settingsPath;
public SettingsManager()
{
settingsPath = Path.Combine(Application.persistentDataPath, "settings.json");
LoadSettings();
Debug.Log("SettingsManager 初始化完成");
}
public void LoadSettings()
{
if (File.Exists(settingsPath))
{
string json = File.ReadAllText(settingsPath);
_currentSettings = JsonUtility.FromJson<GameSettings>(json);
}
else
{
ResetToDefaultSettings();
}
}
public void SaveSettings()
{
string json = JsonUtility.ToJson(_currentSettings, true);
File.WriteAllText(settingsPath, json);
}
public void ResetToDefaultSettings()
{
_currentSettings = new GameSettings();
SaveSettings();
}
}
[System.Serializable]
public class GameSettings
{
public string serverAddress = "http://127.0.0.1:8080";
public float masterVolume = 0.75f;
public int qualityLevel = 2;
public bool vsyncEnabled = true;
public float shadowDistance = 50f;
public int antiAliasing = 2;
public bool bloomEnabled = true;
public float renderScale = 1.0f;
public int resolutionIndex = 0;
public bool fullscreen = true;
public bool borderless = false;
public int displayIndex = 0;
}
public interface ISettingsPanel
{
void ApplySettings();
void ResetToDefault();
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2d1ed5819e87e0f4ebbdf4dad51fcb7a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,83 @@
using UnityEngine;
using UnityEngine.UI;
public class SettingsWindow : UIBaseWindow
{
[Header("Panels")]
public GeneralPanel generalPanel;
public AudioPanel audioPanel;
public GraphicsPanel graphicsPanel;
public DisplayPanel displayPanel;
[Header("Tab Buttons")]
public Button generalTab;
public Button audioTab;
public Button graphicsTab;
public Button displayTab;
[Header("Action Buttons")]
public Button applyButton;
public Button cancelButton;
public Button defaultsButton;
public Button quitButton;
private UIBasePanel currentPanel;
bool init = false;
protected override void OnShow()
{
if (!init)
{
generalTab.onClick.AddListener(() => SwitchPanel(generalPanel));
audioTab.onClick.AddListener(() => SwitchPanel(audioPanel));
graphicsTab.onClick.AddListener(() => SwitchPanel(graphicsPanel));
displayTab.onClick.AddListener(() => SwitchPanel(displayPanel));
applyButton.onClick.AddListener(ApplyAllSettings);
cancelButton.onClick.AddListener(() => { UIManager.Inst.HideWindow(gameObject.name); });
defaultsButton.onClick.AddListener(ResetToDefaults);
quitButton.onClick.AddListener(() => { Application.Quit(); });
}
SwitchPanel(generalPanel);
base.OnShow();
}
private void SwitchPanel(UIBasePanel newPanel)
{
if (currentPanel != null)
currentPanel.gameObject.SetActive(false);
newPanel.gameObject.SetActive(true);
currentPanel = newPanel;
UpdateTabButtons();
}
private void UpdateTabButtons()
{
generalTab.interactable = currentPanel != generalPanel;
audioTab.interactable = currentPanel != audioPanel;
graphicsTab.interactable = currentPanel != graphicsPanel;
displayTab.interactable = currentPanel != displayPanel;
}
public void ApplyAllSettings()
{
generalPanel.ApplySettings();
displayPanel.ApplySettings();
graphicsPanel.ApplySettings();
audioPanel.ApplySettings();
SettingsManager.Inst.SaveSettings();
UIManager.Inst.HideWindow(gameObject.name);
}
public void ResetToDefaults()
{
SettingsManager.Inst.ResetToDefaultSettings();
SwitchPanel(currentPanel);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4d6ce7fe9c8bcb144bfed87170589a6e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using YooAsset;
public class UIManager : SingletonMono<UIManager>
{
Transform uiRoot;
Dictionary<string, UIBaseWindow> openedWindows = new Dictionary<string, UIBaseWindow>();
public void ShowWindow<T>(string windowName,Action<T> onShow = null) where T : UIBaseWindow
{
if (openedWindows.ContainsKey(windowName))
{
var window = openedWindows[windowName] as T;
window.Show();
onShow?.Invoke(window);
}
else
{
YooAssets.LoadAssetAsync<GameObject>(windowName).Completed += handle =>
{
GameObject go = Instantiate((GameObject)handle.AssetObject, GameManager.Inst.MainUICanvas.transform);
var window = go.GetComponent<T>();
window.Show();
onShow?.Invoke(window);
};
}
}
public void HideWindow(string windowName)
{
if (openedWindows.ContainsKey(windowName))
{
openedWindows[windowName].Hide();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 20e218836a1c9e345ae1652efb3f2931
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: