木木的Unity学习笔记(二)—— 利用UGUI ScrollRect组件制作手游摇杆控件

木木的Unity学习笔记(二)—— 利用UGUI ScrollRec组件制作手游摇杆控件

今天和道友聊天的时候听说某大厂做手游摇杆是用UGUI实现的,想想自己以前做手游的时候都是用的Easy Tocuh插件,于是很好奇是如何实现的。于是在大佬的协助下缕清了思路,如何用UGUI的ScrollRect组件制作手游的摇杆控件。

首先创建一个空项目,在场景内创建一个Scroll View并移动至Canvas的左下角:

我是将Scroll View的锚点定在Canvas的左下角并给PosX和PosY均赋值为150,如下图:

选中Scroll View,在Inspector面板找到Scroll Rect组件,将Horizontal ScrollbarVertical Scrollbar下面的Visiblity均设置为Auto Hide

选中Viewport,将锚点设置在左上角,给PosX和PosY赋值为0,给Width和Height赋值为200(和Scroll Rect的Width、Height相同),再选中Content,重复操作


顺带一提,我还将右边和下边的拖拽条隐藏掉并且不接收射线,并且去掉了Viewport的Mask组件,具体操作各位道友们可以自行实现。

创建脚本,我命名为MobileRockerController,拖拽脚本给Content,现添加代码如下:

// MobileRockerController.cs written by Fumiki at 2018-05-04
using UnityEngine;
using UnityEngine.UI;

namespace Com.MobileRocker.Fumiki
{
    public class MobileRockerController : MonoBehaviour
    {
        /// <summary>
        /// 手机摇杆
        /// </summary>
        [SerializeField] private RectTransform rocker;

        private void Awake()
        {
            if (rocker == null)
                rocker = gameObject.GetComponent<RectTransform>();

        private void Update()
        {
            Debug.Log(rocker.anchoredPosition);
        }
    }
}

这时已经可以看到有手柄的效果了,Console输出的就是拖拽Content后Update方法中反馈回来的Content的二维锚点坐标值:

为了美化,我还顺手设置了一下Scroll View的Image组件的颜色,也给Content添加了一个Image组件,使用了UGUI的圆形贴图并设置了一下颜色(暂时先不上图了,有点Low)。

删除Content上的MobileRockerController脚本,并将脚本重新绑定给Scroll View,改写脚本如下:

using System;
using UnityEngine;
using UnityEngine.UI;

namespace Com.MobileRocker.Fumiki
{
    public class MobileRockerController : MonoBehaviour
    {
        #region 将手机摇杆类设置为单例类
        private MobileRockerController() { }
        private MobileRockerController(ref MobileRockerController reference) { }
        private static MobileRockerController instance = null;
        public static MobileRockerController Instance
        {
            get { return instance; }
        }
        #endregion

        /// <summary>
        /// 手机摇杆基座
        /// </summary>
        [SerializeField] private RectTransform rockerBase = null;

        /// <summary>
        /// 手机摇杆
        /// </summary>
        [SerializeField] private RectTransform rocker = null;

        /// <summary>
        /// 手机摇杆横纵偏移量
        /// </summary>
        [SerializeField] private Vector2 rockerOffset = Vector2.zero;

        /// <summary>
        /// 外部获取手机摇杆横纵偏移量的接口属性[只读]
        /// </summary>
        public Vector2 RockerOffset
        {
            get { return rockerOffset; }
        }

        private void Awake()
        {
            if (instance == null)
                instance = this;
            if (rockerBase == null)
                rockerBase = gameObject.GetComponent<RectTransform>();
            if (rocker == null)
                rocker = gameObject.GetComponent<ScrollRect>().content;
        }

        private void Update()
        {
            SetAnchoredPosToRockerOffset();
        }


        /// <summary>
        /// 将摇杆的锚点坐标转换为偏移量
        /// </summary>
        private void SetAnchoredPosToRockerOffset()
        {
            // 分别获取摇杆基座宽高的一般用以后面的判断
            float halfBaseX = rockerBase.sizeDelta.x / 2.0f;
            float halfBaseY = rockerBase.sizeDelta.y / 2.0f;
            // 判断以设定边界
            if (rocker.anchoredPosition.x > halfBaseX)
            {
                if (rocker.anchoredPosition.y > halfBaseY)
                    rockerOffset = new Vector2(1.0f, 1.0f);
                else if (rocker.anchoredPosition.y < -halfBaseY)
                    rockerOffset = new Vector2(1.0f, -1.0f);
                else
                    rockerOffset = new Vector2(1.0f, rocker.anchoredPosition.y / 100.0f);
            }
            else if (rocker.anchoredPosition.x < -halfBaseX)
            {
                if (rocker.anchoredPosition.y > halfBaseY)
                    rockerOffset = new Vector2(-1.0f, 1.0f);
                else if (rocker.anchoredPosition.y < -halfBaseY)
                    rockerOffset = new Vector2(-1.0f, -1.0f);
                else
                    rockerOffset = new Vector2(-1.0f, rocker.anchoredPosition.y / 100.0f);
            }
            else
            {
                if (rocker.anchoredPosition.y > halfBaseY)
                    rockerOffset = new Vector2(rocker.anchoredPosition.x / 100.0f, 1.0f);
                else if (rocker.anchoredPosition.y < -halfBaseY)
                    rockerOffset = new Vector2(rocker.anchoredPosition.x / 100.0f, -1.0f);
                else
                    rockerOffset = new Vector2(rocker.anchoredPosition.x / 100.0f, rocker.anchoredPosition.y / 100.0f);
            }
            // 设定偏移量的精度
            rockerOffset = new Vector2(Convert.ToSingle(Math.Round(rockerOffset.x, 2)), 
                                       Convert.ToSingle(Math.Round(rockerOffset.y, 2)));
        } 
    }
}

这样其他需要和摇杆挂钩的GameObject就可以调用MobileRockerController.Instance.RockerOffset获取手柄的偏移量并根据自己的需求编写逻辑了。
最后上一张效果图,庆祝一下我第一次用UGUI做出的简易手柄:

简单介绍一下,这是用通过手柄的偏移量给小球增加扭力使小球滚动的一个小demo,虽然粗糙了一些,不过证明了手柄确实可用。
顺带附上没有注释的小球脚本(小球绑定了刚体):

using UnityEngine;

namespace Com.MobileRocker.Fumiki
{
    public class PlayerController : MonoBehaviour
    {
        private Vector3 forceOffset = Vector3.zero;

        void Update()
        {
            forceOffset = new Vector3(MobileRockerController.Instance.RockerOffset.y, 0, 
                -MobileRockerController.Instance.RockerOffset.x);
            gameObject.GetComponent<Rigidbody>().AddTorque(forceOffset * 50, ForceMode.Force);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/fumikisama/article/details/80200382