机械拆装-基于Unity-装配功能的实现

目录

[1. 装配场景的相机控制](#1. 装配场景的相机控制)

[2. 鼠标拖拽和旋转功能的实现](#2. 鼠标拖拽和旋转功能的实现)

[2.1 鼠标拖拽](#2.1 鼠标拖拽)

[2.2 物体旋转](#2.2 物体旋转)

[3. 零件与装配位置的对应关系](#3. 零件与装配位置的对应关系)

[4. 轴向装配的准备位置](#4. 轴向装配的准备位置)

[5. 装配顺序的实现](#5. 装配顺序的实现)

[5.1 标签提示](#5.1 标签提示)

[5.2 定义一个变量记录步骤数值](#5.2 定义一个变量记录步骤数值)



1. 装配场景的相机控制

开始装配功能时,需要将相机调节成适合于装配的移动方式,万一鼠标拖拽零件时镜头游移到其他地方会相当不舒服。

在本应用中的做法是,建立一个空节点,让镜头始终跟随这个节点移动,类似于第三人称控制器,控制镜头在这个节点周围的有限位置。

需要将相机设置为这个固定节点的子节点,并且调整好高度和角度。效果如下:

相机控制代码如下:

cs 复制代码
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 鼠标拖拽

查了一下各路大仙的做法,使用协程方法的效果比较好,由于鼠标拖动是比较耗时的工作,不可能在一帧执行时间内完成,因此使用协程方法建立一个新线程,不影响主线程的执行。代码如下:

cs 复制代码
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"层。

cs 复制代码
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循环给每个零件挂上拖拽和旋转的脚本

cs 复制代码
        for (int i = 0; i < 14; i++)
        {   //将所有零件挂上onMouseDrag3d脚本
            workPiece[i].AddComponent<onMouseDrag3d>();

            if (i == 0)
                workPiece[i].GetComponent<pieceToPlace>().ifBase = true;
               //第一个零件为基准零件(主轴),其他为轴向装配零件
        }

4. 轴向装配的准备位置

在主轴一端设置一个轴向准备位置,以便于轴向装配的零件,自由移动到这个位置后,锁定其他两轴,仅沿着轴向装配。(拆卸场景相反)

实现思想是:当零件被自由拖拽,移动到的位置和这个准备位置的距离,小于一定数值时,将这个准备位置的角度和位置赋值给零件。(装配到最终位置也是这么实现的)

cs 复制代码
  var offset = Vector3.Distance(transform.position, piecePlace.transform.position);
  if (offset <= 0.75f)
  {//在两者距离较接近时让两者重叠
     transform.position = piecePlace.transform.position;  //位置一致
     transform.rotation = piecePlace.transform.rotation;  //方向一致
   }

之后仅仅让零件在x方向上被拖动,将上面的协程方法修改为如下,其中正负方向和坐标轴需要根据实际情况而定。

cs 复制代码
  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;当两者不符合时,则提示装配顺序有误。

相关推荐
yi碗汤园1 小时前
【一文了解】C#基础-集合
开发语言·前端·unity·c#
charon877819 小时前
UE ARPG | 虚幻引擎战斗系统
游戏引擎
小春熙子20 小时前
Unity图形学之Shader结构
unity·游戏引擎·技术美术
Sitarrrr1 天前
【Unity】ScriptableObject的应用和3D物体跟随鼠标移动:鼠标放置物体在场景中
3d·unity
极梦网络无忧1 天前
Unity中IK动画与布偶死亡动画切换的实现
unity·游戏引擎·lucene
逐·風1 天前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
_oP_i1 天前
Unity Addressables 系统处理 WebGL 打包本地资源的一种高效方式
unity·游戏引擎·webgl
代码盗圣1 天前
GODOT 4 不用scons编译cpp扩展的方法
游戏引擎·godot
Leoysq2 天前
【UGUI】实现点击注册按钮跳转游戏场景
游戏·unity·游戏引擎·ugui
PandaQue2 天前
《潜行者2切尔诺贝利之心》游戏引擎介绍
游戏引擎