实现思路:
- 利用Scroll Rect组件的Elastic MovementType实现Handle的回弹居中;
- 重写
SetContentAnchoredPosition
函数,限制Handle的拖动范围; - 重写
OnDrag
、OnEndDrag
函数,记录拖动形成的Direction方向; - 添加回调函数,实现摇杆驱动。
Elastic MovementType
添加两个圆形Image,中间的小圆就是摇杆Handle,将其拖拽到Scroll Rect组件中的Content属性中,如图所示:
Movement Type使用默认Elastic类型,该属性帮忙我们可以实现Handle的回弹居中,如图所示:
限制Handle的拖动范围
创建脚本SimpleJoystick
,继承ScrollRect类,通过Override虚函数SetContentAnchoredPosition来限制Handle的可拖拽范围:
using UnityEngine;
using UnityEngine.UI;
public class SimpleJoystick : ScrollRect
{
protected override void SetContentAnchoredPosition(Vector2 position)
{
//base.SetContentAnchoredPosition(position);
if (position != content.anchoredPosition)
{
//大圆的半径
float radius = (transform as RectTransform).rect.height * .5f;
//限制拖拽范围在半径内
content.anchoredPosition = position.magnitude <= radius ? position : position.normalized * radius;
UpdateBounds();
}
}
}
记录拖拽形成的方向
声明Vecotor3类型变量,记录拖拽形成的方向,在拖拽过程中记录,在结束拖拽时重置为Vector3.Zero:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class SimpleJoystick : ScrollRect
{
private Vector3 direction;
protected override void SetContentAnchoredPosition(Vector2 position)
{
//base.SetContentAnchoredPosition(position);
if (position != content.anchoredPosition)
{
float radius = (transform as RectTransform).rect.height * .5f;
content.anchoredPosition = position.magnitude <= radius ? position : position.normalized * radius;
UpdateBounds();
}
}
public override void OnDrag(PointerEventData eventData)
{
base.OnDrag(eventData);
direction = new Vector3(content.anchoredPosition.x, 0f, content.anchoredPosition.y);
}
public override void OnEndDrag(PointerEventData eventData)
{
base.OnEndDrag(eventData);
direction = Vector3.zero;
}
}
添加回调函数
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
public class SimpleJoystick : ScrollRect
{
public UnityAction<Vector3> onDrag;
private Vector3 direction;
protected override void SetContentAnchoredPosition(Vector2 position)
{
//base.SetContentAnchoredPosition(position);
if (position != content.anchoredPosition)
{
float radius = (transform as RectTransform).rect.height * .5f;
content.anchoredPosition = position.magnitude <= radius ? position : position.normalized * radius;
UpdateBounds();
}
}
public override void OnDrag(PointerEventData eventData)
{
base.OnDrag(eventData);
direction = new Vector3(content.anchoredPosition.x, 0f, content.anchoredPosition.y);
}
public override void OnEndDrag(PointerEventData eventData)
{
base.OnEndDrag(eventData);
direction = Vector3.zero;
}
private void Update()
{
if (content.anchoredPosition != Vector2.zero)
{
onDrag?.Invoke(direction);
}
}
}
Example示例:
using UnityEngine;
public class Example : MonoBehaviour
{
[SerializeField] private Rigidbody rb;
[SerializeField] private SimpleJoystick joystick;
private void Start()
{
joystick.onDrag += OnJoystickDrag;
}
private void OnJoystickDrag(Vector3 direction)
{
rb.velocity += direction.normalized * Time.deltaTime * 15f;
if (direction != Vector3.zero)
{
rb.transform.rotation = Quaternion.LookRotation(direction);
}
}
}