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

news2024/10/6 20:35:25

目录

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WebStorm 2024 for Mac JavaScript前端开发工具

Mac分享吧 文章目录 效果一、下载软件二、开始安装1、双击运行软件&#xff08;适合自己的M芯片版或Intel芯片版&#xff09;&#xff0c;将其从左侧拖入右侧文件夹中&#xff0c;等待安装完毕2、应用程序显示软件图标&#xff0c;表示安装成功3、打开访达&#xff0c;点击【文…

加油卡APP开发,汽车加油省钱新模式

随着社会生活水平的提高&#xff0c;汽车已经成为了家家户户的出行工具&#xff0c;汽车加油也就成为了居民日常出行必不可少的开销。为了让居民享受到更加便利、优惠的加油体验&#xff0c;加油卡APP由此产生&#xff0c;不仅方便了用户&#xff0c;也给汽车加油市场提供了更加…

分子AI预测赛Task2笔记

下面所述比较官方的内容都来自官方文档 ‍‌⁠‌‍​​​‌​​⁠​​​​​&#xfeff;​​​&#xfeff;‍‬​​‍⁠‍‍​​‬​&#xfeff;‌​​​‌‍‬​​​​​​‍‌Task2&#xff1a;赛题深入解析 - 飞书云文档 (feishu.cn) 赛题背景 强调了人工智能在科研领域&…

算法训练营day24--93.复原IP地址 +78.子集 +90.子集II

一、93.复原IP地址 题目链接&#xff1a;https://leetcode.cn/problems/restore-ip-addresses/ 文章讲解&#xff1a;https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html 视频讲解&#xff1a;https://www.bilibili.com/video/BV1fA4y1o715 1.1 初…

高温下的稳定选择 —— PP消解管,耐化学更耐用

PP消解管&#xff0c;即聚丙烯材质的消解管&#xff0c;是一种常用于化学分析中的实验室设备&#xff0c;主要用于样品的消解处理。以下是PP消解管的一些主要特性和应用&#xff1a; 主要特性&#xff1a; 1. 耐化学腐蚀&#xff1a;PP材料对多数酸、碱和有机溶剂具有良好的耐…

Keil5 ST-LINK setting闪退问题解决

1. 官网下载新版驱动文件 MDK uVision crashes when using ST-Link debugger 2. 解压替换 STLinkUSBDriver6.1.2.0Signed 我的库文件目录&#xff1a; D:\Tool\Keil5\ARM\STLink

Vue3快速上手--3小时掌握

1. Vue3简介 2020年9月18日&#xff0c;Vue.js发布版3.0版本&#xff0c;代号&#xff1a;One Piece&#xff08;n经历了&#xff1a;4800次提交、40个RFC、600次PR、300贡献者官方发版地址&#xff1a;Release v3.0.0 One Piece vuejs/core截止2023年10月&#xff0c;最新的…

数组-长度最小的子数组

M长度最小的子数组&#xff08;leetcode209&#xff09; /*** param {number} target* param {number[]} nums* return {number}*/ var minSubArrayLen function(target, nums) {const n nums.length;let ans n 1;let sum 0; // 子数组元素和let left 0; // 子数组…

燃料电池混合电源的能量管理系统

这个例子显示了燃料电池混合电源的能量管理系统。 这个例子展示了燃料电池混合电源的能量管理系统。 电路描述 本文给出了基于燃料电池的多电动飞机应急动力系统的仿真模型。随着MEA中起落架和飞控系统的电气化程度的提高&#xff0c;常规应急电源系统(冲压式空气涡轮或空气驱…

友好前端vue脚手架

企业级后台集成方案vue-element-admin-CSDN博客在哔站学习&#xff0c;老师说可以有直接的脚手架&#xff08;vue-element-admin&#xff09;立马去搜索&#xff0c;找到了这博主这篇文章 介绍 | vue-element-admin​​​​​​ 官方默认英文版&#xff1a; git clone https:/…

试用笔记之-Delphi xe 微信/支付宝支付源代码

首先delphi xe 微信/支付宝支付源代码下载&#xff1a; http://www.htsoft.com.cn/download/DelphiXEWeiXin_ZhiFuBao_ZhiFu.rar 解压后可以看到源代码 直接执行可执行文件&#xff1a;

消防认证-防火卷帘

一、消防认证 消防认证是指消防产品符合国家相关技术要求和标准&#xff0c;且通过了国家认证认可监督管理委员会审批&#xff0c;获得消防认证资质的认证机构颁发的证书&#xff0c;消防产品具有完好的防火功能&#xff0c;是住房和城乡建设领域验收的重要指标。 二、认证依据…

墨烯的C语言技术栈-C语言基础-003

三.数据类型 1.char // 字符数据型 2.short // 短整型 3.int // 整型 4.long // 长整型 5.long long // 更长的整型 6.float // 单精度浮点数 7.double // 双精度浮点数 为什么写代码? 为了解决生活中的问题 购物,点餐,看电影 为什么有这么多类型呢? 因为说的话都是字符型…

【详解】RV1106使用RKMPI+Yolov5部署检测

系列文章目录 第一篇&#xff1a;【详解】RV1106移植opencv-mobile库 文章目录 系列文章目录[TOC](文章目录) 前言一、烧入镜像二、项目工程1.获取源码编译2.移植项目文件 前言 记录使用RKMPI和Yolov5实现目标检测的demo。官方的资料比较详细&#xff1a;https://wiki.luckfo…

Android HWASAN使用与实现原理

一、背景 为了提前检测出Android User Sapce的app或native进程的内存错误问题&#xff0c;帮助研发定位与分析这些问题&#xff0c;基于Android 14版本上对HWASAN做了调研分析。 二、ASAN介绍 HWASAN是在ASAN的基础上做了拓展&#xff0c;因此在介绍HWASAN之前先了解下ASAN.…

C#的五大设计原则-solid原则

什么是C#的五大设计原则&#xff0c;我们用人话来解释一下&#xff0c;希望小伙伴们能学会&#xff1a; 好的&#xff0c;让我们以一种幽默的方式来解释C#的五大设计原则&#xff08;SOLID&#xff09;&#xff1a; 单一职责原则&#xff08;Single Responsibility Principle…

更好的方法_交叉观察器API

交叉观察器&#xff08;Intersection Observer&#xff09;API 是一个强大的工具&#xff0c;可以用来检测元素是否进入视口或从视口移出。我们可以利用这个 API 来实现粘贴式导航&#xff08;也称为粘性导航&#xff09;&#xff0c;即在用户滚动页面时&#xff0c;导航栏会在…

简单分享 for循环,从基础到高级

1. 基础篇&#xff1a;Hello, For Loop! 想象一下&#xff0c;你想给班上的每位同学发送“Hello!”&#xff0c;怎么办&#xff1f;那就是for循环啦&#xff0c; eg&#xff1a;首先有个名字的列表&#xff0c;for循环取出&#xff0c;分别打印 names ["Alice", …

从零开始实现大语言模型(一):概述

1. 前言 大家好&#xff0c;我是何睿智。我现在在做大语言模型相关工作&#xff0c;我用业余时间写一个专栏&#xff0c;给大家讲讲如何从零开始实现大语言模型。 从零开始实现大语言模型是了解其原理及领域大语言模型实现路径的最好方法&#xff0c;没有之一。已有研究证明&…

服务器BMC基础知识总结

前言 因为对硬件方面不太理解&#xff0c;所以打算先从服务器开始学习&#xff0c;也想和大家一起分享一下&#xff0c;有什么不对的地方可以纠正一下哦&#xff01;谢谢啦&#xff01;互相学习共同成长~ 1.BMC是什么&#xff1f; 官方解释&#xff1a;BMC全名Baseboard Mana…