在研究基于unity3d的虚拟装配的过程中,查阅了很多资料,本人对其中的机械运动模拟比较感兴趣。其中我认为比较厉害的是Mogoson的机械机构关节绑定和传动插件(链接:link)但是由于没有文档说明,而且泛用性不是很好,我一直没有用自己的模型尝试成功。所以自己写了一个项目,来模拟其中缸的运动。该文章只是抛砖引玉,希望各位大佬能多多指教。
先创建两个圆柱,错位摆放,一个短(y的缩放值为1),一个长(y的缩放值为2),在两个圆柱上做标记(创建空的子物体),mark1是Cylinder(1)的子物体的坐标为(0,-1,0),mark和markRotate是Cylinder的子物体,坐标分别为(0,1,0)和(0,-1,0)。其中mark和mark1是用于虚拟装配的标记点,markRotate是用来模拟缸底铰链的,可以使缸绕该点旋转。
下面这段代码是鼠标随意拖动和旋转物体的脚本,脚本提供了一个接口moveTarget,使用时将该脚本添加在相应物体上,在把需要移动的物体拖到moveTarget处。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class move : MonoBehaviour
{
//旋转速度
public float xSpeed = 250.0f;
public float ySpeed = 120.0f;
//旋转角度
private float x = 0.0f;
private float y = 0.0f;
public Transform moveTarget;//移动的对象
//偏移值
Vector3 m_Offset;
//当前物体对应的屏幕坐标
Vector3 m_TargetScreenVec;
void Start ()
{
}
//物体移动
private IEnumerator OnMouseDown()
{
//当前物体对应的屏幕坐标
m_TargetScreenVec = Camera.main.WorldToScreenPoint(transform.position);
//偏移值=物体的世界坐标,减去转化之后的鼠标世界坐标(z轴的值为物体屏幕坐标的z值)
m_Offset = transform.position - Camera.main.ScreenToWorldPoint(new Vector3
(Input.mousePosition.x, Input.mousePosition.y, m_TargetScreenVec.z));
//当鼠标左键点击
while (Input.GetMouseButton(0))
{
//当前坐标等于转化鼠标为世界坐标(z轴的值为物体屏幕坐标的z值)+ 偏移量
transform.position = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,
Input.mousePosition.y, m_TargetScreenVec.z)) + m_Offset;
//等待固定更新
yield return new WaitForFixedUpdate();
}
}
void Update()
{
//物体旋转
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit) && Input.GetMouseButton(1)&&hit.collider.tag==moveTarget.tag)//旋转的判断条件为射线接触到物体,且接触物体的标签是该脚本绑定物体的标签,且按下了鼠标右键
{
//Input.GetAxis("MouseX")获取鼠标移动的X轴的距离
x += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
//欧拉角转化为四元数
Quaternion rotation = Quaternion.Euler(y, x, 0);
transform.rotation = rotation;
}
//物体缩放
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;
}
}
}
该move脚本是受到了这些文章(射线检测link)(unity使用鼠标控制移动、缩放、旋转link)的影响,自己改进了一些,达到了鼠标指在那个物体,那个物体就动的效果。当然还可以添加高亮组件link。
该案例使用的是同轴约束装配。利用两个标记点计算出它们之间的欧拉角和位置向量,再利用算出的欧拉角和位置向量来修正两个物体之间的位置和旋转角,以达到两个标记点在一定条件下重合的效果。同时可以通过移动标记点mark,实现缸柱塞的伸缩运动。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Assembly2 : MonoBehaviour
{
public Transform thisObject;//本身的接口
public Transform targetObject;//装配目标的接口
private float d;//两个物体之间的距离
public Transform mark;//本身的标记
public Transform targetMark;//装配目标的标记
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
d = Vector3.Distance(targetObject.position, thisObject.position);//算出两物体之间的距离向量
Vector3 moveVector = mark.transform.position - targetMark.transform.position;//算出两标记之间的距离向量
Vector3 RotateAixX = Vector3.Cross(targetObject.transform.up, thisObject.transform.up);//算出两物体y方向向量的叉乘
float angleX = Vector3.Angle(targetObject.transform.up, thisObject.transform.up);//算出两物体y方向向量的角度
if(d <= 5)//当两物体的距离小于5m时,开始标记重合
{
targetObject.transform.Rotate(RotateAixX, angleX, Space.World);
targetMark.transform.parent.transform.Translate(moveVector, Space.World);
}
if (Input.GetKey(KeyCode.W) && d <= 4)//按下W,进行缸体柱塞的伸出移动
{
mark.transform.Translate(Vector3.up * Time.deltaTime);
}
if (Input.GetKey(KeyCode.W) && d > 4)//限位
{
mark.transform.Translate(Vector3.down * 10*Time.deltaTime);
}
if (Input.GetKey(KeyCode.S) && d <= 4)//按下W,进行缸体柱塞的缩回移动
{
mark.transform.Translate(Vector3.down * Time.deltaTime);
}
if (Input.GetKey(KeyCode.S) && d > 4)//限位
{
mark.transform.Translate(Vector3.up * 10 * Time.deltaTime);
}
}
}
该脚本被我加载在Cylinder上,按照提示将相应的组件拖入
参考文章:使用unity进行零件装配link。
以markRotate标记为中心,绕x轴旋转,模拟铰链的旋转。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rotate1 : MonoBehaviour
{
public Transform rotateMark;//旋转标记
public Transform rotateObject;//旋转目标
public float rotateSpeed = 1.0f;//旋转速度
private float x = 0.0f;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Vector3 rotateAxis = rotateMark.transform.right;//将x轴记录在rotateAxis中
if (Input.GetKey(KeyCode.A))//正转
{
x = rotateSpeed * Time.deltaTime;
rotateObject.RotateAround(rotateMark.position, rotateAxis, x);//以rotateMark的位置为中心,绕x轴,旋转x角度。
}
if(Input.GetKey(KeyCode.D))//反转
{
x = -(rotateSpeed * Time.deltaTime);
rotateObject.RotateAround(rotateMark.position, rotateAxis, x);
}
}
}
做完这些,就可以达到最终效果啦!
请各位大佬多多指教,希望大家可以分享一些优秀的虚拟装配相关文章给我。