UGUI新手引导开发

来源:互联网 发布:乔约翰逊生涯数据 编辑:程序博客网 时间:2024/05/18 00:46

转自:

http://blog.csdn.net/lyh916/article/details/50878799,请点击链接查看原文,尊重楼主版权。


效果图:


对于新手引导,主要分两种做法:需要使用shader的和不需要shader的,这里介绍的是后者
1.屏蔽点击
假如ImageA在ImageB前面,且ImageA完全覆盖ImageB,点击两者的重叠部分,ImageA会收到点击事件,而ImageB不会。对于UGUI来说,就是将一张灰色半透明图放到最前面了


2.目标UI高亮
这里有三种做法:
a.调整目标UI的Hierarchy层级
b.克隆目标UI,调整克隆UI的Hierarchy层级
c.使用Canvas + Graphic Raycaster 
这里本人选择的是第三种,因为第一种会破坏原有的的层级,第二种的话,如果目标UI本身没有绑定Mono脚本,则需要复制事件,不太好
需要注意的是,上面三种做法都是会增加drawcall的。


案例为UGUI,Canvas画布下,有几个按钮,画布上挂着Canvas.cs脚本,MonoSingletionRoot下挂着GuideManager.cs脚本。

先看Canvas1.cs:

using UnityEngine;public class Canvas1 : MonoBehaviour {    void Awake()    {        EventTriggerListener.GetListener(UIManager.Instance.Find("Button")).onPointerClick +=            (go) => { UIManager.Instance.Show("EquipPanel"); };        EventTriggerListener.GetListener(UIManager.Instance.Find("Button (1)")).onPointerClick +=            (go) => { UIManager.Instance.Show("BagPanel"); };        EventTriggerListener.GetListener(UIManager.Instance.Find("Button (2)")).onPointerClick +=            (go) => { GuideManager.Instance.Next(); };        EventTriggerListener.GetListener(UIManager.Instance.Find("Button (3)")).onPointerClick +=            (go) => { GuideManager.Instance.Next(); };        GuideManager.Instance.Init(); //初始化引导管理器    }}
这个脚本在一开始给场景中几个物体赋予了点击事件,前两个按钮是显示两个界面,后两个是走引导步骤。

这里用到了GuideManager类,我们再来看看这个脚本写了啥:

using UnityEngine;using System.Collections;using UnityEngine.UI;using System;public class GuideManager : MonoSingletion<GuideManager> {  //单例    private Transform maskTra;//半透黑遮罩UI    private string fileDir = "GuideFile/";//Resource文件夹下引导配置文件夹    private string nowCsvFile;//目前进行的引导配置文件    private int nowIndex; //目前索引    private bool isFinish = false;//是否完成所有的新手引导    private string[] nameArray;  //配置的引导组件路径      public void Init()    {        //读取进度        string content = Resources.Load<TextAsset>(fileDir + "GuideProgress").ToString();  //Guide1,0,FALSE        string[] temp = content.Split(',');        nowCsvFile = temp[0];//读取目前进行的引导配置文件名        nowIndex = int.Parse(temp[1]);//读取目前索引        isFinish = bool.Parse(temp[2]);//读取是否结束        //读取需要高亮的组件的Hierarchy路径        if (!isFinish)        {            string s = Resources.Load<TextAsset>(fileDir + nowCsvFile).ToString();//配置文件内容            nameArray = s.Split(new string[] { "\r\n" }, System.StringSplitOptions.RemoveEmptyEntries);           }     }    void OnDestroy()    {        //退出游戏后的处理        Debug.Log("OnDestroy");    }    /// <summary>    /// 按步骤一个一个强引    /// </summary>    public void Next()    {        if (nowIndex < nameArray.Length)        {            ShowHightLight(nameArray[nowIndex]);//高亮显示组件            nowIndex++;        }        else//加载下一个文件        {            maskTra.gameObject.SetActive(false);                 int index = int.Parse(nowCsvFile.Substring(nowCsvFile.Length - 1));            index++;            nowCsvFile = nowCsvFile.Substring(0, nowCsvFile.Length - 1) + index.ToString();            string path = fileDir + nowCsvFile;            string content = null;            try            {                content = Resources.Load<TextAsset>(path).ToString();            }            catch (Exception e)             {                isFinish = true;                Debug.Log("finish");                return;            }            nowIndex = 0;            nameArray = content.Split(new string[] { "\r\n" }, System.StringSplitOptions.RemoveEmptyEntries);          }    }    /// <summary>    /// 高亮显示组建    /// </summary>    void ShowHightLight(string name)    {        StartCoroutine(FindUI(name));    }    /// <summary>    /// 取消高亮显示组件    /// </summary>    void CancelHightLight(GameObject go)    {        Destroy(go.GetComponent<GraphicRaycaster>());        Destroy(go.GetComponent<Canvas>());        Next();        EventTriggerListener.GetListener(go).onPointerClick -= CancelHightLight;    }    /// <summary>    /// 在画布下寻找制定UI    /// </summary>    IEnumerator FindUI(string name)    {        //寻找目标        GameObject go = UIManager.Instance.Find(name);        while(go == null)        {            yield return new WaitForSeconds(0.1f);            Debug.Log("wait");            go = UIManager.Instance.Find(name);        }               //高亮        maskTra = UIManager.Instance.Show("Mask").transform;        maskTra.SetAsLastSibling();        go.AddComponent<Canvas>().overrideSorting = true;        go.AddComponent<GraphicRaycaster>();        //设置监听        EventTriggerListener.GetListener(go).onPointerClick += CancelHightLight;    }}
代码和注释都很清晰,总之就是把要引导的物体高亮显示(调整层级在遮罩前面)和点击引导高亮显示下一引导,走到下一步引导并移除上一按钮的引导事件。

还有,额外打开的界面上也有脚本设置了按钮的事件:

public class Panel1 : MonoBehaviour {void Start ()     {        EventTriggerListener.GetListener(transform.Find("Button").gameObject).onPointerClick +=            (go) => { gameObject.SetActive(false); };        EventTriggerListener.GetListener(transform.Find("Button (1)").gameObject).onPointerClick +=            (go) => { Debug.Log("装上"); };        EventTriggerListener.GetListener(transform.Find("Button (2)").gameObject).onPointerClick +=            (go) => { Debug.Log("卸下"); };        EventTriggerListener.GetListener(transform.Find("Image/Button").gameObject).onPointerClick +=            (go) => { Debug.Log("查看属性"); };}}
Guide1配置文件类似如下的配置方式:

ButtonEquipPanel/Button (1)EquipPanel/Button (2)EquipPanel/Image/ButtonEquipPanel/Button

下面讲解一下这个配置文件的生成方式:

首先是每组数据都要记录高亮的物体个在Canvas下路径:

所以我们写一个脚本并序列化:

[Serializable]public class GuideUI{    public GameObject go;    public string hierarchyPath;    public GuideUI(GameObject go, string hierarchyPath)    {        this.go = go;        this.hierarchyPath = hierarchyPath;    }}public class MakeGuide : MonoBehaviour {    public List<GuideUI> guideList = new List<GuideUI>();//引导的组件数据}
然后我们写个工具来生成配置文件:

using UnityEngine;using System.Collections;using UnityEditor;using System.Collections.Generic;using System.IO;[CustomEditor(typeof(MakeGuide))][ExecuteInEditMode] public class MakeGuideEditor : Editor {    string filePath;//文件路径    public override void OnInspectorGUI()    {        base.OnInspectorGUI();        if (Event.current.type == EventType.DragExited)           {            UpdatePath();        }        EditorGUILayout.LabelField("----------------------------------------------------------");        EditorGUILayout.LabelField("文件路径:");        EditorGUILayout.TextField(filePath);        EditorGUILayout.BeginHorizontal();        if (GUILayout.Button("清空路径"))        {            filePath = "";        }        if (GUILayout.Button("读取文件(.csv)"))        {            Load();        }        if (GUILayout.Button("保存"))        {            Save();        }        EditorGUILayout.EndHorizontal();    }    string hierarchyPath;//场景中物体路径    string GetHierarchyPath(Transform t, bool initPath = true) //获取物体路径    {        if (initPath) hierarchyPath = "";        hierarchyPath = t.name + hierarchyPath;        if (t.parent.name != "Canvas")        {            Transform parent = t.parent;            hierarchyPath = "/" + hierarchyPath;            GetHierarchyPath(parent, false);        }        return hierarchyPath;    }    void UpdatePath()//拖拽一个物体到这里后更新物体的Hierarchy路径    {        MakeGuide makeGuide = (MakeGuide)target;        List<GuideUI> guideList = makeGuide.guideList;        foreach (GuideUI guideUI in guideList)        {            if (guideUI.go != null)            {                guideUI.hierarchyPath = GetHierarchyPath(guideUI.go.transform);            }        }    }    void Load()    {        filePath = EditorUtility.OpenFilePanel("读取文件(.csv)", Application.dataPath, "csv");        string content = File.ReadAllText(filePath);        string[] paths = content.Split(new string[] { "\r\n" }, System.StringSplitOptions.RemoveEmptyEntries);        MakeGuide makeGuide = (MakeGuide)target;        makeGuide.guideList = new List<GuideUI>();        Transform canvasTra = GameObject.Find("Canvas").transform;        foreach(string path in paths)        {            GameObject go = canvasTra.Find(path).gameObject;            makeGuide.guideList.Add(new GuideUI(go, path));        }    }    void Save()    {        if (string.IsNullOrEmpty(filePath))//创建一个新文件并保存        {            string path = EditorUtility.SaveFilePanel("Save", Application.dataPath, "", "csv");            File.WriteAllText(path, GetInspectorInfo());             }        else//直接保存在读取的文件        {            File.WriteAllText(filePath, GetInspectorInfo());        }        AssetDatabase.Refresh();        Debug.Log("保存成功");    }    string GetInspectorInfo()    {        string content = "";        MakeGuide makeGuide = (MakeGuide)target;        List<GuideUI> guideList = makeGuide.guideList;        foreach (GuideUI guideUI in guideList)        {            if (guideUI.go != null)            {                content += guideUI.hierarchyPath + "\r\n";            }        }        return content;    }}


得到这样的视图,然后把这个脚本MakeGuide脚本拖到场景物体上,我们就可以按照我们想要的引导的高亮顺序来显示UI了。

然后可以得到这样的文件,我们就可以根据配置文件在游戏运行时候设置引导了。

Button (2)EquipPanel/Button (1)EquipPanel/Button (2)EquipPanel/Image
GuideProgress文件为记录强引状态的文件,有服务器就可以在服务器记录了。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

另外补充本例用到的几个课外的脚本:

单例:

using UnityEngine;using System.Collections;public abstract class MonoSingletion<T> : MonoBehaviour where T : MonoBehaviour {    private static string rootName = "MonoSingletionRoot";    private static GameObject monoSingletionRoot;    private static T instance;    public static T Instance    {        get        {            if (monoSingletionRoot == null)            {                monoSingletionRoot = GameObject.Find(rootName);                if (monoSingletionRoot == null) Debug.Log("please create a gameobject named " + rootName);            }            if (instance == null)            {                instance = monoSingletionRoot.GetComponent<T>();                if (instance == null) instance = monoSingletionRoot.AddComponent<T>();            }            return instance;        }    }}
public abstract class CSharpSingletion<T> where T : new() {    private static T instance;    public static T Instance    {        get        {            if (instance == null)            {                instance = new T();            }            return instance;        }    }}
UI管理器:

using UnityEngine;using System.Collections;using System.Collections.Generic;public class UIManager : MonoSingletion<UIManager> {    public Transform canvasTra;    private Dictionary<string, GameObject> nameGoDir = new Dictionary<string, GameObject>();    void Init()    {        if (canvasTra == null) canvasTra = GameObject.Find("Canvas").transform;    }    public void Clear()    {        nameGoDir.Clear();    }    public GameObject Find(string name)    {        Init();        Transform t = canvasTra.Find(name);        if(t == null) return null;        else return t.gameObject;    }    public GameObject Show(string name)    {        if (!nameGoDir.ContainsKey(name))        {            GameObject go = Instantiate<GameObject>(Resources.Load<GameObject>(name));            go.name = name;            go.transform.SetParent(canvasTra, false);            nameGoDir.Add(name, go);        }        else        {            nameGoDir[name].SetActive(true);        }        return nameGoDir[name];    }}


这是unitypackage:

http://pan.baidu.com/s/1bA4dQ2


0 0
原创粉丝点击