一、前言

     我们最近要做一个线路的规划编辑,并且是在三维场景中,编辑完就立马能用。立马能用还好说,有特别多的轮子可以用,在三维场景中实时编辑就有点意思了。其实功能就是类似于在Unity的编辑界面操作一个Cube的位置,当然旋转什么的我这个任务里暂时还不需要,就先简单的做了一个位置的三维拖拽。如图所示:在Unity的编辑界面对一个Cube进行拖拽,选中中心就可以进行任意拖

Unity 拖拽移动相机 unity 拖拽物体_坐标轴

拽,选中单个的坐标轴可以进行指定方向的拖拽。

下面是我实现的效果图,如同所示:

Unity 拖拽移动相机 unity 拖拽物体_Unity 拖拽移动相机_02

二、实现

1、任意拖拽的比较简单,主要思路是控制好鼠标按下和正在拖拽的坐标差,以及鼠标的坐标到世界坐标的转换,代码如下

mr.material.color = IsBeSelected ? beSelectedColor : normalColor;

        if(Input.GetKeyDown(KeyCode.Mouse0))
        {
            screenPosition = Camera.main.WorldToScreenPoint(gameObject.transform.position);
            offset = gameObject.transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPosition.z));
            isMouseDrag = true;
        }
        if(Input.GetKey(KeyCode.Mouse0))
        {
            if (isMouseDrag&&IsBeSelected)
            {
                //Debug.Log("开始拖拽了");
                Vector3 currentScreenSpace = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPosition.z);
                Vector3 currentPosition = Camera.main.ScreenToWorldPoint(currentScreenSpace) + offset;
                gameObject.transform.position = currentPosition;
            }
        }
        if(Input.GetKeyUp(KeyCode.Mouse0))
        {
            isMouseDrag = false;
        }

2、三个坐标轴方向的拖拽,比较复杂,但是鼠标的移动计算是一样的,只是利用这个的最终位置量需要考虑一下,不能直接使用,需要考虑在每个轴方向的投影。比如X轴,那么就需要考虑这个X轴模型所代表的方向是它的Transform的前、上还是右方向,我这里的X模型的前方向是代表它在世界坐标所指向的方向,然后就将得到的鼠标拖着后的位置变化坐标投影到这个方向,但是其他方向的投影还是原来的位置坐标。代码如下:

if (Input.GetKeyDown(KeyCode.Mouse0))
        {
            dragBeforeGameObjPos = parentTransform.transform.position;
            screenPosition = Camera.main.WorldToScreenPoint(dragBeforeGameObjPos);
            offset = transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPosition.z));
            isMouseDrag = true;
        }
        if (Input.GetKey(KeyCode.Mouse0))
        {
            if (isMouseDrag && IsBeSelected)
            {
                //Debug.Log("开始拖拽了");
                Vector3 currentScreenSpace = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPosition.z);
                Vector3 currentPosition = Camera.main.ScreenToWorldPoint(currentScreenSpace) + offset;
                float tempLength = Vector3.Distance(currentPosition, transform.position);
                Vector3 tempPos = Vector3.zero;
                switch (curPAType)
                {
                    case PointAxis_Type.Axis_X:
                        tempPos = Vector3.Project(currentPosition, transform.forward) + Vector3.Project(dragBeforeGameObjPos, transform.up) + Vector3.Project(dragBeforeGameObjPos, transform.right);
                        break;
                    case PointAxis_Type.Axis_Y:
                        tempPos = Vector3.Project(currentPosition, transform.up) + Vector3.Project(dragBeforeGameObjPos, transform.right) + Vector3.Project(dragBeforeGameObjPos, transform.forward);
                        break;
                    case PointAxis_Type.Axis_Z:
                        tempPos = Vector3.Project(currentPosition, transform.right) + Vector3.Project(dragBeforeGameObjPos, transform.up) + Vector3.Project(dragBeforeGameObjPos, transform.forward);
                        break;
                }
                parentTransform.transform.position = tempPos;
            }
        }
        if (Input.GetKeyUp(KeyCode.Mouse0))
        {
            isMouseDrag = false;
        }

3、最后在来一段选中控制操作

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ControlCam : MonoBehaviour
{
    public static ControlCam M_Instance
    {
        get
        {
            if(null==_instance)
            {
                _instance = FindObjectOfType<ControlCam>();
            }
            return _instance;
        }
    }

    public bool M_IsCanControl
    {
        get
        {
            return isCanControl;
        }
    }

    public static ControlCam _instance;
    private bool isMouseDrag = false;
    private Vector3 lastMousePos;
    [SerializeField]
    private Vector3 offset;

    private bool isCanControl = false;
    private PointAxis PA;
    private PointEntity PE;
    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.LeftControl))
        {
            isCanControl = true;
        }
        if (Input.GetKeyUp(KeyCode.LeftControl))
        {
            isCanControl = false;
        }
        if (Input.GetKeyDown(KeyCode.Mouse1))
        {
            lastMousePos = Input.mousePosition;
            isMouseDrag = true;
        }
        if (Input.GetKey(KeyCode.Mouse1))
        {
            if (isMouseDrag)
            {
                offset = Input.mousePosition - lastMousePos;
                transform.RotateAround(Vector3.zero, Vector3.up, offset.x);
                transform.RotateAround(Vector3.zero, Vector3.right, offset.y);
                lastMousePos = Input.mousePosition;
            }
        }
        if (Input.GetKeyUp(KeyCode.Mouse1))
        {
            isMouseDrag = false;
        }
        if (Input.GetKeyUp(KeyCode.Mouse0))
        {
            isCanControl = false;
        }
        if (!isCanControl)
        {
            Reset_SelectedState();
            return;
        }
        Ray tempRay = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast(tempRay, out hit, 2000))
        {
            PointEntity tempHitPE = hit.collider.GetComponent<PointEntity>();
            PointAxis tempHitPA = hit.collider.GetComponent<PointAxis>();
            if (Input.GetKeyDown(KeyCode.Mouse0))
            {
                if (null != tempHitPE)
                {
                    Reset_SelectedState();
                    PE = tempHitPE;
                    PE.IsBeSelected = true;
                }
                else if (null != tempHitPA)
                {
                    Reset_SelectedState();
                    PA = tempHitPA;
                    PA.IsBeSelected = true;
                }
            }
        }
    }

    private void Reset_SelectedState()
    {
        if (null != PE)
        {
            PE.IsBeSelected = false;
            PE = null;
        }
        if (null != PA)
        {
            PA.IsBeSelected = false;
            PA = null;
        }
    }
}

选中单个坐标轴或中心就让其高亮,每个坐标轴和中心都只能选中一个,为了避免在拖拽滑动鼠标的时候会选中其他的坐标轴或者中心,而造成混乱,这里我让鼠标按下的时候才触发选中操作。

三、总结

1、轮子有就好,没有也得学会自力更生

2、基本实现了类似Unity的编辑界面操作拖拽物体的功能,但是操作的设计上还需要有待加强

3、摄像机的旋转是为了观察在不同姿态下,坐标轴的拖拽始终是朝着它所指向的方向