Unity 摇杆功能的 UGUI 实现

本文目标

在 Unity 中使用 UGUI 实现摇杆功能,并将此摇杆模块化以提高其通用性。

软件版本

  • Unity 2018.1.5f1 Personal (64bit)
  • Visual Studio 2017

思路

基于 UnityEngine.UI.ScrollRect 来实现摇块。
ScrollRect 是矩形的,要实现圆形的摇动区域,可通过代码设定摇块可移动半径来实现。当摇块当前位置超出了半径后,不再让摇块向外面的区域移动即可。摇块相对于中心点的位置向量正则化之后可以作为输出的方向向量。

接口

  • 输入:由Unity提供的手势交互事件及相关数据。
  • 输出:方向向量,ForceVector。这是归一化的移动矢量,可以按需要使用。

组件

组件结构:

Canvas
└─JoyStick(Empty)
    ├─ScrollCircle(Empty, +本文创建的ScrollCircle组件)
    │  └─StickImage(UI/Image)
    └─StickBackground(UI/Image)

ScrollCircle 脚本,继承于 UnityEngine.UI.ScrollRect. 挂载到 ScrollCircle 物体上。

逻辑

在 Start 时计算出摇杆区域半径或者直接设置可拖动半径。
响应拖动 OnDrag、结束拖动 OnEndDrag 事件(重载父类的方法)。
先根据滑块大小计算半径,然后在拖动时位置由 ScrollRect 的 OnDrag 函数处理,只在位置超出半径时作特殊处理以限制滑块位置。以 anchoredPosition 正则化后的结果作为输出的方向变量。

OnDrag

在调用基类方法后,先获取摇块相对于 anchor 的当前位置 v
如果 | v | 大于指定的半径 R ,则将摇块位置设置为以半径为模,以 v 的方向为方向的位置向量所在的位置 v | v | R
整个摇杆组件输出的方向向量则为二维的向量,正则化之后提供给其他组件使用。

补充说明

Anchor 和 pivot的概念:锚点(Anchor)、轴心(Pivot)都是 Rect Transform 的参数。这两个都是取值在[0,1]之间,是一个比例值。这个比例值,是根据 Rect Transform 所在 GameObject 的 width 和 height 来进行比例计算的。

  • Anchor: Anchor(锚点)以四个小三角形组成的风扇状图标表示,是相对于父节点的位置。比如 ScrollCircle 的父元素是 Canvas。
  • Pivot: Pivot(轴心)是旋转缩放的中心。(Rotations, size, and scale modifications occur around the pivot)
  • Anchored Postion:相对于 anchor 来设置的 Rect Transform 的位置。

举例解释:

Anchor 与 Pivot 示例

此处 Pivot 是 (0.5, 0.5),则是 x、y 位置的一半,即整个元素的中心点。

OnEndDrag

调用基类方法,使摇块归位。并将方向向量设置为零向量。

代码

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class ScrollCircle : ScrollRect
{
    // 摇杆输出的方向向量
    public Vector2 ForceVector {
        get {
            return m_ForceVector;
        }
        private set { }
    }


    protected override void Start()
    {
        base.Start();
        m_ForceVector = Vector2.zero;
        // mRadius = (transform as RectTransform).sizeDelta.x * 0.5f; // 计算摇杆块的半径
        m_Radius = 50f;
    }

    public override void OnDrag(UnityEngine.EventSystems.PointerEventData eventData)
    {

        base.OnDrag(eventData);
        var contentPostion = this.content.anchoredPosition;
        if (contentPostion.magnitude > m_Radius)
        {
            contentPostion = contentPostion.normalized * m_Radius;
            SetContentAnchoredPosition(contentPostion);
        }
        m_ForceVector = contentPostion.normalized;
        Debug.Log("方向向量:" + m_ForceVector);
    }

    public override void OnEndDrag(PointerEventData eventData)
    {
        base.OnEndDrag(eventData);
        m_ForceVector = Vector2.zero;
    }

    private Vector2 m_ForceVector;
    protected float m_Radius;
}

注解

RectTransform.sizeDelta 用来取 width 的一半作为半径。这段代码如果把 anchor 打散,就失效,因为此时给的值是一个[0, 1]相对值而不是 RectTransform 的大小了。

RectTransform.sizeDelta

The size of this RectTransform relative to the distances between the anchors.

If the anchors are together, sizeDelta is the same as size. If the anchors are in each of the four corners of the parent, the sizeDelta is how much bigger or smaller the rectangle is compared to its parent.

使用注意

控制角色行走时一般是 x, z 平面。使用时注意将摇杆输出的方向向量与场景中模型的方向配置正确。一般把模型朝向 -z 方向,再合理利用方向数值(注意正负号)即可。

示例工程

参考资料

猜你喜欢

转载自blog.csdn.net/u013614126/article/details/81629159