美文网首页
新手引导系统

新手引导系统

作者: APP4x | 来源:发表于2019-12-30 19:06 被阅读0次

    新手引导是游戏中必不可少的系统。

    原理:
    1.添加一个灰色的遮罩层
    2.高亮显示引导玩家的内容,比如需要点击的按钮
    3.显示提示语:“点击此处”

    要解决的问题:
    1.统一的触发接口
    2.统一的引导基类
    3.符合开闭原则
    4.按钮如何触发原按钮的点击事件,并触发下一步引导
    5.如果不是按钮,点击背景触发下一步引导
    6.遮罩锯齿(直接提到遮罩上,不会有锯齿)
    7.记录是否已引导过


    case 1:一个简单引导案例模拟
    如图所示,只有一个按钮和一张图片。
    1.引导玩家点击按钮
    2.高亮图片
    3.结束引导

    这是一个很常见的引导需求,也是可以说明引导的核心逻辑。

    开发步骤:
    1.首先需要GuideManager,管理引导是否开启、是否已经引导过、以及创建引导的功能类。
    a.需要一个enum,区分不同的类型

    public enum EGuideType
    {
        TestPanel = 1 << 1,
        TestPanel_2 = 1 << 2,
        TestPanel_3 = 1 << 3,
    }
    

    按位存储,可以节约内存。
    b.主逻辑

    public class GuideManager : MonoBehaviour
    {
        private static GuideManager instance;
        public static GuideManager Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = FindObjectOfType<GuideManager>();
                }
                return instance;
            }
        }
    
        //功能开关
        public static bool bOpen = true;
        //PlayerPrefs存库的 KEY
        private const string KEY_GUIDE = "GuildRecord";
    
        //是否需要引导
        public bool Need(EGuideType guideType)
        {
            if (!bOpen)
                return false;
    
            int data = PlayerPrefs.GetInt(KEY_GUIDE, 0);
            return (data & (int)guideType) != (int)guideType;
        }
    
        //尝试直接进入引导
        public void TryEnter(EGuideType guideType)
        {
            if (!this.Need(guideType))
                return;
    
            string typeName = "Guide" + guideType.ToString();
            Assembly assembly = Type.GetType(typeName).Assembly;
            GuideBase guide = assembly.CreateInstance(typeName) as GuideBase;//使用反射代替工厂,符合开闭原则
            guide.Init(this);
            guide.Next();
        }
    
        //记录引导
        public void Save(EGuideType guideType)
        {
            int data = PlayerPrefs.GetInt(KEY_GUIDE, 0);
            int value = data | (int)guideType;
            PlayerPrefs.SetInt(KEY_GUIDE, value);
            PlayerPrefs.Save();
        }
    }
    

    2.引导封装成基类,提取公共方法,易于扩展

    public abstract class GuideBase
    {
        public int step;
        public GuideManager manager;
    
        public abstract EGuideType GuideType { get; }
    
        public virtual void Init(GuideManager guideManager)
        {
            this.step = 0;
            this.manager = guideManager;
        }
    
        //===========================提取公共方法=====================
        //显示背景遮罩
        public void ShowMask()
        {
            GuideMask.Instance.Show(this);
        }
        //提高Button的物体的层级,统一点击按钮进行下一步引导
        public void RaiseTop(Button buttton)
        {
            GuideMask.Instance.RaiseTopButton(buttton);
        }
        //提高非Button的物体的层级,统一点击背景进行下一步引导
        public void RaiseTop(Transform transform)
        {
            GuideMask.Instance.RaiseTopTransform(transform);
        }
        //设置描述
        public void SetTip(string text, Vector2 position)
        {
            GuideMask.Instance.SetTip(text, position);
        }
        //隐藏背景遮罩
        public void HideMask()
        {
            GuideMask.Instance.Hide();
        }
        //存储
        public void Save()
        {
            GuideManager.Instance.Save(this.GuideType);
        }
        //===========================提取公共方法=====================
    
        //开启下一步引导,这里预定义了6步,基本可以满足需求
        //IEnumerator可以等几秒动画、网络请求、加载等因素
        public void Next()
        {
            this.step++;
            switch (this.step)
            {
                case 1:
                    manager.StartCoroutine(ExecuteStep1());
                    break;
                case 2:
                    manager.StartCoroutine(ExecuteStep2());
                    break;
                case 3:
                    manager.StartCoroutine(ExecuteStep3());
                    break;
                case 4:
                    manager.StartCoroutine(ExecuteStep4());
                    break;
                case 5:
                    manager.StartCoroutine(ExecuteStep5());
                    break;
                case 6:
                    manager.StartCoroutine(ExecuteStep6());
                    break;
            }
        }
    
        public virtual IEnumerator ExecuteStep1()
        {
            yield break;
        }
        public virtual IEnumerator ExecuteStep2()
        {
            yield break;
        }
        public virtual IEnumerator ExecuteStep3()
        {
            yield break;
        }
        public virtual IEnumerator ExecuteStep4()
        {
            yield break;
        }
        public virtual IEnumerator ExecuteStep5()
        {
            yield break;
        }
        public virtual IEnumerator ExecuteStep6()
        {
            yield break;
        }
    }
    

    3.对于遮罩的逻辑
    1.copyBtn,点击触发下一步
    2.copyTransform,点击背景触发下一步
    3.设置提示问题

    public class GuideMask : MonoBehaviour
    {
        private static GuideMask instance;
        public static GuideMask Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = FindObjectOfType<GuideMask>();
                    instance.btnMask.gameObject.SetActive(true);
                    instance.txtTip.gameObject.SetActive(true);
                    instance.Hide();
                }
                return instance;
            }
        }
    
        public Button btnMask;
        private bool canClick;
        public Text txtTip;
    
        //当前引导
        private GuideBase guide;
    
        //引导目标的copyBtn
        private Button btnGuide;
        private Button btnGuideCopy;
        //引导目标的copyTr
        private Transform trGuide;
        private Transform trGuideCopy;
    
        private void Awake()
        {
            btnMask.onClick.AddListener(OnBtnMaskClicked);
        }
    
        public void Show(GuideBase guide)
        {
            this.guide = guide;
            gameObject.SetActive(true);
        }
        public void Hide()
        {
            this.guide = null;
            gameObject.SetActive(false);
        }
    
        public void SetTip(string text, Vector2 anchoredPos)
        {
            this.txtTip.text = text;
            this.txtTip.rectTransform.anchoredPosition = anchoredPos;
        }
    
        public void RaiseTopButton(Button _button)
        {
            //原有按钮
            this.btnGuide = _button;
            //创建新按钮(也可以把原按钮的层提上去)
            this.btnGuideCopy = GameObject.Instantiate(_button.gameObject).GetComponent<Button>();
            this.btnGuideCopy.onClick.AddListener(OnBtnGuideCopyClicked);
            this.btnMask.gameObject.SetActive(true);
            this.btnGuideCopy.transform.SetParent(btnMask.transform);
            this.btnGuideCopy.transform.position = _button.transform.position;
            this.btnGuideCopy.transform.localScale = Vector3.one;
        }
    
        public void RaiseTopTransform(Transform _transform)
        {
            this.trGuide = _transform;
            this.trGuideCopy = GameObject.Instantiate(_transform.gameObject).transform;
            this.btnMask.gameObject.SetActive(true);
            this.trGuideCopy.SetParent(btnMask.transform);
            this.trGuideCopy.position = _transform.position;
            this.trGuideCopy.localScale = Vector3.one;
            //设置背景可点击
            this.canClick = true;
        }
    
        private void OnBtnGuideCopyClicked()
        {
            //触发原按钮点击方法
            this.btnGuide.OnSubmit(null);
            //删除新创建的按钮
            Destroy(this.btnGuideCopy.gameObject);
            this.btnGuide = null;
            this.btnGuideCopy = null;
            //触发下一步引导
            guide.Next();
        }
    
        private void OnBtnMaskClicked()
        {
            if (this.canClick)
            {
                this.trGuide = null;
                this.trGuideCopy = null;
                this.canClick = false;
                //触发下一步引导
                guide.Next();
            }
        }
    }
    

    这样基本就完成逻辑了。

    4.添加GuideTestPanel,继承GuideBase,开始引导
    1.第一步:显示遮罩,提高button
    2.第二步:提高image的层级
    3.第三步:关闭遮罩,记录引导

    public class GuideTestPanel : GuideBase
    {
        public override EGuideType GuideType
        {
            get
            {
                return EGuideType.TestPanel;
            }
        }
    
        public override IEnumerator ExecuteStep1()
        {
            this.ShowMask();
            Button btn = TestPanel.Instance.btn;
            this.RaiseTop(btn);
            this.SetTip("请按照提示点击继续", new Vector2(0f, -585f));
            yield break;
        }
    
        public override IEnumerator ExecuteStep2()
        {
            Transform tr = TestPanel.Instance.img.transform;
            this.RaiseTop(tr);
            this.SetTip("点击任意位置继续", new Vector2(0f, -585f));
            yield break;
        }
    
        public override IEnumerator ExecuteStep3()
        {
            this.HideMask();
            this.Save();
            yield break;
        }
    }
    

    5.触发引导

    public class TestPanel : MonoBehaviour
    {
        private static TestPanel instance;
        public static TestPanel Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = FindObjectOfType<TestPanel>();
                }
                return instance;
            }
        }
    
        public Button btn;
        public Image img;
    
        private void Awake()
        {
            btn.onClick.AddListener(OnBtnClicked);
        }
    
        private void Start()
        {
            //这里触发引导,一行代码即可
            GuideManager.Instance.TryEnter(EGuideType.TestPanel); 
        }
    
        public void OnBtnClicked()
        {
            Debug.LogError("Btn Clicked!!!!");
        }
    }
    

    OK!满足需求。
    PS:实际会读取配置表,根据表中步骤再进行对应引导。

    相关文章

      网友评论

          本文标题:新手引导系统

          本文链接:https://www.haomeiwen.com/subject/vjcmoctx.html