Unity单手轮盘控制2D/3D物体移动

玩家控制脚本:PlayerController.cs

csharp 复制代码
using UnityEngine;

/// <summary>
/// 通用角色移动控制器 - 挂载到【2D精灵】或【3D胶囊体/物体】上
/// 一键切换2D/3D模式,自动适配刚体组件,摇杆控制移动
/// </summary>
public class PlayerController : MonoBehaviour
{
    [Header("===== 通用移动配置 =====")]
    [Tooltip("移动速度,2D/3D通用,数值越大越快")]
    public float moveSpeed = 8f;

    [Header("===== 模式切换【一键切换,不用改代码】 =====")]
    [Tooltip("勾选 = 2D模式(精灵移动) | 取消勾选 = 3D模式(胶囊体移动)")]
    public bool is2DMode = false;

    // 组件缓存
    private Rigidbody rb3D;       // 3D刚体组件
    private Rigidbody2D rb2D;     // 2D刚体组件

    private void Awake()
    {
        // 自动识别并获取对应刚体组件,无需手动赋值
        if (is2DMode)
        {
            rb2D = GetComponent<Rigidbody2D>();
        }
        else
        {
            rb3D = GetComponent<Rigidbody>();
        }
    }

    // 物理移动固定写在FixedUpdate,帧率稳定无抖动,2D/3D通用
    private void FixedUpdate()
    {
        if (is2DMode)
        {
            MovePlayer2D(); // 2D精灵移动逻辑
        }
        else
        {
            MovePlayer3D(); // 3D物体移动逻辑
        }
    }

    /// <summary>
    /// 3D物体移动逻辑 - 胶囊体/立方体等,X-Z平面水平移动,Y轴高度不变
    /// </summary>
    private void MovePlayer3D()
    {
        if (rb3D == null) return;
        
        // 摇杆方向转3D世界坐标:X→X轴,Y→Z轴,Y轴=0 不浮空不下陷
        Vector3 moveVec = new Vector3(UIController.moveDirection.x, 0, UIController.moveDirection.y);
        // 平滑移动,归一化保证斜向速度一致
        Vector3 targetPos = transform.position + moveVec.normalized * moveSpeed * Time.fixedDeltaTime;
        rb3D.MovePosition(targetPos);
    }

    /// <summary>
    /// 2D精灵移动逻辑 - Sprite,X-Y平面移动,2D游戏标准逻辑
    /// </summary>
    private void MovePlayer2D()
    {
        if (rb2D == null) return;
        
        // 摇杆方向直接转2D世界坐标:X→X轴,Y→Y轴,完美适配2D平面
        Vector2 moveVec = UIController.moveDirection;
        // 平滑移动,归一化保证斜向速度一致
        Vector2 targetPos = (Vector2)transform.position + moveVec.normalized * moveSpeed * Time.fixedDeltaTime;
        rb2D.MovePosition(targetPos);
    }
}

轮盘控制脚本:UIController

csharp 复制代码
using UnityEngine;

public class UIController : MonoBehaviour
{
    [Header("摇杆对象 → 拖拽赋值即可(和之前一致)")]
    [SerializeField] private GameObject joystickRoot;
    [SerializeField] private RectTransform fixedBg;
    [SerializeField] private RectTransform moveBtn;

    private float joystickMaxRadius;
    private bool isTouching = false;
    private RectTransform canvasRect;

    public static Vector2 moveDirection;

    private void Start()
    {
        if (joystickRoot != null) joystickRoot.SetActive(false);
        if (fixedBg != null) joystickMaxRadius = fixedBg.sizeDelta.x / 2;
        canvasRect = GetComponent<RectTransform>();
    }

    private void Update()
    {
        // 电脑鼠标 + 手机触摸 双兼容
        if (Input.touchCount > 0)
        {
            Touch touch = Input.GetTouch(0);
            HandleTouch(touch.position, touch.phase);
        }
        else
        {
            if (Input.GetMouseButtonDown(0)) HandleTouch(Input.mousePosition, TouchPhase.Began);
            else if (Input.GetMouseButton(0)) HandleTouch(Input.mousePosition, TouchPhase.Moved);
            else if (Input.GetMouseButtonUp(0)) HandleTouch(Input.mousePosition, TouchPhase.Ended);
        }
    }

    private void HandleTouch(Vector2 screenPos, TouchPhase phase)
    {
        if (joystickRoot == null || fixedBg == null || moveBtn == null || canvasRect == null) return;

        switch (phase)
        {
            case TouchPhase.Began:
                isTouching = true;
                joystickRoot.SetActive(true);
                // 零误差坐标转换,摇杆精准贴合点击位置
                RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, screenPos, null, out Vector2 joystickPos);
                joystickRoot.GetComponent<RectTransform>().anchoredPosition = joystickPos;
                moveBtn.anchoredPosition = Vector2.zero;
                moveDirection = Vector2.zero;
                break;

            case TouchPhase.Moved:
                if (isTouching)
                {
                    RectTransformUtility.ScreenPointToLocalPointInRectangle(fixedBg, screenPos, null, out Vector2 offsetPos);
                    offsetPos = LimitJoystickMove(offsetPos);
                    moveBtn.anchoredPosition = offsetPos;
                    moveDirection = offsetPos.normalized;
                }
                break;

            case TouchPhase.Ended:
            case TouchPhase.Canceled:
                isTouching = false;
                joystickRoot.SetActive(false);
                moveBtn.anchoredPosition = Vector2.zero;
                moveDirection = Vector2.zero;
                break;
        }
    }

    // 限制内圆不超外圆边界
    private Vector2 LimitJoystickMove(Vector2 targetPos)
    {
        float distance = Vector2.Distance(targetPos, Vector2.zero);
        if (distance > joystickMaxRadius)
        {
            targetPos = targetPos.normalized * joystickMaxRadius;
        }
        return targetPos;
    }
}

Unity中的赋值操作和UI构建

  • 3D玩家的创建(无需勾选脚本上的Is2D Mode!!! ):

  • 2D玩家的创建(需要在脚本上勾选Is2DMode!!!! )

  • 轮盘UI的创建

    • 画布挂载脚本拖拽赋值(也可以挂载在JoyStick上)
    • JoyStick配置
    • FixedJoystickBg(移动轮盘边界)
    • moveJoystickBtn(玩家操作轮盘指向器)
相关推荐
阿松爱学习5 小时前
【Unity开发】监听Scene场景事件
unity·c#
3D阅阅三维模型轻量化及浏览平台9 小时前
3D 阅阅:机加工与模具厂的智能看图报价实战
3d·机械加工·机加工·钣金加工·模具加工
福大大架构师每日一题13 小时前
ComfyUI v0.40.0发布:资产系统、原生 LoRA 堆栈、Qwen 3.5、3D 输出、相机节点与多项合作节点全面更新
数码相机·3d
xiaogai_gai13 小时前
BOM 物料清单同步实战:金蝶云星空 → 四化智造 MES 修改场景深度拆解
unity·游戏引擎
LuminWave14 小时前
洛微科技重磅发布DM-Mini 3D ToF相机:以超近距精准感知,赋能机器人灵巧操作
科技·3d·机器人
Mark White14 小时前
ICCV 2019【从颜色科学到相机】06_相机为什么有自己的“味道”?色调映射、3D LUT 与 JPEG
数码相机·3d
淡海水16 小时前
03-03-架构篇-SGen GC架构解析
unity·架构·c#·gc·sgen
/**书香门第*/16 小时前
Cocos Creator 打螺丝游戏的实现
游戏引擎·cocos2d·螺丝·打螺丝
Jmyd01231 天前
文物3D扫描采集系统选型与集成:固定+手持双链路技术方案
3d·文物修复·文物数字化·文物建模
博图光电1 天前
笔记本键盘漏光缺陷检测
人工智能·数码相机·3d·自动化