需求分析
项目中需要接入遥控器,代替键鼠,以及替代触屏
改造内容
1.需要UI上进行适配:需要设置UI的Selected和Normal状态进行明显区分,当选中时会显示Selected的状态。效果较好的是UI本身作为一个底图或描边,不影响前面显示的图片
2.代码方面:需要设置选中的Selectable,传给EventSystem.current.SetSelectedGameObject( ),上下左右移动需要监听到按键事件的时候先获取到对应方向的UI : current.FindSelectableOnUp() ,然后进行移动。
3.交互优化:界面上尽可能不要有多层可交互的UI并且互相遮挡,除非手动指定按钮的上下左右对应的UI,否则可能会随时选到被遮挡的UI。
示例代码
RemoteControlNavigation.cs
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class RemoteControlNavigation : MonoBehaviour
{
public static RemoteControlNavigation Instance;
private void Awake()
{
Instance = this;
}
public Selectable firstSelected;
public float inputDelay = 0.15f;
public Transform currentUIRoot;
public Selectable current;
private float lastInputTime;
public Selectable firstSelected2;
public Transform currentUIRoot2;
public Selectable current2;
public bool isOnlyBtn;
void Start()
{
if(isOnlyBtn)
{
firstSelected = gameObject.GetComponent<Selectable>();
current = gameObject.GetComponent<Selectable>();
currentUIRoot = transform.parent;
}
else
{
if (LoginManager.Instance.isUser1Login || LoginManager.Instance.isUser2Login || LoginManager.Instance.isUser3Login || LoginManager.Instance.isUser4Login||
LoginManager.Instance.isUser1VisitorLogin || LoginManager.Instance.isUser2VisitorLogin || LoginManager.Instance.isUser3VisitorLogin || LoginManager.Instance.isUser4VisitorLogin)
{
firstSelected = firstSelected2;
current = current2;
currentUIRoot = currentUIRoot2;
}
}
Debug.LogError(currentUIRoot == null);
Debug.LogError(firstSelected == null);
Debug.LogError(firstSelected.transform == null);
Debug.LogError(firstSelected.transform.root == null);
// 如果没指定UI根节点,就用当前物体所在的Canvas
if (currentUIRoot == null)
currentUIRoot = firstSelected.transform.root;
current = firstSelected;
if (current != null)
EventSystem.current.SetSelectedGameObject(current.gameObject);
}
void Update()
{
if(isOnlyBtn) //返回
{
if(Input.GetKeyDown((KeyCode)System.Enum.Parse(typeof(KeyCode),XMLConfigData.instance.Back)))
{
SceneManager.LoadScene("MenuScene");
}
}
if (Time.time - lastInputTime < inputDelay)
return; // 防抖
bool moved = false;
// 上下左右导航
if (Input.GetKeyDown((KeyCode)System.Enum.Parse(typeof(KeyCode), XMLConfigData.instance.Up)))
{
MoveTo(current.FindSelectableOnUp());
moved = true;
}
else if (Input.GetKeyDown((KeyCode)System.Enum.Parse(typeof(KeyCode), XMLConfigData.instance.Down)))
{
MoveTo(current.FindSelectableOnDown());
moved = true;
}
else if (Input.GetKeyDown((KeyCode)System.Enum.Parse(typeof(KeyCode), XMLConfigData.instance.Left)))
{
MoveTo(current.FindSelectableOnLeft());
moved = true;
}
else if (Input.GetKeyDown((KeyCode)System.Enum.Parse(typeof(KeyCode), XMLConfigData.instance.Right)))
{
MoveTo(current.FindSelectableOnRight());
moved = true;
}
// 确认键(回车)
if (Input.GetKeyDown((KeyCode)System.Enum.Parse(typeof(KeyCode), XMLConfigData.instance.Enter)))
{
if (current != null)
{
ExecuteEvents.Execute(current.gameObject, new BaseEventData(EventSystem.current), ExecuteEvents.submitHandler);
}
}
if (moved)
lastInputTime = Time.time;
}
void MoveTo(Selectable next)
{
if (next != null && IsInCurrentUIRoot(next))
{
current = next;
EventSystem.current.SetSelectedGameObject(current.gameObject);
}
}
bool IsInCurrentUIRoot(Selectable target)
{
return target.transform.IsChildOf(currentUIRoot);
}
/// <summary>
/// 切换UI焦点范围(比如从背景切到弹窗)
/// </summary>
public void SetUIRoot(Transform newRoot, Selectable first)
{
currentUIRoot = newRoot;
current = first;
EventSystem.current.SetSelectedGameObject(current.gameObject);
}
}
SetUIFocus.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class SetUIFocus : MonoBehaviour
{
public Selectable popupFirstButton;
public Transform popupCanvas;
public Selectable backgroundLastButton;
public Transform backgroundCanvas;
void OnEnable()
{
// remote 是挂脚本的对象
RemoteControlNavigation.Instance.SetUIRoot(popupCanvas.transform, popupFirstButton);
}
private void OnDisable()
{
RemoteControlNavigation.Instance.SetUIRoot(backgroundCanvas.transform, backgroundLastButton);
}
}