之前的项目中有用虚拟摇杆来操纵角色移动,但是之前使用的是EasyTouch,属于NGUI下的一个插件,但是本身项目是基于UGUI的,觉得这样掺杂在一起有些不伦不类,就自己用UGUI做了一个简易的虚拟摇杆(可以实现给角色移动脚本发出摇杆的偏移参数类似于EasyTouch),若要实现更加复杂的功能,可以参照EasyTouch自行编写。
不多说下面开始实现步骤:(写这篇文章的时候离制作有些久了,有些地方可能有遗忘或者错误请见谅)
一、UGUI上接受拖拽事件。
我们项目基于Android平台,所以要考虑一些点击,拖动什么的能不能得到相应,然后在网上搜到了这篇帖子:
这是监听事件的脚本:EventTriggerListener.cs
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
public class EventTriggerListener : UnityEngine.EventSystems.EventTrigger
{
public delegate void VoidDelegate (GameObject go);
public VoidDelegate onClick;
public VoidDelegate onDown;
public VoidDelegate onEnter;
public VoidDelegate onExit;
public VoidDelegate onUp;
public VoidDelegate onSelect;
public VoidDelegate onUpdateSelect;
static public EventTriggerListener Get (GameObject go)
{
EventTriggerListener listener = go.GetComponent<EventTriggerListener>();
if (listener == null) listener = go.AddComponent<EventTriggerListener>();
return listener;
}
public override void OnPointerClick(PointerEventData eventData)
{
if(onClick != null) onClick(gameObject);
}
public override void OnPointerDown (PointerEventData eventData)
{
if(onDown != null) onDown(gameObject);
}
public override void OnPointerEnter (PointerEventData eventData)
{
if(onEnter != null) onEnter(gameObject);
}
public override void OnPointerExit (PointerEventData eventData)
{
if(onExit != null) onExit(gameObject);
}
public override void OnPointerUp (PointerEventData eventData)
{
if(onUp != null) onUp(gameObject);
}
public override void OnSelect (BaseEventData eventData)
{
if(onSelect != null) onSelect(gameObject);
}
public override void OnUpdateSelected (BaseEventData eventData)
{
if(onUpdateSelect != null) onUpdateSelect(gameObject);
}
}
二、虚拟摇杆的实现
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.Events;
public class JoyStick : MonoBehaviour
{
public float fJoyTouchRadius;
public float fJoyAreaRadius;
private Image joyTouchImage;
private Image jouAreaImage;
private bool bIsActived;//是否触发
public bool BIsActived
{
get{ return bIsActived; }
}
private bool bIsLocked = false;//是否上锁
public bool BIsLocked
{
get{ return bIsLocked; }
set
{
bIsLocked = value;
if(bIsLocked)
{
if(joyTouchImage)
{
joyTouchImage.transform.position = initPos;
//将偏移量归零
JoyStickAxis = Vector2.zero;
//角色移动函数
if(joyStickMoveEnd != null)
joyStickMoveEnd();
}
}
}
}
private float fDragRadius;//可拖动的半径
private Vector3 initPos;//初始位置
private float fScreenRate;//屏幕比率
private Vector2 JoyStickAxis;//用来传值给角色控制器
//角色移动代理函数
public delegate void JoyStickMove(Vector2 joyStickOffset);
public static event JoyStickMove joyStickMove;
//角色停止移动代理函数
public delegate void JoyStickMoveEnd();
public static event JoyStickMoveEnd joyStickMoveEnd;
void Start ()
{
//我这边自己做的自适应,后面的GlobalManager.iScreenWitdh是你默认的屏幕宽度,其实可以用UGUI里面的
//Horizontal Layout Group来控制自适应
fScreenRate = (float)Screen.width / GlobalManager.iScreenWitdh;
joyTouchImage = transform.FindChild("JoyTouch").GetComponent<Image>();
jouAreaImage = transform.FindChild("JoyArea").GetComponent<Image>();
//设置joystick的大小
joyTouchImage.rectTransform.localScale = new Vector2 (fJoyTouchRadius, fJoyTouchRadius);
jouAreaImage.rectTransform.localScale = new Vector2 (fJoyAreaRadius, fJoyAreaRadius);
//可拖拽的半径
fDragRadius = jouAreaImage.rectTransform.rect.width * fJoyAreaRadius * fScreenRate * 0.5f;
initPos = new Vector3 (1, 1, 0) * fDragRadius * 1.2f;//1.2距离侧边的距离放大1.2倍,这边可以自己后面更改
joyTouchImage.transform.position = initPos;
jouAreaImage.transform.position = initPos;
//给joytouch注册事件
EventTriggerListener.Get (joyTouchImage.gameObject).onDown = OnJoyTouchDown;
EventTriggerListener.Get (joyTouchImage.gameObject).onUp = OnJoyTouchUp;
//给joyarea注册事件
EventTriggerListener.Get (jouAreaImage.gameObject).onDown = OnJoyTouchDown;
EventTriggerListener.Get (jouAreaImage.gameObject).onUp = OnJoyTouchUp;
}
void Update()
{
#if UNITY_ANDROID
// if (Input.touchCount > 1)
// return;
//这边会有一个bug就是当你在拖拽摇杆的时候,点击屏幕别的地方,摇杆会向点击处偏移
//我没有找到很好的解决方法,暂时是用多点触控屏蔽的方法
#endif
if(bIsActived && !bIsLocked)
{
//joy世界坐标坐标
Vector3 wJoyTouchPos = joyTouchImage.transform.position;
//鼠标屏幕坐标
Vector3 mScreenPosition = new Vector3 (Input.mousePosition.x, Input.mousePosition.y, wJoyTouchPos.z);
//获得鼠标和对象之间的偏移量,拖拽时相机应该保持不动
Vector3 offset = wJoyTouchPos - mScreenPosition;
//对象新坐标
joyTouchImage.transform.position -= offset;
//判断与初始位置的距离,之所以不用世界坐标是因为本身半径要转化为世界坐标的半径,麻烦
float fDis = Vector3.Distance(joyTouchImage.transform.position, initPos);
//当相对位置大于可拖动的半径的时候,按比例缩减
if(fDis > fDragRadius)
{
joyTouchImage.transform.position = initPos +
(joyTouchImage.transform.position - initPos) * fDragRadius / fDis;
}
//虚拟摇杆偏移量
JoyStickAxis.x = (joyTouchImage.transform.position.x - initPos.x) / fDragRadius;
JoyStickAxis.y = (joyTouchImage.transform.position.y - initPos.y) / fDragRadius;
//角色移动函数
if(joyStickMove != null)
joyStickMove(JoyStickAxis);
}
}
//当触碰虚拟摇杆的进入
private void OnJoyTouchDown(GameObject go)
{
if(go == joyTouchImage.gameObject || go == jouAreaImage.gameObject)
{
bIsActived = true;
}
}
//当触碰虚拟摇杆的退出
private void OnJoyTouchUp(GameObject go)
{
if(go == joyTouchImage.gameObject || go == jouAreaImage.gameObject)
{
bIsActived = false;
//回到原位
joyTouchImage.transform.position = initPos;
//将偏移量归零
JoyStickAxis = Vector2.zero;
//角色停止移动函数
if(joyStickMoveEnd != null)
joyStickMoveEnd();
}
}
}
该脚本的核心处就是Update里面,我写的效率不是很高,可以自行改写。
三、具体操作方式
1.在canvas下创建一个空的JoyStick,该joyStick放在canvas的最下面,否则会被其他控件覆盖掉(UGUI就是这点很烦不能更改控件的Depth)。
2.在joystick下创建2个sprite,给两张转为2dSperite的图片(joyTouch在joyArea的下面)输入缩放大小,本来我这边还有参数设置初始位置,但是后面做自适应找到一个比较适中的值就去掉了,需要的可以自行编写。
3.在角色控制移动脚本处
加入:
void OnEnable()
{
JoyStick.joyStickMove += JoystickMove;
JoyStick.joyStickMoveEnd += JoystickMoveEnd;
}
void OnDisable()
{
JoyStick.joyStickMove -= JoystickMove;
JoyStick.joyStickMoveEnd -= JoystickMoveEnd;
}
还有就是:
public void JoystickMoveEnd()
{
//摇杆停止移动响应函数
}
public void JoystickMove(Vector2 joyPos)
{
//摇杆开始移动响应函数,这边的joyPos就是摇杆偏移量
}
差不多就做好了一个UGUI的简易虚拟摇杆,没有什么扩展功能,没有进行什么优化,如果有什么更好的改进方法请私信我,谢谢!