Unity实现的汽车方向盘转动效果[完整案例]

说明:

这是一个UI界面中操作方向盘的演示,源于一个游戏项目,仅供学习参考,其中:
1、方向盘可复原
2、Android测试能够正常操作,没有出现轴心偏离现象(测试机型Galaxy S8-分辨率:2960x1440)

注意:

如果你要使用这个脚本,应将Rect Transfrom设置成如下属性,当然,某些属性不是必须要这么设定,但至少这样做可以正常工作,作为一个案例,供参考!

1、Rect Transfrom相关属性设定
属性

2、其画布下的SteerWheel是Image类型的UI元素,图像设定为方向盘图片
方向盘
3、Game
Game属性
4、Inspector
面板

5、演示结果
测试效果

代码:

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

/// <summary>
/// Steering wheel.
/// Defines the steerign wheel behaviour.
/// Original source:
/// http://forum.unity3d.com/threads/touchscreen-steering-wheel-rotation-example-mouse-supported.196741/
/// </summary>
public class SteeringWheel2D : MonoBehaviour
{
    [Tooltip("UI_Element通常它是一个Image类型的图像")]
    [Header("方向盘UI本体")]
    public Graphic UI_Element;
    [Header("方向盘最大旋转角度")]
    public float maximumSteeringAngle = 520f;
    [Header("方向盘旋转复原速度")]
    public float wheelReleasedSpeed = 700f;
    [Header("方向盘当前旋转角度")]
    public float wheelAngle = 0f;
    float wheelPrevAngle = 0f;
    bool wheelBeingHeld = false;
    RectTransform rectT;
    
    [Tooltip("centerPoint是方向盘旋转的中心,其中你应将设定UI_Element的Img图片")]
    [Header("方向盘旋转中心点")]
    public Vector2 centerPoint;

    /// <summary>
    /// Gets the clamped value.
    /// Returns a value in range [-1,1] similar to GetAxis("Horizontal")
    /// 返回旋转进度比
    /// </summary>
    /// <returns>The clamped value.</returns>
    public float GetClampedValue()
    {

        return wheelAngle / maximumSteeringAngle;
    }

    /// <summary>
    /// Gets the angle.
    /// returns the wheel angle itself without clamp operation
    /// 获取实际旋转角度(-520°~520°)
    /// </summary>
    /// <returns>The angle.</returns>
    public float GetAngle()
    {
        return wheelAngle;
    }

    /// <summary>
    /// Use this for initialization.
    /// </summary>
    void Start()
    {
        rectT = UI_Element.rectTransform;
        InitEventsSystem();
        UpdateRect();
    }

    /// <summary>
    /// Initializes the events system to catch
    /// the user movements.i.e. MouseDown/MouseDrag/MouseUp.
    /// </summary>
    void InitEventsSystem()
    {
        EventTrigger events = UI_Element.gameObject.GetComponent<EventTrigger>();

        if (events == null)
            events = UI_Element.gameObject.AddComponent<EventTrigger>();

        if (events.triggers == null)
            events.triggers = new System.Collections.Generic.List<EventTrigger.Entry>();

        EventTrigger.Entry entry = new EventTrigger.Entry();
        EventTrigger.TriggerEvent callback = new EventTrigger.TriggerEvent();
        UnityAction<BaseEventData> functionCall = new UnityAction<BaseEventData>(PressEvent);
        callback.AddListener(functionCall);
        entry.eventID = EventTriggerType.PointerDown;
        entry.callback = callback;

        events.triggers.Add(entry);

        entry = new EventTrigger.Entry();
        callback = new EventTrigger.TriggerEvent();
        functionCall = new UnityAction<BaseEventData>(DragEvent);
        callback.AddListener(functionCall);
        entry.eventID = EventTriggerType.Drag;
        entry.callback = callback;

        events.triggers.Add(entry);

        entry = new EventTrigger.Entry();
        callback = new EventTrigger.TriggerEvent();
        functionCall = new UnityAction<BaseEventData>(ReleaseEvent);
        callback.AddListener(functionCall);
        entry.eventID = EventTriggerType.PointerUp;
        entry.callback = callback;

        events.triggers.Add(entry);
    }

    /// <summary>
    /// Initializes the center point of seetring wheel transform
    /// rectangle.
    /// 初始化方向盘变换矩形的旋转中心点。
    /// </summary>
    void UpdateRect()
    {
        Vector3[] corners = new Vector3[4];
        rectT.GetWorldCorners(corners);
        for (int i = 0; i < 4; i++)
        {
            corners[i] = RectTransformUtility.WorldToScreenPoint(null, corners[i]);
        }
        Vector3 bottomLeft = corners[0];
        Vector3 topRight = corners[2];
        float width = topRight.x - bottomLeft.x;
        float height = topRight.y - bottomLeft.y;
        Rect _rect = new Rect(bottomLeft.x, topRight.y, width, height);
        centerPoint = new Vector2(_rect.x + _rect.width * 0.5f, _rect.y - _rect.height * 0.5f);
    }

    /// <summary>
    /// Update is called once per frame.
    /// Update the wheel status. If the 
    /// wheel is released, reset the rotation
    /// to initial (zero) rotation by 
    /// wheelReleasedSpeed degrees per second
    /// </summary>
    void Update()
    {
        if (!wheelBeingHeld && !Mathf.Approximately(0f, wheelAngle))
        {
            float deltaAngle = wheelReleasedSpeed * Time.deltaTime;
            if (Mathf.Abs(deltaAngle) > Mathf.Abs(wheelAngle))
                wheelAngle = 0f;
            else if (wheelAngle > 0f)
                wheelAngle -= deltaAngle;
            else
                wheelAngle += deltaAngle;
        }
        rectT.localEulerAngles = Vector3.back * wheelAngle;
    }

    /// <summary>
    /// Presses the event.
    /// Executed when mouse/finger starts touching the steering wheel.
    /// </summary>
    /// <param name="eventData">Event data.</param>
    public void PressEvent(BaseEventData eventData)
    {
        Vector2 pointerPos = ((PointerEventData)eventData).position;
        wheelBeingHeld = true;
        wheelPrevAngle = Vector2.Angle(Vector2.up, pointerPos - centerPoint);
    }

    /// <summary>
    /// Drags the event.
    /// Executed when mouse/finger is dragged over the steering wheel.
    /// Do nothing if the pointer is too close to the center of the wheel.
    /// Make sure wheel angle never exceeds maximumSteeringAngle.
    /// </summary>
    /// <param name="eventData">Event data.</param>
    public void DragEvent(BaseEventData eventData)
    {

        Vector2 pointerPos = ((PointerEventData)eventData).position;

        float wheelNewAngle = Vector2.Angle(Vector2.up, pointerPos - centerPoint);

        if (Vector2.Distance(pointerPos, centerPoint) > 20f)
        {
            if (pointerPos.x > centerPoint.x)
                wheelAngle += wheelNewAngle - wheelPrevAngle;
            else
                wheelAngle -= wheelNewAngle - wheelPrevAngle;
        }

        wheelAngle = Mathf.Clamp(wheelAngle, -maximumSteeringAngle, maximumSteeringAngle);
        wheelPrevAngle = wheelNewAngle;
    }

    /// <summary>
    /// Executed when mouse/finger stops touching the steering wheel
    /// Performs one last DragEvent, just in case
    /// </summary>
    /// <param name="eventData">Event data.</param>
    public void ReleaseEvent(BaseEventData eventData)
    {
        DragEvent(eventData);
        wheelBeingHeld = false;
    }
}

—END—