unity 实现类似坐标轴的功能

本文介绍了如何在Unity中创建一个可交互的Gizmo组件,支持平移、旋转和特定平面的操作,同时处理了与相机距离相关的旋转问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

资源下载:https://download.csdn.net/download/WantFK/88558389

核心代码

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;


namespace AdvancedGizmo
{
    public class Gizmo : MonoBehaviour
    {


        public LayerMask layer;

        public Collider xAxis;
        public Collider yAxis;
        public Collider zAxis;

        public Collider xyPlane;
        public Collider xzPlane;
        public Collider yzPlane;

        public Collider xyRotate;
        public Collider xzRotate;
        public Collider yzRotate;

        public Collider sphereRotate;

        public float rotationSpeed = 4.0f;

        private enum GizmoAxis { X, Y, Z, XY, XZ, YZ, XYRotate, XZRotate, YZRotate, none };
        private GizmoAxis selectedAxis;

        private RaycastHit hit;
        private Ray ray;
        private Plane dragplane;
        private float rayDistance, distance;
        private Vector3 hitvector, mousePos, lookCamera, startDrag, startPos, startDragRot, lookHitPoint;
        private bool dragging, rotating;

        private EventSystem ES;

        private float sphereRadius;

        private GameObject rotationParent;
        // private Transform thisparther; //2

        Vector3 rotationAxis = Vector3.zero;

        public int i = 1;


        void Start()
        {
            ES = EventSystem.current;
            //layer = 1 << layer;
            sphereRadius = sphereRotate.bounds.extents.x;
            ChangeMode();
        }

        void LateUpdate()
        {

            bool mouseOutsideGUI = true;
            if (ES) mouseOutsideGUI = !EventSystem.current.IsPointerOverGameObject();

            if (Input.GetMouseButtonDown(1) && mouseOutsideGUI)
            {
                ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                if (Physics.Raycast(ray, out hit, 1000f, layer))
                {
                    if (hit.transform.parent == transform) ChangeMode();
                }
            }

            if (Input.GetMouseButtonDown(0) && mouseOutsideGUI)
            {

                ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                dragplane = new Plane();

                if (Physics.Raycast(ray, out hit, 1000f, layer))
                {

                    hitvector = hit.point - transform.position;

                    if (hit.collider == xAxis)
                    {
                        selectedAxis = GizmoAxis.X;
                        dragplane.SetNormalAndPosition(transform.up, transform.position);
                    }
                    else if (hit.collider == yAxis)
                    {
                        selectedAxis = GizmoAxis.Y;
                        dragplane.SetNormalAndPosition(transform.forward, transform.position);
                    }
                    else if (hit.collider == zAxis)
                    {
                        selectedAxis = GizmoAxis.Z;
                        dragplane.SetNormalAndPosition(transform.up, transform.position);
                    }
                    else if (hit.collider == xyPlane)
                    {
                        selectedAxis = GizmoAxis.XY;
                        dragplane.SetNormalAndPosition(transform.forward, transform.position);
                    }
                    else if (hit.collider == xzPlane)
                    {
                        selectedAxis = GizmoAxis.XZ;
                        dragplane.SetNormalAndPosition(transform.up, transform.position);
                    }
                    else if (hit.collider == yzPlane)
                    {
                        selectedAxis = GizmoAxis.YZ;
                        dragplane.SetNormalAndPosition(transform.right, transform.position);
                    }
                    else if (hit.collider == xyRotate)
                    {
                        selectedAxis = GizmoAxis.XYRotate;
                        rotationAxis = -transform.forward;
                        lookHitPoint = hit.point - transform.position - Vector3.Project(hit.point - transform.position, transform.forward);
                        //Debug.DrawLine(transform.position, transform.position + lookHitPoint, Color.cyan, 10.0f);
                        dragplane.SetNormalAndPosition(lookHitPoint, hit.point);
                        //DrawPlane(lookHitPoint, transform.position + lookHitPoint, 10.0f);
                        rotating = true;
                    }
                    else if (hit.collider == xzRotate)
                    {
                        selectedAxis = GizmoAxis.XZRotate;
                        rotationAxis = -transform.up;
                        lookHitPoint = hit.point - transform.position - Vector3.Project(hit.point - transform.position, transform.up);
                        Debug.DrawLine(transform.position, transform.position + lookHitPoint, Color.cyan, 10.0f);
                        dragplane=new Plane(lookHitPoint, hit.point);
                        DrawPlane(lookHitPoint, transform.position + lookHitPoint, 10.0f);
                        rotating = true;
                    }
                    else if (hit.collider == yzRotate)
                    {
                        selectedAxis = GizmoAxis.YZRotate;
                        rotationAxis = -transform.right;
                        lookHitPoint = hit.point - transform.position - Vector3.Project(hit.point - transform.position, transform.right);
                        //Debug.DrawLine(transform.position, transform.position + lookHitPoint, Color.cyan, 10.0f);
                        dragplane=new Plane(lookHitPoint, hit.point);
                        //DrawPlane(lookHitPoint, transform.position + lookHitPoint, 10.0f);
                        rotating = true;
                    }
                    else if (hit.collider == sphereRotate)
                    {
                        selectedAxis = GizmoAxis.none;
                        lookCamera = Camera.main.transform.position - transform.position;
                        startDragRot = transform.position + lookCamera.normalized * sphereRadius;
                        dragplane=new Plane(lookCamera.normalized, startDragRot);
                        //DrawPlane(lookCamera.normalized, startDragRot, 10.0f);
                        rotating = true;
                    }
                    else
                    {
                        return;
                    }

                    if (rotating)
                    {

                        Debug.DrawRay(ray.origin, ray.direction*2, Color.white);
                        if (dragplane.Raycast(ray, out rayDistance))
                        {
                            Debug.Log("有1");
                            startDragRot = ray.GetPoint(rayDistance);
                        }
                        else
                        {
                            Debug.Log("无1");
                        }
                        rotationParent = new GameObject();            //1
                        rotationParent.transform.position = transform.position;//1
                        transform.SetParent(rotationParent.transform);//1
                        
                        //thisparther = this.transform.parent;       //2
                        //rotationParent = new GameObject();         //2
                        //rotationParent.transform.parent = thisparther;//2
                        //rotationParent.transform.localPosition = transform.localPosition;//2
                        //transform.SetParent(rotationParent.transform);//2
                    }

                    distance = hit.distance;
                    startDrag = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, distance));
                    startPos = transform.position;
                    dragging = true;
                }
                
            }

            if (dragging || rotating)
            {

                ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                Debug.DrawRay(ray.origin, ray.direction*2 , Color.green);
                if (dragplane.Raycast(ray, out rayDistance))
                {
                    mousePos = ray.GetPoint(rayDistance);
                }
                Vector3 onDrag = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, distance));
                Vector3 translation = onDrag - startDrag;
                Vector3 projectedTranslation = Vector3.zero;

                if (dragging)
                {
                    switch (selectedAxis)
                    {

                        case GizmoAxis.X:
                            {
                                projectedTranslation = Vector3.Project(translation, transform.right);
                                transform.position = startPos + projectedTranslation.normalized * translation.magnitude;
                                break;
                            }
                        case GizmoAxis.Y:
                            {
                                projectedTranslation = Vector3.Project(translation, transform.up);
                                transform.position = startPos + projectedTranslation.normalized * translation.magnitude;
                                break;
                            }
                        case GizmoAxis.Z:
                            {
                                projectedTranslation = Vector3.Project(translation, transform.forward);
                                transform.position = startPos + projectedTranslation.normalized * translation.magnitude;
                                break;
                            }
                        case GizmoAxis.XY:
                        case GizmoAxis.XZ:
                        case GizmoAxis.YZ:
                            {
                                transform.position = mousePos - hitvector;
                                break;
                            }

                    }
                }
                if (rotating)
                {
                    translation = mousePos - startDragRot;
                    //Debug.DrawLine(startDragRot, mousePos, Color.white, 6.0f);

                    Vector3 rotationAxis2 = Vector3.zero;

                    switch (selectedAxis)
                    {
                        case GizmoAxis.XYRotate:
                            {
                                projectedTranslation = translation - Vector3.Project(translation, rotationAxis);
                                //Debug.DrawLine(startDragRot, startDragRot + projectedTranslation, Color.yellow, 1.0f);
                                rotationAxis2 = Vector3.Cross(projectedTranslation, lookHitPoint);
                                //Debug.DrawLine(transform.position, transform.position + 5 * rotationAxis2.normalized, Color.magenta, 1.0f);
                                //Debug.DrawLine(transform.position, transform.position + 5 * rotationAxis.normalized, Color.cyan, 1.0f);
                                break;
                            }
                        case GizmoAxis.XZRotate:
                            {
                                projectedTranslation = translation - Vector3.Project(translation, rotationAxis);
                                //Debug.DrawLine(startDragRot, startDragRot + projectedTranslation, Color.yellow, 1.0f);
                                rotationAxis2 = Vector3.Cross(projectedTranslation, lookHitPoint);
                                //Debug.DrawLine(transform.position, transform.position + 5 * rotationAxis2.normalized, Color.magenta, 1.0f);
                                //Debug.DrawLine(transform.position, transform.position + 5 * rotationAxis.normalized, Color.magenta, 1.0f);
                                break;
                            }
                        case GizmoAxis.YZRotate:
                            {
                                projectedTranslation = translation - Vector3.Project(translation, rotationAxis);
                                //Debug.DrawLine(startDragRot, startDragRot + projectedTranslation, Color.yellow, 1.0f);
                                rotationAxis2 = Vector3.Cross(projectedTranslation, lookHitPoint);

                                //Debug.DrawLine(transform.position, transform.position + 5 * rotationAxis.normalized, Color.gray, 1.0f);
                                break;
                            }
                        case GizmoAxis.none:
                            {
                                rotationAxis2 = rotationAxis;
                                projectedTranslation = translation;
                                rotationAxis = Vector3.Cross(translation, lookCamera);
                                break;
                            }
                    }
                    float angle;
                    Quaternion delta;

                    //Debug.DrawLine(transform.position, transform.position + 5 * rotationAxis.normalized, Color.red, 1.0f);
                    angle = -Mathf.Rad2Deg * projectedTranslation.magnitude / sphereRadius;
                    delta = Quaternion.AngleAxis(angle, rotationAxis2);

                    if (selectedAxis == GizmoAxis.none)
                    {

                        rotationParent.transform.rotation = delta;

                    }
                    else
                    {

                        rotationParent.transform.rotation = delta;
                    }

                }

                if (Input.GetMouseButtonUp(0)) SetFalse();

            }

        }

        public void ChangeMode()
        {
            i = (i + 1) % 2;
            // i = 0 / translation;  i = 1 / rotation 
            bool val = (i == 1);

            xyRotate.gameObject.SetActive(val);
            xzRotate.gameObject.SetActive(val);
            yzRotate.gameObject.SetActive(val);
            sphereRotate.gameObject.SetActive(val);

            xAxis.gameObject.SetActive(!val);
            yAxis.gameObject.SetActive(!val);
            zAxis.gameObject.SetActive(!val);
            xyPlane.gameObject.SetActive(!val);
            xzPlane.gameObject.SetActive(!val);
            yzPlane.gameObject.SetActive(!val);
        }


        void SetFalse()
        {
            transform.SetParent(null);//1   
            // transform.SetParent(thisparther);//2
            if (rotationParent) Destroy(rotationParent);
            rotating = false;
            dragging = false;
        }

        void DrawPlane(Vector3 normal, Vector3 position, float t)
        {

            Vector3 v3;

            if (normal.normalized != Vector3.forward)
                v3 = Vector3.Cross(normal, Vector3.forward).normalized * normal.magnitude;
            else
                v3 = Vector3.Cross(normal, Vector3.up).normalized * normal.magnitude; ;

            var corner0 = position + v3;
            var corner2 = position - v3;
            Quaternion q = Quaternion.AngleAxis(90.0f, normal);
            v3 = q * v3;
            var corner1 = position + v3;
            var corner3 = position - v3;

            Debug.DrawLine(corner0, corner2, Color.green, t);
            Debug.DrawLine(corner1, corner3, Color.green, t);
            Debug.DrawLine(corner0, corner1, Color.green, t);
            Debug.DrawLine(corner1, corner2, Color.green, t);
            Debug.DrawLine(corner2, corner3, Color.green, t);
            Debug.DrawLine(corner3, corner0, Color.green, t);
            Debug.DrawRay(position, normal, Color.red, t);
        }
    }
}

1如果坐标轴位于物体下时 注销 //1  使用 //2

2有时候我们需要这个坐标轴随相机距离而缩放 这个时候发现有时候旋转出现bug 

是因为 sphereRadius = sphereRotate.bounds.extents.x; 在start中获取 我们需要将其放在updata下实时获取 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

屋檐上的大修勾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值