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

目录

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/765712.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

k8s公网集群安装(1.23.0)

网上搜到的公网搭建k8s都不太一致, 要么说的太复杂, 要么镜像无法下载, 所以写了一个简洁版,小白也能一次搭建成功 使用的都是centos7,k8s版本为1.23.0 使用二台机器搭建的, 三台也是一样的思路1.所有节点分别设置对应主机名 hostnamectl set-hostname master hostnamectl set…

QT4-QT5(6)-const char* QString 乱码转换

我简单粗暴的给出个结论&#xff1a; QString GBK编码正常&#xff0c;可以转UTF-8编码&#xff0c;但会有少量乱码。 const char* 编码就不要转编码&#xff0c;转哪个都是乱码。 UTF-8.cpp 下 1.QString GBK->UTF-8 2.const char * GBK->UTF-8 const char *…

ViewBinding的使用(因为kotlin-android-extensions插件的淘汰)

书籍&#xff1a; 《第一行代码 Android》第三版 开发环境&#xff1a; Android Studio Jellyfish | 2023.3.1 问题&#xff1a; 3.2.4在Activity中使用Toast章节中使用到了kotlin-android-extensions插件,但是该插件已经淘汰,根据网上了解,目前使用了新的技术VewBinding替…

Shiro框架

入门概述 1 shiro是什么? Apache Shiro 是一个功能强大且易于使用的 Java 安全(权限)框架。Shiro 可以完成&#xff1a;认证、授权、加密、会话管理、与 Web 集成、缓存 等。借助 Shiro 您可以快速轻松地保护任何应用程序——从最小的移动应用程序到最大的 Web 和企业应用程…

Spring之spring的单例bean是线程安全的吗

Spring单例bean是线程安全的吗&#xff1f; 不是线程安全的。 1、Bean的作用域 Service Scope("singleton") public class UserServiceImpl implements UserService{ } singleton &#xff08;默认&#xff09;&#xff1a;bean在每个Spring IOC容器中只有一个实例…

【C++进阶学习】第五弹——二叉搜索树——二叉树进阶及set和map的铺垫

二叉树1&#xff1a;深入理解数据结构第一弹——二叉树&#xff08;1&#xff09;——堆-CSDN博客 二叉树2&#xff1a;深入理解数据结构第三弹——二叉树&#xff08;3&#xff09;——二叉树的基本结构与操作-CSDN博客 二叉树3&#xff1a;深入理解数据结构第三弹——二叉树…

BAS(入侵与攻击模拟)正在替代红队测试?

之前经常会被用户问到&#xff0c;漏扫、渗透和红队红的区别是啥&#xff1f; 传统的漏扫、渗透和红蓝对抗&#xff0c;可以看到工具化的漏洞不可靠&#xff0c;人工的成本就高。怎么找到一个漏洞可信度又高&#xff0c;成本又低的&#xff0c;就诞生了BAS。 抛开漏扫&#xf…

实体行业零基础做短视频矩阵,轻松实现海量曝光!

​在很多人的理解中&#xff0c;抖音是一个不错的盈利渠道&#xff0c;就像早些年的某宝、某多一样&#xff0c;我们现在在抖音看到的许多账号&#xff0c;大的IP&#xff0c;大多数都是品牌方、MCN机构&#xff0c;或者草根的网红等&#xff0c;但还是有不少实体老板没有入局&…

ShareSDK iOS端如何实现小红书分享

下载SDK 请登陆官网 &#xff0c;找到SDK下载&#xff0c;勾选需要的平台下载 导入SDK &#xff08;1&#xff09;离线导入将上述下载到的SDK&#xff0c;直接将整个SDK资源文件拖进项目里&#xff0c;如下图&#xff1a; 并且勾选以下3个选项 在点击Finish&#xff0c;…

Python - 递归函数(Recursive Function)的速度优化 (Python实现)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/140137432 免责声明&#xff1a;本文来源于个人知识与开源资料&#xff0c;仅用于学术交流&#xff0c;不包含任何商业技术&#xff0c;欢迎相互学…

RTSP协议在视频监控系统中的典型应用、以及视频监控设备的rtsp地址格式介绍

目录 一、协议概述 1、定义 2、提交者 3、位置 二、主要特点 1、实时性 2、可扩展性 3、控制功能 4、回放支持 5、网络适应性 三、RTSP的工作原理 1、会话准备 2、会话建立 3、媒体流控制 4、会话终止 5、媒体数据传输 四、协议功能 1、双向性 2、带外协议 …

Studying-代码随想录训练营day26| 491.递增子序列、46.全排列、47.全排列 II、51.N皇后、37.解数独、回溯总结

第26天&#xff0c;回溯part04&#xff0c;昨天休息复习总结回溯内容&#xff0c;&#x1f4aa;(ง •_•)ง&#x1f4aa; 目录 491.递增子序列 46.全排列 47.全排列 II 51.N皇后 37.解数独 回溯总结 491.递增子序列 文档讲解&#xff1a;代码随想录递增子序列 视频讲…

d3dcompiler47dll丢失怎么解决,总结几种靠谱的方法

在日常生活和工作中&#xff0c;电脑已经成为我们不可或缺的工具。然而&#xff0c;在使用电脑的过程中&#xff0c;我们常常会遇到一些错误提示&#xff0c;其中之一就是“找不到d3dcompiler_47.dll”。这个问题可能会对电脑系统的正常运行造成一定的影响&#xff0c;因此我们…

多商户b2b2c商城系统怎么运营

B2B2C多用户商城系统支持多种运营模式&#xff0c;以满足不同类型和发展阶段的企业需求。以下是五大主要的运营模式&#xff1a; **1. 自营模式&#xff1a;**平台企业通过建立自营线上商城&#xff0c;整合自身多渠道业务。通过会员、商品、订单、财务和仓储等多用户商城管理系…

旧版st7789屏幕模块 没有CS引脚的天坑 已解决!!!

今天解决了天坑一个&#xff0c;大家可能有的人买的是st7789屏幕模块&#xff0c;240x240&#xff0c;1.3寸的 他标注的是老版&#xff0c;没有CS引脚&#xff0c;小崽子长这样&#xff1a; 这熊孩子用很多通用的驱动不吃&#xff0c;死活不显示&#xff0c;网上猛搜&#xff…

【简单讲解神经网络训练中batch的作用】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

pdf怎么拆分成一页一页?4种拆分方法分享

在日常的办公学习中&#xff0c;PDF文档因其跨平台、易阅读、不易篡改等特性&#xff0c;成为我们工作和学习中不可或缺的一部分。然而&#xff0c;当我们需要对PDF进行编辑、打印或分享时&#xff0c;有时需要将整个PDF文档拆分成一页一页的单独文件。那么&#xff0c;如何高效…

嵌入式学习——硬件(Linux系统在2440上的启动)——day57

1. Linux2.6系统在s3c2440上的启动过程分三个阶段 1.1 启动u-boot 1.2 启动Linux内核 1.3 挂载根文件系统 2. bootloader 2.1 定义 bootloader的本质是一个裸机程序&#xff0c;bootlood专门是为了能够正确地启动linux操作系 统&#xff0c;在系统初上电时需要对系统做一些…

TFD那智机器人仿真离线程序文本转换为现场机器人程序

TFD式样那智机器人离线程序通过Process Simulation、DELMIA等仿真软件为载体给机器人出离线&#xff0c;下载下来的文本程序&#xff0c;现场机器人一般是无法导入及识别出来的。那么就需要TFD on Desk TFD控制器来进行转换&#xff0c;才能导入现场机器人读取程序。 导入的文…

CAN通信波形【示波器抓取】

在测试bms系统过程中&#xff0c;在上位机发现无法读取CAN通信&#xff0c;尝试使用示波器抓取CAN通信波形&#xff0c;&#xff0c;去确定CAN通信是否正常。 做一想要从车上测出can总线上的数据还不太容易。 于是我首先使用示波器&#xff08;我使用的示波器型号是TDS 220&am…