木木的Unity学习笔记(二)—— 利用UGUI ScrollRec组件制作手游摇杆控件
今天和道友聊天的时候听说某大厂做手游摇杆是用UGUI实现的,想想自己以前做手游的时候都是用的Easy Tocuh插件,于是很好奇是如何实现的。于是在大佬的协助下缕清了思路,如何用UGUI的ScrollRect组件制作手游的摇杆控件。
首先创建一个空项目,在场景内创建一个Scroll View并移动至Canvas的左下角:
我是将Scroll View的锚点定在Canvas的左下角并给PosX和PosY均赋值为150,如下图:
选中Scroll View,在Inspector面板找到Scroll Rect组件,将Horizontal Scrollbar和Vertical 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);
}
}
}