新手引导是游戏中必不可少的系统。
原理:
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:实际会读取配置表,根据表中步骤再进行对应引导。
网友评论