目录
1. 装配场景的相机控制
2. 鼠标拖拽和旋转功能的实现
2.1 鼠标拖拽
2.2 物体旋转
3. 零件与装配位置的对应关系
4. 轴向装配的准备位置
5. 装配顺序的实现
5.1 标签提示
5.2 定义一个变量记录步骤数值
1. 装配场景的相机控制
开始装配功能时,需要将相机调节成适合于装配的移动方式,万一鼠标拖拽零件时镜头游移到其他地方会相当不舒服。
在本应用中的做法是,建立一个空节点,让镜头始终跟随这个节点移动,类似于第三人称控制器,控制镜头在这个节点周围的有限位置。
需要将相机设置为这个固定节点的子节点,并且调整好高度和角度。效果如下:
相机控制代码如下:
public class CameraControl : MonoBehaviour
{//使用鼠标左键点击画面调整角度public GameObject mainPerson;//跟随点private float distance;//相机和主角点的距离private Quaternion Cam_rotation; //相机的旋转角度public bool isClickRotate=true; //判断是否有旋转命令private float xAngle,yAngle;private Vector2 inputPos;private Vector3 resultPos;private Ray ray;public float rotationSpeed=80; //旋转速度public float yAngleMin=3, yAngleMax=80;public float lerp = 5; //位置跟随的范围void Start(){distance = Vector3.Distance(mainPerson.transform.position, transform.position);Cam_rotation = transform.rotation; //记录相机初始角度//计算相机初始与X、Y轴的角度xAngle = Vector3.Angle(Vector3.right, transform.right);yAngle = Vector3.Angle(Vector3.up, transform.up);}void Update(){//或者使用lateUpdate()if(!isClickRotate){//如果点击到了零件,就不再让相机旋转return;}else{isClickRotate = Input.GetMouseButton(0);inputPos.x = Input.GetAxis("Mouse X");inputPos.y = Input.GetAxis("Mouse Y");if (isClickRotate){xAngle += inputPos.x * rotationSpeed * Time.deltaTime;yAngle -= inputPos.y * rotationSpeed * Time.deltaTime;yAngle = Mathf.Clamp(yAngle, yAngleMin, yAngleMax);Cam_rotation = Quaternion.Euler(yAngle, xAngle, 0);transform.rotation = Quaternion.Lerp(transform.rotation, Cam_rotation, Time.deltaTime * 5);}resultPos = mainPerson.transform.position - (Cam_rotation * Vector3.forward * distance);transform.position = Vector3.Lerp(transform.position, resultPos, Time.deltaTime * lerp);}//镜头缩放if (Input.GetAxis("Mouse ScrollWheel") != 0){//鼠标滚动滑轮if (Input.GetAxis("Mouse ScrollWheel") < 0){//范围值限定if (Camera.main.fieldOfView <= 100)Camera.main.fieldOfView += 2;if (Camera.main.orthographicSize <= 20)Camera.main.orthographicSize += 0.5F;}//Zoom in if (Input.GetAxis("Mouse ScrollWheel") > 0){//范围值限定if (Camera.main.fieldOfView > 2)Camera.main.fieldOfView -= 2;if (Camera.main.orthographicSize >= 1)Camera.main.orthographicSize -= 0.5F;}}}
}
2. 鼠标拖拽和旋转功能的实现
2.1 鼠标拖拽
查了一下各路大仙的做法,使用协程方法的效果比较好,由于鼠标拖动是比较耗时的工作,不可能在一帧执行时间内完成,因此使用协程方法建立一个新线程,不影响主线程的执行。代码如下:
public class onMouseDrag3d : MonoBehaviour
{//偏移值private Vector3 _Offset;//当前物体对应的屏幕坐标private Vector3 _ScreenPos;private void Start(){StartCoroutine(OnMouseDrag());}private IEnumerator OnMouseDrag(){//当前物体对应的屏幕坐标_ScreenPos = Camera.main.WorldToScreenPoint(transform.position);//偏移值=物体的世界坐标,减去转化之后的鼠标世界坐标(z轴的值为物体屏幕坐标的z值)_Offset = transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, _ScreenPos.z));//当鼠标左键点击while (Input.GetMouseButton(0)){//物体当前坐标transform.position = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,Input.mousePosition.y, _ScreenPos.z)) + _Offset;//等待固定更新,且循环执行yield return new WaitForFixedUpdate();}}
}
将这个代码挂在需要移动的物体上,能够实现移动的效果。
2.2 物体旋转
将下面的旋转脚本挂在物体上,并且需要将物体的层设置为“WorkPiece"层。
public class onMouseRotate : MonoBehaviour
{ //旋转所需的参数//上下旋转最大角度限制public int yMinLimit = -20;public int yMaxLimit = 80;//旋转速度public float xSpeed = 250.0f;//左右旋转速度public float ySpeed = 120.0f;//上下旋转速度//旋转角度private float x = 0.0f;private float y = 0.0f;private void Update(){RaycastHit hit;//射线检测需要旋转的物体Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);Physics.Raycast(ray, out hit, 100, LayerMask.GetMask("WorkPiece")); //零件需要加入WorkPiece层if (Input.GetMouseButton(1)&&hit.collider!=null){//旋转的判断条件为射线接触到物体,且按下了鼠标右键x += Input.GetAxis("Mouse X") * xSpeed * 0.02f;y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;y = ClampAngle(y, yMinLimit, yMaxLimit);//欧拉角转化为四元数target.transform.rotation = Quaternion.Euler(y, x, 0);} }//角度范围值限定static float ClampAngle(float angle, float min, float max){if (angle < -360)angle += 360;if (angle > 360)angle -= 360;return Mathf.Clamp(angle, min, max);}
}
3. 零件与装配位置的对应关系
在一个装配场景中,零件数量从几个到上百个不等,因此,需要根据零件数量定义数据表,如数据表、数组或二维数组、字典等数据形式,必要时需要用数据库存储零件信息。这里使用相对容易理解的数组,设置三个数组,分别存储零件、零件位置和提示标签,让这三者的下标一致作为对应标记。
并且可以在初始化时Start() ,使用for循环给每个零件挂上拖拽和旋转的脚本
for (int i = 0; i < 14; i++){ //将所有零件挂上onMouseDrag3d脚本workPiece[i].AddComponent<onMouseDrag3d>();if (i == 0)workPiece[i].GetComponent<pieceToPlace>().ifBase = true;//第一个零件为基准零件(主轴),其他为轴向装配零件}
4. 轴向装配的准备位置
在主轴一端设置一个轴向准备位置,以便于轴向装配的零件,自由移动到这个位置后,锁定其他两轴,仅沿着轴向装配。(拆卸场景相反)
实现思想是:当零件被自由拖拽,移动到的位置和这个准备位置的距离,小于一定数值时,将这个准备位置的角度和位置赋值给零件。(装配到最终位置也是这么实现的)
var offset = Vector3.Distance(transform.position, piecePlace.transform.position);if (offset <= 0.75f){//在两者距离较接近时让两者重叠transform.position = piecePlace.transform.position; //位置一致transform.rotation = piecePlace.transform.rotation; //方向一致}
之后仅仅让零件在x方向上被拖动,将上面的协程方法修改为如下,其中正负方向和坐标轴需要根据实际情况而定。
private IEnumerator OnMouseDragX(){//X方向拖拽//当前物体对应的屏幕坐标_ScreenPos = Camera.main.WorldToScreenPoint(transform.position);//偏移值_Offset = transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, _ScreenPos.z));//当鼠标左键点击while (Input.GetMouseButton(0)){//修改:y、z方向不变transform.position = Camera.main.ScreenToWorldPoint(new Vector3(-Input.mousePosition.x,transform.position.y, transform.position.z)) + Offset;//等待固定更新,且循环执行yield return new WaitForFixedUpdate();}}
5. 装配顺序的实现
5.1 标签提示
使用UI制作标签,包含Canvas和Text,其中画布Canvas设置为WorldSpace,相机设置为主相机。
这些标签起到在场景中提示拆装顺序的作用:
5.2 定义一个变量记录步骤数值
比如,可以定义一个记录步骤的整型变量,初始值为0,用这个整型变量与零件的数组下标进行比较,如果选取一个零件的下标与步骤变量相符时,可以顺利实现装配,并且变量值+1;当两者不符合时,则提示装配顺序有误。