/** * 全屏示例的 UI 控制器 * * Author: Kirurobo http://twitter.com/kirurobo * License: MIT */ using UnityEngine; using UnityEngine.UI; namespace Kirurobo { /// /// 使用 Toggle 开关 WindowController 设置的示例 /// public class FullscreenSample : MonoBehaviour { private UniWindowController uniwinc; private RectTransform canvasRect; private float mouseMoveSS = 0f; // 鼠标轨迹平方和。[px^2] private float mouseMoveSSThreshold = 36f; // 点击(非拖拽)阈值。[px^2] private Vector3 lastMousePosition; // 右键点击位置。 private float touchDuration = 0f; private float touchDurationThreshold = 0.5f; // 长按时间阈值。[s] public Toggle transparentToggle; public Toggle topmostToggle; public Toggle bottommostToggle; public Dropdown fitWindowDropdown; public Button quitButton; public Button menuCloseButton; public RectTransform menuPanel; /// /// 设置 /// void Start() { // 查找 UniWindowController uniwinc = GameObject.FindAnyObjectByType(); // 获取 Canvas 的 RectTransform if (menuPanel) canvasRect = menuPanel.GetComponentInParent().GetComponent(); // 根据有效显示器数量创建选项 UpdateMonitorDropdown(); // 将 Toggle 的选中状态与当前状态同步 UpdateUI(); // 初始状态下关闭菜单 CloseMenu(); if (uniwinc) { // 操作 UI 时将其反映到窗口 transparentToggle?.onValueChanged.AddListener(val => uniwinc.isTransparent = val); topmostToggle?.onValueChanged.AddListener(val => uniwinc.isTopmost = val); bottommostToggle?.onValueChanged.AddListener(val => uniwinc.isBottommost = val); fitWindowDropdown?.onValueChanged.AddListener(val => SetFitToMonitor(val)); quitButton?.onClick.AddListener(Quit); menuCloseButton?.onClick.AddListener(CloseMenu); // Add events uniwinc.OnStateChanged += (type) => { UpdateUI(); //ShowEventMessage("Window state changed: " + type); }; uniwinc.OnMonitorChanged += () => { UpdateMonitorDropdown(); UpdateUI(); //ShowEventMessage("Resolution changed!"); }; } } /// /// 每帧执行 /// private void Update() { // 鼠标右键点击时显示上下文菜单。 // 如果鼠标移动距离在阈值以下,则视为点击 if (InputProxy.GetMouseButtonDown(1)) { lastMousePosition = InputProxy.mousePosition; touchDuration = 0f; } if (InputProxy.GetMouseButton(1)) { mouseMoveSS += (InputProxy.mousePosition - lastMousePosition).sqrMagnitude; } if (InputProxy.GetMouseButtonUp(1)) { if (mouseMoveSS < mouseMoveSSThreshold) { ShowMenu(lastMousePosition); } mouseMoveSS = 0f; touchDuration = 0f; } // 暂时仅在 Legacy Input Manager 中处理触摸 #if ENABLE_LEGACY_INPUT_MANAGER // 长按也可显示菜单 if (Input.touchSupported && (Input.touchCount > 0)) { Touch touch = Input.GetTouch(0); if (touch.phase == TouchPhase.Began) { lastMousePosition = Input.mousePosition; touchDuration = 0f; } if (touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary) { mouseMoveSS += touch.deltaPosition.sqrMagnitude; touchDuration += touch.deltaTime; } if (touch.phase == TouchPhase.Ended) { if ((mouseMoveSS < mouseMoveSSThreshold) && (touchDuration >= touchDurationThreshold)) { ShowMenu(lastMousePosition); } mouseMoveSS = 0f; touchDuration = 0f; } } #endif // 按下 [Space] 键也可显示菜单 if (InputProxy.GetKeyUp("space")) { if (menuPanel) { if (menuPanel.gameObject.activeSelf) { CloseMenu(); } else { Vector2 pos = new Vector2(Screen.width / 2, Screen.height / 2); ShowMenu(pos); } } } // 按下 [ESC] 时退出或停止播放 if (InputProxy.GetKeyUp("escape")) { Quit(); } } void Quit() { #if UNITY_EDITOR UnityEditor.EditorApplication.isPlaying = false; #else Application.Quit(); #endif } /// /// 适配显示器下拉列表更改时的处理 /// /// void SetFitToMonitor(int val) { if (!uniwinc) return; if (val < 1) { // 下拉列表第一项为不适配 uniwinc.shouldFitMonitor = false; } else { // 由于从第二项开始,显示器编号减1 uniwinc.monitorToFit = val - 1; uniwinc.shouldFitMonitor = true; // 从false变为true时才会移动窗口,因此先指定显示器编号再更改 } } /// /// 在指定坐标显示上下文菜单 /// /// 指定中心坐标 private void ShowMenu(Vector2 position) { if (menuPanel) { Vector2 pos = position * (canvasRect.sizeDelta.x / Screen.width); float w = menuPanel.rect.width; float h = menuPanel.rect.height; // 以指定坐标为中心进行位置调整 pos.y = Mathf.Max(Mathf.Min(pos.y, Screen.height - h / 2f), h / 2f); // 如果超出则向上移动 pos.x = Mathf.Max(Mathf.Min(pos.x, Screen.width - w / 2f), w / 2f); // 如果超出右侧则向左移动 menuPanel.pivot = Vector2.one * 0.5f; // 设置为中心 menuPanel.anchorMin = Vector2.zero; menuPanel.anchorMax = Vector2.zero; menuPanel.anchoredPosition = pos; menuPanel.gameObject.SetActive(true); } } /// /// 关闭上下文菜单 /// private void CloseMenu() { if (menuPanel) { menuPanel.gameObject.SetActive(false); } } /// /// 将实际状态反映到 UI 显示 /// private void UpdateUI() { if (uniwinc) { if (transparentToggle) { transparentToggle.isOn = uniwinc.isTransparent; } if (topmostToggle) { topmostToggle.isOn = uniwinc.isTopmost; } if (bottommostToggle) { bottommostToggle.isOn = uniwinc.isBottommost; } if (fitWindowDropdown) { if (uniwinc.shouldFitMonitor) { fitWindowDropdown.value = uniwinc.monitorToFit + 1; } else { fitWindowDropdown.value = 0; } fitWindowDropdown.RefreshShownValue(); } } } /// /// 更新显示器选择下拉列表的选项 /// 之后需要调用 UpdateUI() /// void UpdateMonitorDropdown() { if (!fitWindowDropdown) return; // 删除除第一项以外的选项 fitWindowDropdown.options.RemoveRange(1, fitWindowDropdown.options.Count - 1); if (!uniwinc) { fitWindowDropdown.value = 0; } else { int count = UniWindowController.GetMonitorCount(); for (int i = 0; i < count; i++) { fitWindowDropdown.options.Add(new Dropdown.OptionData("适配显示器 " + i)); } if (uniwinc.monitorToFit >= count) { uniwinc.monitorToFit = count - 1; } } } /// /// 显示带超时功能的消息 /// /// private void ShowEventMessage(string message) { Debug.Log(message); } /// /// 如果 UI 中有文本框,则显示消息。否则输出到控制台 /// /// public void OutputMessage(string text) { Debug.Log(text); } } }