前面讲了学习的别人的新手引导制作方案,然后现在我结合我们项目的新手引导制作方式,加上用ScriptableObject配置引导,稍微个人改动一下。
1.先看最基础的类,引导步骤信息类,我使用了我们项目中的几个枚举。
/// <summary> /// 引导类型 /// </summary> public enum GUIDE_TYPE { undefined = 0, click = 1, //点击 protocol = 2, //服务器通知 waitSeconds = 3, //显示几秒 arrival = 4, //到达 } /// <summary> /// 引导手指方向 /// </summary> public enum HAND_DIRECTION { top = 0, bottom = 1, } /// <summary> /// 任务方向 /// </summary> public enum WIZARD_DIRECTION { None = 0, TopLeft = 1, TopRight = 2, BottomLeft = 3, BottomRight = 4, } [Serializable] public class GuideUI { public GameObject go;//要点击的物体 public string hierarchyPath; //物体在层次面板路径 public GUIDE_TYPE guideType = GUIDE_TYPE.click;//引导种类 public float typeNeedParam = 0.5f;//种类需要的参数 public HAND_DIRECTION handDirection = HAND_DIRECTION.top; public WIZARD_DIRECTION wizardDirection = WIZARD_DIRECTION.BottomLeft; public string showMessage = string.Empty;//提示框显示信息 public bool foldOut = false; //折叠 public GuideUI() { } public GuideUI(GameObject go, string hierarchyPath) { this.go = go; this.hierarchyPath = hierarchyPath; } } 2.然后是引导信息类,管理该引导下的所有步骤: /// <summary> /// //引导种类 /// </summary> public enum GUIDE_SOURCE { None = 0, Lv = 1, Mission = 2, } /// <summary> /// 引导配置类 /// </summary> [Serializable] public class MakeGuide : ScriptableObject { //继承自ScriptableObject public bool foldOut = false;//折叠(编辑器里使用) public string guideName = "";//引导名称 public int guideId = 0;//引导id public GUIDE_SOURCE guideSource = GUIDE_SOURCE.None; //引导种类 public int guideParam = 0;//种类对应需要值,任务则为任务id,等级则为需要等级 public List<GuideUI> guideList = new List<GuideUI>(); //该引导下的步骤 }2.写一个编辑器脚本来在菜单里创建这个引导资源文件: public class MakeGuideEditorWindow : EditorWindow { //继承自EditorWindow [MenuItem("HIEngine/MakeGuide/Create a guide")] //菜单 static void CreateGuideAsset() { ScriptableObject guide = CreateInstance<MakeGuide>();//创建脚本文件对象 if (!guide) { Debug.Log("guide not found!"); return; } string path = Const.guideAssetsFolder;//这是我另一个脚本定义的静态变量:Application.dataPath + "/Packages/Guides"引导文件夹生成位置 if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } string[] filePaths = Directory.GetFiles(path);//获取文件夹下已有对象数,新建的对象名称从1开始依次加1(跳过.meta文件) int index = 0; if (filePaths==null || filePaths.Length==0) { index = 1; } else { index = filePaths.Length + 1; } foreach (string filePath in filePaths) { if (filePath.Contains(".meta")) { index--; } } path = string.Format("Assets/Packages/Guides/{0}.asset", (typeof(MakeGuide).ToString()+"_"+ index)); // 生成自定义资源到指定路径 AssetDatabase.CreateAsset(guide, path); } }然后序列化脚本文件就在指定位置创建好了。
3.然后我们重写一下MakeGuide这个脚本对象在Inspector面板的GUI显示。
using UnityEngine; using System.Collections; using UnityEditor; using System.Collections.Generic; using System.IO; [CustomEditor(typeof(MakeGuide))] //CustomEditor(typeof(T))重写谁的GUI [ExecuteInEditMode] public class MakeGuideEditor : Editor { //继承自Editor int guideListLength;//步骤数 MakeGuide guide = null;//对象 void OnEnable() { //获取当前编辑自定义Inspector的对象 guide = (MakeGuide)target; guideListLength = guide.guideList.Count; } public override void OnInspectorGUI() //重写GUI { GUILayout.Space(10); //绘制标题 GUI.skin.label.fontSize = 20; GUI.color = Color.yellow; GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUILayout.Label("Guide Config"); GUILayout.Space(5); GUI.color = Color.white; DrawConfigGuide(); } /// <summary> /// 配置 /// </summary> void DrawConfigGuide() { if (Event.current.type == EventType.DragExited) { UpdatePath(); } GUI.skin.label.fontSize = 13; GUI.color = Color.grey; GUILayout.Label("Tip:引导id代码中读取文件末尾数字..."); GUILayout.Space(10); GUI.color = Color.white; guide.guideName = EditorGUILayout.TextField("引导名称:",guide.guideName); guide.guideSource = (GUIDE_SOURCE)EditorGUILayout.EnumPopup("引导来源:", guide.guideSource); if (guide.guideSource == GUIDE_SOURCE.Lv) { guide.guideParam = EditorGUILayout.IntField("引导等级:", guide.guideParam); } else if (guide.guideSource == GUIDE_SOURCE.Mission) { guide.guideParam = EditorGUILayout.IntField("引导任务id:", guide.guideParam); } else { EditorGUILayout.HelpBox("请选择引导来源!", MessageType.Error); return; } EditorGUILayout.LabelField("-------------------------------------------------------------"); guideListLength = EditorGUILayout.IntField("步骤个数:", guideListLength); if (guideListLength<=0) { EditorGUILayout.HelpBox("请设置本次引导步骤数!",MessageType.Error); return; } //调整引导数量 AdjustGuideList(); guide.foldOut = EditorGUILayout.Foldout(guide.foldOut, "步骤列表"); if (guide.foldOut) { for (int i=0;i<guide.guideList.Count;i++) { EditorGUILayout.BeginHorizontal(); GUILayout.Space(20.0f); guide.guideList[i].foldOut = EditorGUILayout.Foldout(guide.guideList[i].foldOut, "Step " + (i + 1)); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("-",GUILayout.MaxWidth(20.0f),GUILayout.MaxWidth(15.0f),GUILayout.MaxHeight(15.0f))) { guide.guideList.RemoveAt(i); guideListLength--; if (guideListLength<=0) { EditorGUILayout.HelpBox("请设置本次引导步骤数!", MessageType.Error); return; } } if (GUILayout.Button("+", GUILayout.MaxWidth(20.0f), GUILayout.MaxWidth(15.0f), GUILayout.MaxHeight(15.0f))) { guide.guideList.Insert(i+1,new GuideUI()); guideListLength++; } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndHorizontal(); if (guide.guideList[i].foldOut) { GuideUI g = guide.guideList[i]; EditorGUILayout.BeginHorizontal(); GUILayout.Space(30.0f); g.go = EditorGUILayout.ObjectField("引导物体",g.go,typeof(GameObject),true) as GameObject; EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Space(30.0f); g.hierarchyPath = EditorGUILayout.TextField("场景路径",g.hierarchyPath); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Space(30.0f); g.guideType =(GUIDE_TYPE)EditorGUILayout.EnumPopup("需求动作",g.guideType); EditorGUILayout.EndHorizontal(); if (g.guideType == GUIDE_TYPE.waitSeconds) { EditorGUILayout.BeginHorizontal(); GUILayout.Space(30.0f); g.typeNeedParam = EditorGUILayout.FloatField("等待秒数", g.typeNeedParam); EditorGUILayout.EndHorizontal(); } EditorGUILayout.BeginHorizontal(); GUILayout.Space(30.0f); g.handDirection = (HAND_DIRECTION)EditorGUILayout.EnumPopup("手指方向",g.handDirection); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Space(30.0f); g.wizardDirection = (WIZARD_DIRECTION)EditorGUILayout.EnumPopup("人物方向",g.wizardDirection); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Space(30.0f); g.showMessage = EditorGUILayout.TextField("提示信息",g.showMessage); EditorGUILayout.EndHorizontal(); } GUILayout.Space(10.0f); } } //保存序列化到文件 if (GUI.changed) { EditorUtility.SetDirty(target); } serializedObject.ApplyModifiedProperties();//这个是必须的把改写的数据保存到文件里,不然运行后数据会丢失 } //修改步骤个数后调整步骤列表信息 void AdjustGuideList() { if (guide.guideList.Count== guideListLength) { return; } while (guide.guideList.Count > guideListLength) { guide.guideList.RemoveAt(guide.guideList.Count - 1); } while (guide.guideList.Count < guideListLength) { guide.guideList.Add(new GuideUI()); } } 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() { MakeGuide makeGuide = (MakeGuide)target; List<GuideUI> guideList = makeGuide.guideList; foreach (GuideUI guideUI in guideList) { if (guideUI.go != null) { guideUI.hierarchyPath = GetHierarchyPath(guideUI.go.transform); } } } }然后得到这样的:4.数据都有了,然后就是在游戏里怎么用他,我们需要一个管理类
这里先写一下解析数据的方法,有空再写具体怎么怎下,并且会和UI结合一下。
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; using UnityEditor; namespace HIEngine //放在了我自己的命名空间下,不用可以去掉,不然调用这个类要先using HIEngine; { public class GuideManager : MonoSingletion<GuideManager> //单例 { public List<MakeGuide> guideInfo = new List<MakeGuide>(); private MakeGuide curGuide = null; private void Start() { Init(); //初始化引导数据 } void Init() { guideInfo.Clear(); string path = Const.guideLoadPath;//Application.dataPath + "/Resources/Guides";引导资源读取文件夹 if (path == null || path == string.Empty) return; string[] files= Directory.GetFiles(path); if (files == null || files.Length == 0) return; foreach (var file in files) { if (!file.Contains(".meta")) { int index = file.IndexOf("Resources/"); string resPath = file.Substring(index+10); resPath = resPath.Remove(resPath.IndexOf('.')); ScriptableObject so = Resources.Load<ScriptableObject>(resPath);//移除后缀 if (so==null) { Log.Info("Res load null,path: "+resPath); } else { AddGuide(so as MakeGuide); Log.Info("Load res guide success,res path: "+resPath); } } } if (guideInfo.Count == 0) { return; } } void AddGuide(MakeGuide mg) { if (!guideInfo.Contains(mg)) { guideInfo.Add(mg); } } void ReadAll() { foreach (var value in guideInfo) { Log.Error("name: "+value.guideName); } } } } 就到这里,有空写整理下一节。-