Unity InputSystem 摇杆及扩展

零.参考资料

1.视频

基于Unity New Input System实现的类似moba游戏的虚拟摇杆

2.文章

官方文档
新输入系统InputSystem简介(一)
新输入系统InputSystem简介(二)

一.基本步骤

注1:参考资料视频中有比较详细的介绍,以下步骤仅记录简要步骤。
注2:以下步骤以安装InputSystem插件并完成工程配置为前提,关于插件安装,“参考资料” - “文章”中有详细介绍。

1.配置InputAction文件

1.1创建InputAction文件

(Project窗口中)右键CreateInputActions
文件图示

1.2配置InputAction文件

在这里插入图片描述

1.3生成InputAction类文件

生成InputAction类

2.挂载OnSenceSticks脚本

挂载OnSenceSticks脚本

3.绑定事件

截取控制移动关键代码

public class PlayerController : MonoBehaviour
{
    //前文通过InputAction生成的类
    private MyInputAction inputActions;
    void Start()
    {
        inputActions = new MyInputAction();

        inputActions.Enable();
        inputActions.Player.Move.performed += Player_Move_Performed;
        inputActions.Player.Move.canceled += Player_Move_Canceled;

    }
    private void FixedUpdate()
    {
        //弧度转角度
        float angle = Mathf.Atan2(aimDir.y, aimDir.x) * 180 / Mathf.PI;
        Quaternion targetRot = Quaternion.Euler(new Vector3(0, 0, angle - 90));
        playerModel.rotation = Quaternion.Lerp(playerModel.rotation, targetRot, rotationSpeed * Time.deltaTime);
        rigidbody2D.AddForce(moveDir.normalized * moveForceSpeed, ForceMode2D.Impulse);
    }

    private void Player_Move_Performed(InputAction.CallbackContext context)
    {
        moveDir = context.ReadValue<Vector2>();
    }
    private void Player_Move_Canceled(InputAction.CallbackContext context)
    {
        moveDir = Vector2.zero;
    }
}

x.效果

请添加图片描述

二.扩展

基于OnSenceSticks脚本进行功能扩展。

1.实现的效果:

在这里插入图片描述

未接触屏幕时接触屏幕时移动时
隐藏/显示摇杆显示,摇杆背景固定在默认位置摇杆背景固定在默认位置,摇杆跟随触摸位置或者触摸方向
隐藏/显示摇杆显示,摇杆背景出现在触摸位置摇杆背景固定在触摸位置,摇杆跟随触摸位置或者触摸方向
隐藏/显示摇杆显示, 摇杆背景出现在触摸位置摇杆背景和摇杆均跟随触摸位置

2.思路&概括

1.多根手指触摸屏幕时,将摇杆的控制权分配给在点击区域范围内的第一根手指产生的Touch。
2.不想每一帧都去检测Touch状态,因此通过预先放置点击区域的UI和OnPointerDown实现只有在触摸屏幕时进行状态检测。

3.代码实现

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.EnhancedTouch;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.OnScreen;
using UnityEngine.Serialization;

[AddComponentMenu("Input/虚拟摇杆 - 新输入系统(InputSystem)")]
public class JoyStick_InputSystem : OnScreenControl, IPointerDownHandler
{
    enum JoyStickType
    {
        [InspectorName("固定在默认位置")]
        FixedInDefaultPosition = 0,
        [InspectorName("固定在触摸位置")]
        FixedInClickPosition = 1,
        [InspectorName("跟随触摸移动")]
        Flexible = 2
    }

    [Header("基础设置")]

    [Tooltip("摇杆背景")]
    [SerializeField]
    private RectTransform joyStickBackGround;

    [Tooltip("摇杆按钮")]
    [SerializeField]
    private RectTransform joyStickButton;

    [Tooltip("摇杆按钮移动范围(半径)")]
    [FormerlySerializedAs("movementRange")]
    [SerializeField]
    private float movementRange = 50;

    [Tooltip("控制设备路径")]
    [InputControl(layout = "Vector2")]
    [SerializeField]
    private new string controlPath;

    [Header("类型设置")]

    [Tooltip("摇杆类型")]
    [SerializeField]
    private JoyStickType joyStickType;

    [Tooltip("摇杆是否默认隐藏")]
    [SerializeField]
    private bool isHideWithoutTouch;

    //触摸屏(设备)
    private Touchscreen currentTouchScreen;
    //控制摇杆的触点对象
    private TouchControl joyStickTouch;
    //摇杆初始位置
    private Vector2 joyStickStartPos;
    //与屏幕相关的相机(主要用于将屏幕上的位置转换为某UI矩形内的相对位置)
    private Camera screenCamera;
    //模拟(鼠标或者笔)输入
    private TouchSimulation touchSimulation;
    //画布渲染模式
    private RenderMode renderMode;
    //点击区域UI的RectTransform
    private RectTransform rectTransform;

    protected override string controlPathInternal
    {
        get => controlPath;
        set => controlPath = value;
    }

    private void Awake()
    {
#if UNITY_EDITOR
        if (!gameObject.TryGetComponent(out touchSimulation))
        {
            touchSimulation = gameObject.AddComponent<TouchSimulation>();
        }
#endif
    }

    private void Start()
    {
        currentTouchScreen = Touchscreen.current;
        //记录摇杆初始位置
        joyStickStartPos = joyStickBackGround.anchoredPosition;
        if (isHideWithoutTouch)
        {
            joyStickBackGround.gameObject.SetActive(false);
        }
        //获取画布的渲染模式
        var canvas = transform.GetComponentInParent<Canvas>();
        renderMode = canvas != null ? canvas.renderMode : RenderMode.ScreenSpaceOverlay;
        //获取点击区域UI的RectTransform
        rectTransform = transform.GetComponent<RectTransform>();
    }

    private void Update()
    {
        UpdateJoyStickUIPos();
    }

    private void OnDestroy()
    {
#if UNITY_EDITOR
        touchSimulation = null;
#endif
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        //获取与屏幕相关的相机
        if (renderMode != RenderMode.ScreenSpaceOverlay)
        {
            screenCamera = eventData.pressEventCamera;
        }
        //摇杆没有被控制时分配控制权
        if (joyStickTouch == null)
        {
            for (int i = 0; i < currentTouchScreen.touches.Count; i++)
            {
                var touch = currentTouchScreen.touches[i];
                if (touch.phase.ReadValue() == UnityEngine.InputSystem.TouchPhase.Began)
                {
                    if (RectTransformUtility.RectangleContainsScreenPoint(rectTransform, touch.position.ReadValue(), screenCamera))
                    {
                        joyStickTouch = touch;
                        RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, joyStickTouch.position.ReadValue(), screenCamera, out var joyStickBgPos);
                        SetJoyStickUI(joyStickBgPos, Vector2.zero, true);
                    }
                }
            }
        }
    }

    private void UpdateJoyStickUIPos()
    {
        if (joyStickTouch == null)
            return;
        bool isActive = true;
        Vector2 joyStickBgPos = Vector2.zero;
        Vector2 joyStickBtnPos = Vector2.zero;
        Vector2 sendToControlValue = Vector2.zero;
        switch (joyStickTouch.phase.ReadValue())
        {
            case UnityEngine.InputSystem.TouchPhase.None:
                break;
            case UnityEngine.InputSystem.TouchPhase.Began:
            case UnityEngine.InputSystem.TouchPhase.Moved:
                //更新摇杆位置数据
                RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, joyStickTouch.position.ReadValue(), screenCamera, out var touchInRectPos);
                Vector2 delta = touchInRectPos - joyStickBackGround.anchoredPosition;
                joyStickBgPos = joyStickBackGround.anchoredPosition;
                if (joyStickType == JoyStickType.Flexible)
                {
                    if (delta.magnitude > movementRange)
                    {
                        joyStickBgPos += delta.normalized * (delta.magnitude - movementRange);
                    }
                }
                joyStickBtnPos = Vector2.ClampMagnitude(delta, movementRange);
                isActive = true;
                sendToControlValue = delta / movementRange;
                break;
            case UnityEngine.InputSystem.TouchPhase.Ended:
            case UnityEngine.InputSystem.TouchPhase.Canceled:
                //收回Touch对摇杆的控制权
                joyStickTouch = null;
                joyStickBgPos = joyStickStartPos;
                isActive = false;
                break;
            case UnityEngine.InputSystem.TouchPhase.Stationary:
                break;
        }
        //设置摇杆位置
        SetJoyStickUI(joyStickBgPos, joyStickBtnPos, isActive);
        //给控制器发送新位置
        SendValueToControl(sendToControlValue);
    }

    private void SetJoyStickUI(Vector2 joyStickBgPos, Vector2 joyStickBtnPos, bool isActive)
    {
        //设置joyStickBackGround位置
        switch (joyStickType)
        {
            case JoyStickType.FixedInDefaultPosition:
                break;
            case JoyStickType.FixedInClickPosition:
            case JoyStickType.Flexible:
                joyStickBackGround.anchoredPosition = joyStickBgPos;
                break;
        }
        //设置joyStickButton位置
        joyStickButton.anchoredPosition = joyStickBtnPos;
        //设置
        if (isHideWithoutTouch && joyStickBackGround.gameObject.activeSelf != isActive)
        {
            joyStickBackGround.gameObject.SetActive(isActive);
        }
    }

}


4.使用

在这里插入图片描述
之后再按照“基本步骤”中的“绑定事件”就可以了。
本人新手,文章本是个人记录,仅供参考,如有不足和错误,欢迎指正。

### 如何在 Unity 中获取键盘输入 #### 使用 `Input` 类检测特定键的按下状态 对于简单的键盘输入检测,可以直接利用 Unity 的内置 `Input` 类来判断某个按键是否被按下: ```csharp void Update() { if (Input.GetKeyDown(KeyCode.Space)) { Debug.Log("Space key was pressed"); } } ``` 这段代码会在每次更新循环中检查空格键(`KeyCode.Space`) 是否刚刚被按下。如果是,则会打印一条消息到控制台[^1]。 #### 利用字符串名称匹配键位 除了使用枚举类型的 `KeyCode` 来表示具体按键外,还可以通过传递相应的字符串名给 `GetKey()` 方法来进行更灵活的操作: ```csharp if (Input.GetKey("w")) { Debug.Log("W key is being held down."); } if (Input.GetKeyUp("s")) { Debug.Log("S key has been released."); } ``` 这里展示了如何捕捉持续按住以及释放指定字符键的情况[^3]。 #### 获取轴向输入用于角色移动等场景 针对需要连续变化的方向性输入(如玩家操控的角色行走),通常建议采用 `GetAxis()` 函数配合预定义好的虚拟轴配置文件一起工作: ```csharp float horizontalMovement = Input.GetAxis("Horizontal"); // A/D 或 左右箭头 float verticalMovement = Input.GetAxis("Vertical"); // W/S 或 上下箭头 Debug.Log($"Moving Horizontally: {horizontalMovement}, Vertically: {verticalMovement}"); ``` 此方法能够提供平滑过渡的效果,并支持摇杆设备作为替代输入源。 #### 新版 Input System API 示例 考虑到未来兼容性和功能扩展的需求,在较新的项目里可能更适合选用新版的 Input System 插件及其提供的接口实现相同的功能: ```csharp using UnityEngine; using UnityEngine.InputSystem; public class PlayerController : MonoBehaviour { private void OnMove(InputValue value) { Vector2 movementVector = value.Get<Vector2>(); Debug.Log($"Player moving with vector ({movementVector.x},{movementVector.y})"); } public void Start() { Controls.Player.Move.performed += ctx => OnMove(ctx); } private void OnDestroy() { Controls.Player.Move.performed -= ctx => OnMove(ctx); } } ``` 上述例子假设已经设置好了自定义的动作映射方案,并注册了对应的回调函数去响应用户的交互行为[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值