游戏开发之UGUI工具类
在使用UGUI中的Button控件的时候,每次给Button添加绑定一个事件的话,都得调用它的OnClick.AddListener(传方法名),绑定一个方法,但是随着界面越来越复杂,Button也越来越多的时候,如果我们还是这样挨个绑定的话,会增加很多重复的代码,怎么样处理这种情况呢?我们先了解一下Butoon绑定事件的原理,我们知道两个不同的类调用方法的时候,在不new 对象的时候,用委托吧,Button原理就是传一个游戏对象,然后选择这个对象上的某个脚本中的某个方法,这样的好处在于当界面上的Button足够少的时候,拖物体绑定方法方便,不方便的是当Button足够多的时候,会产生很多的垃圾代码。通过看源代码我们知道Button组件继承的是UnityEngine.EventSystem 命名空间下的EventTrigger这个类,这个类有一些Unity本身的回调函数,但都是虚方法,比如OnPointerClick,OnPointerDown等,所以呢,我想自己写一个Button组件,绑定事件的原理和Button组件一样,但是为了解决Button组件的不方便问题。具体点就是我本来是想在初始化的时候,遍历Button组件,然后挨个给Button组件绑定方法,但是这样做,只能传一个方法,很不方便,不灵活。怎么办呢?我们可不可以实现在Game视图中当鼠标点击Button控件的时候,回调它绑定的方法呢?
思考,当我们点击Button物体的时候,怎么去回调对应的方法呢?那这个方法该怎么写呢?在写之前把这个类名先确定了UIEventListener。
首先想到鼠标点击,那么就得去继承一个类EventTrigger,EventTrigger类里面提供了一些回调方法,如下
public virtual void OnBeginDrag(PointerEventData eventData);
public virtual void OnCancel(BaseEventData eventData);
public virtual void OnDeselect(BaseEventData eventData);
public virtual void OnDrag(PointerEventData eventData);
public virtual void OnDrop(PointerEventData eventData);
public virtual void OnEndDrag(PointerEventData eventData);
public virtual void OnInitializePotentialDrag(PointerEventData eventData);
public virtual void OnMove(AxisEventData eventData);
public virtual void OnPointerClick(PointerEventData eventData);
public virtual void OnPointerDown(PointerEventData eventData);
public virtual void OnPointerEnter(PointerEventData eventData);
public virtual void OnPointerExit(PointerEventData eventData);
public virtual void OnPointerUp(PointerEventData eventData);
public virtual void OnScroll(PointerEventData eventData);
public virtual void OnSelect(BaseEventData eventData);
public virtual void OnSubmit(BaseEventData eventData);
只需要重写一下 public virtual void OnPointerClick(PointerEventData eventData);
然后给它添加一个回调事件,这个时候就需要用到委托了,委托怎么去定义呢?根据我们要调的方法决定,首先这个委托在OnPointerClick(PointerEventData eventData)方法里回调的,对吧,那么参数必须得有PointerEventData,其次要传一个GameObject对象,那要回调的方法已经可以确定了,
private void MainMenuUI_OnMouseClick(GameObject go,UnityEngine.EventSystems.PointerEventData eventData)
{
switch (go.name)
{
case UIButtonName.playBtn:
SceneManager.LoadScene(1);break;
default:
break;
}
}
接下来就可以定义委托和事件了:
public delegate void objEventHandle(GameObject go, PointerEventData eventData);
//鼠标点击事件
public event objEventHandle onMouseClick;
//鼠标按下事件
public event objEventHandle onMouseDownClick;
//重写OnPointerClick方法
public override void OnPointerClick(PointerEventData eventData)
{
//如果委托已经注册事件,那么在回调的时候会回调相应的方法
if(onMouseClick != null)
{
onMouseClick(this.gameObject, eventData);
}
}
为了保证每个Button游戏对象身上都有UIEventListener组件,我写了一个根据游戏对象GameObject获取它身上的UIEventListener组件。
//根据物体对象返回物体对象身上的UIEventListener组件
public static UIEventListener Get(GameObject obj)
{
//从obj获取UIEventListener组件,并返回
UIEventListener lister = obj.GetComponent<UIEventListener>();
//如果该物体上没有这个组件,给它添加上这个组件并返回
if (lister == null) {lister= obj.AddComponent<UIEventListener>(); }
return lister;
}
下面是完善的UIEventListener:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
//定义委托 是根据要回调的方法决定参数
public delegate void objEventHandle(GameObject go, PointerEventData eventData);
public class UIEventListener :EventTrigger {
//鼠标点击事件
public event objEventHandle onMouseClick;
//鼠标按下事件
public event objEventHandle onMouseDownClick;
//根据物体对象返回物体对象身上的UIEventListener组件
public static UIEventListener Get(GameObject obj)
{
//从obj获取UIEventListener组件,并返回
UIEventListener lister = obj.GetComponent<UIEventListener>();
//如果该物体上没有这个组件,给它添加上这个组件并返回
if (lister == null) {lister= obj.AddComponent<UIEventListener>(); }
return lister;
}
//重写OnPointerClick方法
public override void OnPointerClick(PointerEventData eventData)
{
//如果委托已经注册事件,那么在回调的时候会回调相应的方法
if(onMouseClick != null)
{
onMouseClick(this.gameObject, eventData);
}
}
//重写OnPointerDown方法
public override void OnPointerDown(PointerEventData eventData)
{
if(onMouseDownClick!=null)
{ onMouseDownClick(this.gameObject, eventData); }
}
}
下面这个类是对UIEventListener的运用:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class UIButtonName
{
public const string playBtn = "PlayBtn";
public const string headRightBtn = "HeadRightBtn";
public const string headLeftBtn = "HeadLeftBtn";
public const string handRightBtn = "HandRightBtn";
public const string handLeftBtn = "HandLeftBtn";
public const string upperBodyRightBtn = "UpperBodyRightBtn";
public const string upperBodyLeftBtn = "UpperBodyLeftBtn";
public const string lowerBodyRightBtn = "LowerBodyRightBtn";
public const string lowerBodyLeftBtn = "LowerBodyLeftBtn";
public const string footRightBtn = "FootRightBtn";
public const string footLeftBtn = "FootLeftBtn";
public const string blueBtn = "BlueBtn";
public const string cyanBtn = "CyanBtn";
public const string greenBtn = "GreenBtn";
public const string pureBtn = "PureBtn";
public const string redBtn = "RedBtn";
}
public class MainMenuUI : MonoBehaviour {
///这样写的目的 为了保证List<Button>的封装性,
//不让其他类调用,但是又想在Unity的Inspecter
//上面板显示,并且可以拖游戏对象,故此用[SerializeField]
[SerializeField]
private List<Button> btns=new List<Button>();
private Transform player;
private SkinnedMeshRenderer[] meshRenderers;
private void Awake()
{
player = GameObject.FindGameObjectWithTag("Player").transform;
meshRenderers =player.GetComponentsInChildren<SkinnedMeshRenderer>();
//对于UIEventListener的使用
foreach (Button btn in btns)
{
//给每个Button游戏对象注册回调方法
UIEventListener.Get(btn.gameObject).onMouseClick += MainMenuUI_OnMouseClick;
}
AudioManager.Instance.PlayBgMusic(AudioType.BGM);
}
//回调方法,根据游戏对象的名字,调用对应的方法
private void MainMenuUI_OnMouseClick(GameObject go,UnityEngine.EventSystems.PointerEventData eventData)
{
AudioManager.Instance.PlayAudio(AudioType.ButtonClick);
switch (go.name)
{
case UIButtonName.playBtn:
SceneManager.LoadScene(1);break;
case UIButtonName.blueBtn:
ChangeColor(new Color(53f/255f,53f/255,169f/255,1));break;
case UIButtonName.cyanBtn:
ChangeColor(new Color(10f / 255, 164f / 255, 164f / 255, 1)); break;
case UIButtonName.greenBtn:
ChangeColor(new Color(62f / 255, 152f / 255, 62f / 255, 1)); break;
case UIButtonName.pureBtn:
ChangeColor(new Color(130f / 255, 42f / 255, 130f / 255, 1)); break;
case UIButtonName.redBtn:
ChangeColor(new Color(183f / 255,43f / 255, 43f / 255, 1)); break;
case UIButtonName.headLeftBtn:
ChangeHead(0); break;
case UIButtonName.headRightBtn:
ChangeHead(1);break;
case UIButtonName.handLeftBtn:
ChangeHand(0);break;
case UIButtonName.handRightBtn:
ChangeHand(1);break;
case UIButtonName.upperBodyLeftBtn:
ChangeUpperBody(0);break;
case UIButtonName.upperBodyRightBtn:
ChangeUpperBody(1);break;
case UIButtonName.lowerBodyLeftBtn:
ChangeLowerBody(0);break;
case UIButtonName.lowerBodyRightBtn:
ChangeLowerBody(1);break;
case UIButtonName.footLeftBtn:
ChangeFoot(0);break;
case UIButtonName.footRightBtn:
ChangeFoot(1);break;
default:
break;
}
}
[SerializeField]
private List<Mesh> headMeshs = new List<Mesh>();
private int headIndex;
[SerializeField]
private SkinnedMeshRenderer headMeshRenderer;
[SerializeField]
private List<Mesh> handMeshs = new List<Mesh>();
private int handIndex;
[SerializeField]
private SkinnedMeshRenderer handMeshRenderer;
[SerializeField]
private List<Mesh> upperBodyMeshs = new List<Mesh>();
private int upperBodyIndex;
[SerializeField]
private SkinnedMeshRenderer upperBodyMeshRenderer;
[SerializeField]
private List<Mesh> lowerBodyMeshs = new List<Mesh>();
private int lowerBodyIndex;
[SerializeField]
private SkinnedMeshRenderer lowerBodyMeshRenderer;
[SerializeField]
private List<Mesh> footMeshs = new List<Mesh>();
private int footIndex;
[SerializeField]
private SkinnedMeshRenderer footMeshRenderer;
private void ChangeFoot(int leftOrRight)
{
//leftButton
if (leftOrRight == 0)
{
footIndex--;
if (footIndex < 0)
{ footIndex = footMeshs.Count - 1; }
}
else
{
footIndex++;
if (footIndex > footMeshs.Count - 1)
{ footIndex = 0; }
}
Mesh mesh = footMeshs[footIndex];
footMeshRenderer.sharedMesh = mesh;
PlayerTemplate.footMeshRenderer = mesh;
}
private void ChangeLowerBody(int leftOrRight)
{
//leftButton
if (leftOrRight == 0)
{
lowerBodyIndex--;
if (lowerBodyIndex < 0)
{ lowerBodyIndex = lowerBodyMeshs.Count - 1; }
}
else
{
lowerBodyIndex++;
if (lowerBodyIndex > lowerBodyMeshs.Count - 1)
{lowerBodyIndex = 0; }
}
Mesh mesh = lowerBodyMeshs[lowerBodyIndex];
lowerBodyMeshRenderer.sharedMesh = mesh;
PlayerTemplate.lowerBodyMeshRenderer = mesh;
}
private void ChangeUpperBody(int leftOrRight)
{
//leftButton
if (leftOrRight == 0)
{
upperBodyIndex--;
if (upperBodyIndex < 0)
{ upperBodyIndex = upperBodyMeshs.Count - 1; }
}
else
{
upperBodyIndex++;
if (upperBodyIndex > upperBodyMeshs.Count - 1)
{ upperBodyIndex = 0; }
}
Mesh mesh = upperBodyMeshs[upperBodyIndex];
upperBodyMeshRenderer.sharedMesh = mesh;
PlayerTemplate.upperBodyMeshRenderer= mesh;
}
private void ChangeHand(int leftOrRight)
{
//leftButton
if (leftOrRight == 0)
{
handIndex--;
if (handIndex < 0)
{ handIndex = handMeshs.Count - 1; }
}
else
{
handIndex++;
if (handIndex > handMeshs.Count - 1)
{ handIndex = 0; }
}
Mesh mesh = handMeshs[handIndex];
handMeshRenderer.sharedMesh = mesh;
PlayerTemplate.handMeshRenderer = mesh;
}
private void ChangeHead(int leftOrRight)
{
//leftButton
if(leftOrRight==0)
{
headIndex--;
if(headIndex<0)
{ headIndex = headMeshs.Count - 1; }
}
else
{
headIndex++;
if(headIndex>headMeshs.Count-1)
{ headIndex = 0; }
}
Mesh mesh = headMeshs[headIndex];
headMeshRenderer.sharedMesh = mesh;
PlayerTemplate.headMeshRenderer = mesh;
}
private void ChangeColor(Color color)
{
PlayerTemplate.selectColor = color;
foreach (SkinnedMeshRenderer render in meshRenderers)
{
render.material.color = color;
}
}
}
总结:使用方法
1.把UIEventListener脚本拖到项目中
2.在UI类中写一个List<Button> btns列表
[SerializeField]
private List<Button> btns=new List<Button>();
3.在初始化的时候遍历btns,挨个添加上UIEventListener组件,并绑定方法。
foreach (Button btn in btns)
{
UIEventListener.Get(btn.gameObject).onMouseClick += MainMenuUI_OnMouseClick;
}
private void MainMenuUI_OnMouseClick(GameObject go,UnityEngine.EventSystems.PointerEventData eventData)
{
switch (go.name)
{
case UIButtonName.playBtn:
SceneManager.LoadScene(1);break;
default:
break;
}
}