新手引导系统

本文介绍了游戏新手引导系统的实现,包括灰色遮罩、高亮显示、提示语等关键元素,以及统一的触发接口、引导基类的设计。通过GuideManager管理引导流程,GuideBase为引导模块提供抽象基类,GuideMask处理遮罩逻辑。最后展示了具体引导案例,演示了如何引导玩家点击按钮和高亮图片。整个系统遵循开闭原则,易于扩展和维护。
摘要由CSDN通过智能技术生成

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

原理:
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:实际会读取配置表,根据表中步骤再进行对应引导。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值