(2025)Unity四分之一圆弧JoyStick摇杆原生UGUI实现

效果如下图:

控件核心组成

        1.RotateStick:空物体,挂载核心脚本 ;

        2.Handle:摇杆,使用圆形图片;

        3.RotateCenter:空物体,位置必须在摇杆旋转中心点;

        4.HandleCenter:空物体,位置必须在Handle中心点。

核心代码 

 代码如下(在项目Assets文件夹内新建C#脚本后复制以下代码粘贴到新建的脚本内,挂载到“RotateStick”上即可。)。

通过读取实例中的rotateAngle属性获取摇杆旋转角度,以实现其他控制操作。

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class RotateStick : MonoBehaviour
{
    public Transform centerTransform; // 指定的圆心Transform
    public float innerRadius = 50f; // 内圆半径
    public float outerRadius = 100f; // 外圆半径
    public Vector2 value;
    public Vector3 nowControlPos;
    RectTransform bar;
    RectTransform range;
    public Transform center;
    public static int bindFingerIndex = -1;
    public  float rotateAngle;
    void OnDisable()
    {
        if (center != null)
        {
            bindFingerIndex = -1;
            nowControlPos = center.position;
            bar.position = center.position;
        }
    }

    void Start()
    {
        this.transform.SetAsFirstSibling();
        range = transform.GetChild(0) as RectTransform;
        bar = range.GetChild(0) as RectTransform;
        //bar.localPosition = Vector3.zero;
        bar.position = center.position;
        Image rangeImage = range.GetComponent<Image>();
        rangeImage.alphaHitTestMinimumThreshold = 0.1f;
        void OnBeginDrag(PointerEventData peDate)
        {
            bindFingerIndex = Input.touchCount - 1;
            Vector2 aimPos = peDate.position;
            nowControlPos = aimPos;
            Vector3 constrainedPosition = ConstrainToQuarterRing(aimPos);
            bar.position = constrainedPosition;
            
        }

        void OnDrag(PointerEventData peDate)
        {
            Vector2 aimPos = peDate.position;
            nowControlPos = aimPos;
            Vector3 constrainedPosition = ConstrainToQuarterRing(aimPos);
            Debug.Log(constrainedPosition);
            //if (constrainedPosition.y < 332)
            //{
            //    constrainedPosition.y = 332;
            //}
            bar.position = constrainedPosition;
        }

        void OnEndDrag(PointerEventData peDate)
        {
            bindFingerIndex = -1;
            nowControlPos = center.position;
            bar.position = center.position;
        }

        SetTriggerEvent(rangeImage, EventTriggerType.BeginDrag, (bed) =>
        {
            OnBeginDrag(bed as PointerEventData);
        });

        SetTriggerEvent(rangeImage, EventTriggerType.PointerDown, (bed) =>
        {
            OnBeginDrag(bed as PointerEventData);
        });

        SetTriggerEvent(rangeImage, EventTriggerType.Drag, (bed) =>
        {
            OnDrag(bed as PointerEventData);
        });

        SetTriggerEvent(rangeImage, EventTriggerType.EndDrag, (bed) =>
        {
            OnEndDrag(bed as PointerEventData);
        });
    }

    void Update()
    {
        Vector3 dir = (bar.position - centerTransform.position);
        dir.x /= outerRadius;
        dir.y /= outerRadius; // Normalize both x and y
        value = dir;

        if (Input.touchCount == 0 && !Input.GetMouseButton(0))
        {
            if (bar.localPosition != Vector3.zero)
            {
                bindFingerIndex = -1;
                nowControlPos = center.position;
                bar.position = center.position;
                rotateAngle = 0;
            }
        }
    }

    private Vector3 ConstrainToQuarterRing(Vector2 position)
    {
        Vector3 dir = position - (Vector2)centerTransform.position;
        float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
        float distance = dir.magnitude;
        if (angle < 0) angle = 180;
        if (angle < 90) angle = 90;
        Debug.Log(angle-135);
        rotateAngle = -(angle - 135)/45f;
        distance = Mathf.Clamp(distance, innerRadius, outerRadius);
        float radian = angle * Mathf.Deg2Rad;
        return new Vector3(Mathf.Cos(radian) * distance, Mathf.Sin(radian) * distance, 0) + centerTransform.position;
    }

    private void SetTriggerEvent(MaskableGraphic ui, EventTriggerType type, UnityAction<BaseEventData> doSth)
    {
        EventTrigger eventTrigger = ui.gameObject.GetComponent<EventTrigger>();
        if (eventTrigger == null)
        {
            eventTrigger = ui.gameObject.AddComponent<EventTrigger>();
        }
        EventTrigger.Entry entry = new EventTrigger.Entry();
        entry.eventID = type;
        UnityAction<BaseEventData> callback = doSth;
        entry.callback.AddListener(callback);
        eventTrigger.triggers.Add(entry);
    }
}

脚本配置界面如下

 CenterTransform:摇杆旋转中心点;

Inner/Outer Radius:摇杆移动范围;

Value:当前摇杆XY轴的值(用于精细控制);

Center:摇杆中心点(用于EndDrag时摇杆位置归零);

RotateAngle:摇杆最终角度,用于外部读取摇杆状态;

注意事项

BackGround的Raycast Target属性,勾选上Stick可以滑动和点击,取消勾选则Stick只能点击不能滑动,根据需要自行修改。