[Unity] 使用UGUI制作自己的虚拟摇杆

查看:2743 |回复:22 | 2015-11-2 23:25:28

您需要 登录 才可以下载或查看,没有账号?注册

x
本帖最后由 大西几 于 2021-2-24 15:28 编辑

作者原话:
最近因为项目需要决定尝试自己来实现一个虚拟摇杆,所以在今天的文章中我们的目标是使用uGUI来制作一个可以在移动平台稳定运行的虚拟摇杆(请不要问我为什么不使用NGUI来实现,你说我做个虚拟摇杆有必要在项目里导入那么多的资源嘛23333)。


原帖地址见末尾
虚拟摇杆这种输入方式相信大家在手机游戏平台上已经相当的熟悉了,首先我们来简单了解下虚拟摇杆的设计原理。
虚拟摇杆有一张固定的2D贴图(背景层)和一张可拖动的2D贴图(控制层)构成,
默认情况下控制层在背景层的中心,我们称这个位置为初始位置,当移动控制层后移动层的位置会发生变化,


此时控制层的当前位置和初始位置两点间可以计算出一个2D向量,通过这个向量我们就可以判断虚拟摇杆的移动方向。在经典的八方向摇杆导航中摇杆中可移动方向被分成了上、左上、右上、下、左下、右下、左、右共8个方向。
我们知道根据三角函数可以非常容易地计算出这个2D向量的角度并由此判定摇杆是在向着这8个方向中的哪一个方向移动。
在今天的文章中,我们不需要考虑这8个方向,因为我们可以向任何一个方向进行移动。
  好了,首先在场景中创建两个Image组件和一个空的游戏体,然后将这两个Image组件拖拽到这个空的游戏体下使它们称为其子节点。这里需要注意的是这两个Image的层级关系。现在我们来编写脚本,这个脚本将被添加到控制层物体上:

  1. /*
  2. * uGUI虚拟摇杆
  3. * 作者:秦元培
  4. * 博客:[url]http://qinyuanpei.com[/url]
  5. * 时间:2015年10月24日
  6. */

  7. using UnityEngine;
  8. using System.Collections;
  9. using UnityEngine.UI;
  10. using UnityEngine.EventSystems;

  11. public class JoyStick : MonoBehaviour,IPointerDownHandler, IPointerUpHandler, IDragHandler
  12. {

  13.     /// <summary>
  14.     /// 摇杆最大半径
  15.     /// 以像素为单位
  16.     /// </summary>
  17.     public float JoyStickRadius = 50;

  18.     /// <summary>
  19.     /// 摇杆重置所诉
  20.     /// </summary>
  21.     public float JoyStickResetSpeed = 5.0f;

  22.     /// <summary>
  23.     /// 当前物体的Transform组件
  24.     /// </summary>
  25.     private RectTransform selfTransform;

  26.     /// <summary>
  27.     /// 是否触摸了虚拟摇杆
  28.     /// </summary>
  29.     private bool isTouched = false;

  30.     /// <summary>
  31.     /// 虚拟摇杆的默认位置
  32.     /// </summary>
  33.     private Vector2 originPosition;

  34.     /// <summary>
  35.     /// 虚拟摇杆的移动方向
  36.     /// </summary>
  37.     private Vector2 touchedAxis;
  38.     public Vector2 TouchedAxis
  39.     {
  40.         get
  41.         {
  42.             if(touchedAxis.magnitude < JoyStickRadius)
  43.                 return touchedAxis.normalized / JoyStickRadius;
  44.             return touchedAxis.normalized;
  45.         }
  46.     }

  47.     /// <summary>
  48.     /// 定义触摸开始事件委托
  49.     /// </summary>
  50.     public delegate void JoyStickTouchBegin(Vector2 vec);

  51.     /// <summary>
  52.     /// 定义触摸过程事件委托
  53.     /// </summary>
  54.     /// <param name="vec">虚拟摇杆的移动方向</param>
  55.     public delegate void JoyStickTouchMove(Vector2 vec);

  56.     /// <summary>
  57.     /// 定义触摸结束事件委托
  58.     /// </summary>
  59.     public delegate void JoyStickTouchEnd();

  60.     /// <summary>
  61.     /// 注册触摸开始事件
  62.     /// </summary>
  63.     public event JoyStickTouchBegin OnJoyStickTouchBegin;

  64.     /// <summary>
  65.     /// 注册触摸过程事件
  66.     /// </summary>
  67.     public event JoyStickTouchMove OnJoyStickTouchMove;

  68.     /// <summary>
  69.     /// 注册触摸结束事件
  70.     /// </summary>
  71.     public event JoyStickTouchEnd OnJoyStickTouchEnd;

  72.     void Start ()
  73.     {
  74.         //初始化虚拟摇杆的默认方向
  75.         selfTransform = this.GetComponent<RectTransform>();
  76.         originPosition = selfTransform.anchoredPosition;
  77.     }


  78.     public void OnPointerDown(PointerEventData eventData)
  79.     {
  80.         isTouched = true;
  81.         touchedAxis = GetJoyStickAxis(eventData);
  82.         if(this.OnJoyStickTouchBegin != null)
  83.             this.OnJoyStickTouchBegin(TouchedAxis);
  84.     }

  85.     public void OnPointerUp(PointerEventData eventData)
  86.     {
  87.         isTouched = false;
  88.         selfTransform.anchoredPosition = originPosition;
  89.         touchedAxis = Vector2.zero;
  90.         if(this.OnJoyStickTouchEnd != null)
  91.             this.OnJoyStickTouchEnd();
  92.     }

  93.     public void OnDrag(PointerEventData eventData)
  94.     {
  95.         touchedAxis = GetJoyStickAxis(eventData);
  96.         if(this.OnJoyStickTouchMove != null)
  97.             this.OnJoyStickTouchMove(TouchedAxis);
  98.     }


  99.     void Update()
  100.     {
  101.         //当虚拟摇杆移动到最大半径时摇杆无法拖动
  102.         //为了确保被控制物体可以继续移动
  103.         //在这里手动触发OnJoyStickTouchMove事件
  104.         if(isTouched && touchedAxis.magnitude>=JoyStickRadius)
  105.         {
  106.             if(this.OnJoyStickTouchMove != null)
  107.                 this.OnJoyStickTouchMove(TouchedAxis);
  108.         }

  109.         //松开虚拟摇杆后让虚拟摇杆回到默认位置
  110.         if(selfTransform.anchoredPosition.magnitude > originPosition.magnitude)
  111.             selfTransform.anchoredPosition -= TouchedAxis * Time.deltaTime * JoyStickResetSpeed;
  112.     }

  113.     /// <summary>
  114.     /// 返回虚拟摇杆的偏移量
  115.     /// </summary>
  116.     /// <returns>The joy stick axis.</returns>
  117.     /// <param name="eventData">Event data.</param>
  118.     private Vector2 GetJoyStickAxis(PointerEventData eventData)
  119.     {
  120.         //获取手指位置的世界坐标
  121.         Vector3 worldPosition;
  122.         if (RectTransformUtility.ScreenPointToWorldPointInRectangle (selfTransform,
  123.                  eventData.position, eventData.pressEventCamera, out worldPosition))
  124.             selfTransform.position = worldPosition;
  125.         //获取摇杆的偏移量
  126.         Vector2 touchAxis = selfTransform.anchoredPosition-originPosition;
  127.         //摇杆偏移量限制
  128.         if(touchAxis.magnitude >= JoyStickRadius)
  129.         {
  130.             touchAxis = touchAxis.normalized * JoyStickRadius;
  131.             selfTransform.anchoredPosition = touchAxis;
  132.         }
  133.         return touchAxis;
  134.     }


  135. }
点击此处复制文本

在这段脚本中,我们实现了OnPointerDown、OnPointerUp和OnDrag三个uGUI事件接口,
然后注册了相关的事件委托,这里借鉴了EasyTouch的设计,可以使得虚拟摇杆的逻辑和角色控制逻辑相互分离。

这里的核心方法是GetJoyStickAxis()方法,
通过这个方法我们可以获得一个Vector2类型的值,它表示的是未标准化过的虚拟摇杆的偏移量。

这里的RectTransformUtility.ScreenPointToWorldPointInRectangle()方法表示将一个屏幕坐标转化为对应RectTransform的世界坐标,

RectTransform的anchoredPosition属性表示的是当前元素在场景中的屏幕坐标。

我们知道屏幕坐标是以像素为单位的,因此这里使用屏幕坐标可以计算出虚拟摇杆在水平方向和垂直方向上移动了多少个像素,
我们以此来作为虚拟摇杆的偏移量衡量指标。


TouchedAxis是经过标准化以后的偏移量,我们将把这个值传递到事件委托中以提供给外部来调用。
好了,要说的就这些了,没有说到的大家可以看看代码里的注释或者是在博客中给我留言,就是这样啦。
&#8195;&#8195;接下来,我们在场景中添加一个角色模型来测试我们编写的虚拟摇杆,因为在JoyStick中我们已经定义了事件委托,所以在这里就是简单的调用啦。好了,我们一起来看看代码吧!

  1. /*
  2. * Joystick3D.cs
  3. * 3D模式下的虚拟摇杆测试
  4. * 作者:秦元培
  5. * 博客:[url]http://qinyuanpei.com[/url]
  6. * 时间:2015年10月30日
  7. */

  8. using UnityEngine;
  9. using System.Collections;

  10. public class JoyStick3D : MonoBehaviour
  11. {

  12.     private JoyStick js;
  13.      
  14.     void Start ()
  15.     {
  16.         js = GameObject.FindObjectOfType<JoyStick> ();
  17.         js.OnJoyStickTouchBegin += OnJoyStickBegin;
  18.         js.OnJoyStickTouchMove += OnJoyStickMove;
  19.         js.OnJoyStickTouchEnd += OnJoyStickEnd;
  20.     }
  21.      

  22.     void OnJoyStickBegin(Vector2 vec)
  23.     {
  24.         Debug.Log("开始触摸虚拟摇杆");
  25.     }

  26.     void OnJoyStickMove (Vector2 vec)
  27.     {
  28.         Debug.Log("正在移动虚拟摇杆");

  29.         //设置角色朝向
  30.         Quaternion q = Quaternion.LookRotation (new Vector3 (vec.x, 0, vec.y));
  31.         transform.rotation = q;

  32.         //移动角色并播放奔跑动画
  33.         transform.Translate(Vector3.forward * 75f * Time.deltaTime);
  34.         animation.CrossFade("Run");
  35.     }
  36.      
  37.     void OnJoyStickEnd ()
  38.     {
  39.         Debug.Log("触摸移动摇杆结束");

  40.         //播放默认待机动画
  41.         animation.CrossFade("idle");
  42.     }

  43.     void OnGUI()
  44.     {
  45.         GUI.Label(new Rect(30,30,200,30),"3D模式下的虚拟摇杆测试");
  46.     }
  47. }
点击此处复制文本


最终程序的运行效果如下图所示,我们编写的这个虚拟摇杆可以在手机上完美的运行,欢饮大家来一起测试和吐槽!
2D模式、
20151030145705436.gif
3D模式


20151030150441747.gif

原帖地址:http://qinyuanpei.com/2015/10/30 ... e-virtual-joystick/

2015-11-2 23:25:28  
 赞 赞 1

使用道具 登录

22个回答,把该问题分享到群,邀请大神一起回答。
2#
天下武功出少林,世界资源入元素!
回复 收起回复
2015-11-3 08:28:35   回复
 赞 赞 1

使用道具 登录

4#
元素帖子强,满满正能量!
回复 收起回复
2015-11-7 09:07:50   回复
 赞 赞 1

使用道具 登录

5#
这个可以有,就是程序员不太多啊,看不明白
回复 收起回复
2015-11-16 15:13:28   回复
 赞 赞 1

使用道具 登录

6#
{:1_144:}
回复 收起回复
2015-11-19 15:46:43   回复
 赞 赞 1

使用道具 登录

7#
赞赞赞赞赞赞
回复 收起回复
2015-11-20 21:54:05   回复
 赞 赞 1

使用道具 登录

8#
如果使用easytouch岂不是更方便?
回复 收起回复
2015-11-21 16:27:11   回复
 赞 赞 1

使用道具 登录

9#
我好像来过,感谢楼主分享教程
回复 收起回复
2015-11-23 15:01:56   回复
 赞 赞 1

使用道具 登录

10#
好东西就要收藏
回复 收起回复
2015-11-25 09:40:28   回复
 赞 赞 1

使用道具 登录

11#
看起来很强大,学习了.
回复 收起回复
2015-11-30 01:19:35   回复
 赞 赞 1

使用道具 登录

13#
正好刚刚需要,谢谢大神,赞!!!
回复 收起回复
2015-12-2 17:28:37   回复
 赞 赞 1

使用道具 登录

14#
正好刚刚需要,谢谢大神,赞!!!
回复 收起回复
2015-12-2 17:28:41   回复
 赞 赞 1

使用道具 登录

15#
文能提笔控萝莉
回复 收起回复
2017-2-9 16:04:10   回复
 赞 赞 1

使用道具 登录

16#
此贴甚好,朕已阅,且阅且珍惜!
回复 收起回复
2017-2-15 09:03:43   回复
 赞 赞 1

使用道具 登录

17#
资源发布哪家强?元素首发称大王!
回复 收起回复
2017-2-23 15:47:00   回复
 赞 赞 1

使用道具 登录

18#
回复 收起回复
2017-3-15 10:12:43   回复
 赞 赞 1

使用道具 登录

19#

带你赚币带你飞,元素里面有正妹
回复 收起回复
2017-4-16 09:28:31   回复
 赞 赞 1

使用道具 登录

20#
回复 收起回复
2017-4-20 17:09:56   回复
 赞 赞 1

使用道具 登录

CG 游戏行业专业问题

Unity3D技术手机游戏引擎手游引擎
12下一页
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表