手游常见一种用来移动玩家人物的触碰摇杆,它的组成一般是由一个大圆和一个小圆,玩家的直觉是在大圆范围内操作这个小圆来进行移动操作。但作为开发者,请问实际上接收玩家操作的是大圆还是小圆?
好,如果你没答出,带着这个疑问来看这个摇杆的具体实现。
具体实现
目录
首先我们要写一个脚本。
PEListener
public class PEListener : MonoBehaviour, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler, IDragHandler
{
public Action<PointerEventData> onClickDown;
public Action<PointerEventData> onClickUp;
public Action<PointerEventData> onDrag;
public Action<PointerEventData> onClick;
public void OnPointerDown(PointerEventData eventData)
{
onClickDown?.Invoke(eventData);
}
public void OnPointerUp(PointerEventData eventData)
{
onClickUp?.Invoke(eventData);
}
public void OnDrag(PointerEventData eventData)
{
onDrag?.Invoke(eventData);
}
public void OnPointerClick(PointerEventData eventData)
{
onClick?.Invoke(eventData);
}
}
从类名及方法名来看不难看出是一个监听玩家各种触屏操作的脚本,这里面的c#知识点是委托Action,Action可以简单地看作是方法的集合,图中调用委托的写法是一种简化的写法。
等同于图中红色的写法,若委托不空则调用里面的方法。
第二步我们要再写一个脚本
UIWnd这个
public class UIWnd : MonoBehaviour
{
public Image imgTouch;//玩家实际触碰的范围
public Image imgDirBg;//大圆
public Image imgDirPoint;//小圆
private float pointDis;//当前屏幕下小球可以移动的最大距离
private Vector2 startPos = Vector2.zero;//在按下时大圆的位置,即玩家触碰处
private Vector2 defaultPos = Vector2.zero;//大圆的初始位置,用于归正
private void Start()
{
pointDis = Screen.height * 1.0f / 750 * 90;//750是我canvas分辨率的高度,90是小圆可以移动的最大距离
defaultPos = imgDirBg.transform.position;
RegisterTouchEvts();
}
}
答对了吗,接收玩家操作的既不是大圆也不是小圆,而是本脚本里的imgTouch,那它到底是什么呢,我们放到最后界面实现再来揭晓
Start里RegisterTouchEvts()就是玩家对摇杆操作的具体实现了。我们来看怎么写。
private void RegisterTouchEvts()
{
PEListener listener = GetOrAddComponect<PEListener>(imgTouch.gameObject);
istener.onClickDown = (PointerEventData evt) =>
{
startPos = evt.position;//此处evt.position是玩家按下的位置的屏幕坐标
imgDirPoint.gameObject.SetActive(true);//显示小圆
imgDirBg.transform.position = evt.position;//大圆位置即按下位置
};
listener.onClickUp = (PointerEventData evt) =>
{
imgDirBg.transform.position = defaultPos; //大圆位置归正
imgDirPoint.transform.localPosition = Vector2.zero;//小圆回到中心
imgDirPoint.gameObject.SetActive(false);//隐藏小圆
//player.SetMoveDir(Vector2.zero);//此时无移动方向
};
listener.onDrag = (PointerEventData evt) =>
{
Vector2 dir = evt.position - startPos;
float len = dir.magnitude;//小圆此时移动的距离,向量相减求模长
//float dis = Vector2.Distance(evt.position, startPos); len等同于dis
if (len > pointDis)
{
Vector2 clampDir = Vector2.ClampMagnitude(dir, 90);//返回dir,最大值为90
imgDirPoint.transform.position = startPos + clampDir;//小圆无法超出其最大距离
}
else
{
imgDirPoint.transform.position = evt.position;//小圆在玩家按下的位置
}
//player.SetMoveDir(dir.normalized);传入角色移动方向
};
}
先获取imgTouch上的监听脚本PEListener,然后设置PElistener的委托方法,我们这里要用到的是按下,松开和拖拽三种操作,用(委托参数)=>匿名函数可以更方便地设置委托方法。
evt.position,将屏幕看成左下角为原点(0,0),右上角为(屏宽,屏高)一个矩形,该坐标就是玩家触碰的位置在该矩形中的坐标。为什么要着重讲这个屏幕坐标,请往下看。
第三步unity里的设置和界面实现
canvas先做如此设置
这一步就回答了刚刚的那个问题,有时候屏幕坐标不等于世界坐标,例如屏幕空间是由摄像机控制时,而这个屏幕空间覆盖的设置会让屏幕坐标一定等于世界坐标,也就是为什么imgDirBg.transform.position 可以直接等于 evt.position的原因。
接着我们在canvas下新建三个img
imgTouch不需要设置图片且要比大圆还要大,它们的关系类似于这样
接着改变imgTouch透明度使其看不见。现在揭晓为什么要用这个东西来接受玩家的操作,可能有的小伙伴在阅读RegisterTouchEvts()方法就猜出来了(提示:大圆位置改变)。
3
2
1
-
玩家在触屏时并不是只能手指在摇杆上时才接收操作信息,而是手落在哪里摇杆就在哪里,此时我们增加一个可控制摇杆的具体范围,并且使大圆在玩家按下时改变位置。
现在我们只需给imgTouch挂载上PEListener,给任意一个游戏物体挂上UIWnd就可以使用这个摇杆了
我们要知道为什么,你知道为什么吗,如果你知道,那么你就知道了。