前言
此前偶然有机会接触到unity做机械臂仿真的项目,期间参考了很多位大佬的文章,但仍然觉得参考资料实在是太少。于是作为一个unity小白想在这里分享一些在学习过程中遇到的问题以及项目的解决方案,供其他刚刚接触unity的小伙伴进行参考,如有讲得不对或者更好的解决方法欢迎各位大神斧正指出。
机械臂仿真
一、模型导入
第一步毫无疑问是导入已经准备好的模型(fbx格式)
(鉴于fbx格式的机械臂模型并不太容易找,可以通过solidworks先建模机械臂,然后通过3dmax转为fbx格式,在这里不对这部分过多赘述。)
对模型父子关系的基本处理
导入后需要先处理机械臂模型各个零件之间的父子关系,详情参考:
二、单个关节旋转固定角度的实现
我们以关节一(绕y轴旋转)为例。
旋转部分的代码参考了:
个人在代码中加入了UI按钮监听,具体代码如下:
1、声明变量。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/*
* 由点击触发
* 转动所需时间为t
* 则转动所需要的帧数为fps*t
* 每一帧转动一次
* 每次转动的角度为(sin((n/(fps*t))*(PI/2))-sin((n-1)/(fps*t)*(PI/2)))*angle,这里设置angle为90度
*/
public class Y_Link1_ : MonoBehaviour
{
float fps; //帧率
float t; //旋转所需的时间
float PI; //π
float currentFrame; //当前帧n
int angle = 60; //旋转的角度
Button Button_L;
Button Button_R;
bool isRotate_L = false;
bool isRotate_R = false;
2、在void_Start()中设置帧率及每次点击按钮关节旋转的角度(角度可自行更改)。
void Start()
{
fps = 60.0f;
t = 1.5f;
PI = 3.14159f;
currentFrame = 0.0f;
Button_L = GameObject.Find("Canvas/Link1_Left").GetComponent<Button>();
Button_L.onClick.AddListener(IsRotate_L);
Button_R = GameObject.Find("Canvas/Link1_Right").GetComponent<Button>(); //按钮监听
Button_R.onClick.AddListener(IsRotate_R);
}
3、代码实现关节旋转 。(来自NyankoKoKyo大佬的天才想法!)
void Update()
{
//示例:绕y轴转动
if (isRotate_L)
{
currentFrame += 1.0f;
transform.Rotate(0
, (Mathf.Sin(currentFrame / (fps * t) * (PI / 2.0f)) - Mathf.Sin((currentFrame - 1) / (fps * t) * (PI / 2.0f))) * angle
, 0);
if (currentFrame > fps * t)
{
isRotate_L = false;
currentFrame = 0.0f;
}
}
if (isRotate_R)
{
currentFrame += 1.0f;
transform.Rotate(0
, -(Mathf.Sin(currentFrame / (fps * t) * (PI / 2.0f)) - Mathf.Sin((currentFrame - 1) / (fps * t) * (PI / 2.0f))) * angle
, 0);
if (currentFrame > fps * t)
{
isRotate_R = false;
currentFrame = 0.0f;
}
}
}
private void Awake()
{
QualitySettings.vSyncCount = 0; //关闭垂直同步
Application.targetFrameRate = (int)fps; //设置帧率
}
public void IsRotate_L()
{
isRotate_L = true;
}
public void IsRotate_R()
{
isRotate_R = true;
}
}
UI按钮控件
在这里顺便也提一下按钮的使用,省的不熟悉的小伙伴再去翻其他文章绕的头晕。
1、右键层级——UI——按钮。
2、手动调整按钮的位置,使之出现在游戏画面中。
3、依照这个方法制作多几个按钮,并修改其名字及文本(名字在监听中会用到,注意每个按钮名字要加以区分 )——在Text中可以修改按钮上的文本。
4、给关节一(Pointer1)挂上脚本——直接将脚本拖动到对应关节处。
5、给按钮加上脚本 ——点击“+”——将对应的关节拖入到框中并挂上脚本监听。
到这里按钮这一步基本完成,其余关节同理。 (大致效果如图)
三、夹爪部分运动
关于夹爪部分运动,本人做的其实有点粗糙,并没有完全学到大佬的精髓,因此只提供一个过程的参考。
在夹爪运动部分,参考了大佬的文章,使用滑杆来控制夹爪的开合:
1、对Pointer5中的零件分类,分为左右爪和不运动的组件。
同样需要调整好旋转中心。
2、给夹爪添加碰撞检测。
然后编辑触发器的范围 !!!
将左边绿色框调整至合适的位置和大小。(可按住绿色点进行调整 )
大概这样子,右边夹爪同理。
3、给夹爪根节点(即Pointer5)加上刚体组件——关闭掉重力。
4、夹爪的运动脚本。(旋转步长可经过多次尝试修改到合适角度)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Claw : MonoBehaviour
{
bool isCLap = false;
GameObject fingerLeft;
GameObject fingerRight;
GameObject ClawTarget;
Slider ClawClap; //载入UI中控制手爪开合的滑动条
float RotateStep = 15; //旋转步长
void Start()
{
ClawClap = GameObject.Find("Canvas/Slider").GetComponent<Slider>();
ClawClap.value = 1; //手抓初始值为0,为打开状态
ClawClap.onValueChanged.AddListener(OnClap);
fingerLeft = GameObject.Find("Left");
fingerRight = GameObject.Find("Right");
}
public void OnClap(float Value)
{
//定义OnClap函数,传递一个0或1的值,代表手爪开合的命令
//也可以定义一个范围内的数值,使用滑动条控制手爪移动步长
if (Value==1&&isCLap==true)
{
fingerLeft.transform.Rotate(Vector3.back, -RotateStep);
fingerRight.transform.Rotate(Vector3.forward, -RotateStep);
isCLap = false;
Debug.Log("开爪");
}
else if (Value==0&&isCLap==false)
{
//接到夹爪命令
fingerLeft.transform.Rotate(Vector3.back, RotateStep);
fingerRight.transform.Rotate(Vector3.forward, RotateStep);
isCLap = true;
Debug.Log("闭爪");
}
}
private void OnTriggerStay(Collider other)
{
//使用碰撞监测,一旦夹到物体,立刻使其变成手爪的子节点
Debug.Log("碰到了"+other.name);
if (other.gameObject.tag=="Goods"&&isCLap == true)
{
ClawTarget=other.gameObject;
ClawTarget.GetComponent<Rigidbody>().isKinematic = true; //关闭物体的物理状态,防止撞飞
if (isCLap)
{
//碰到物体且闭合手爪后,让物体成为手爪的子对象,与手爪一起移动
ClawTarget.transform.SetParent(this.transform);
}
}
}
private void OnTriggerExit(Collider other)
{
//打开手爪且物体离开手爪时,让物体恢复重力,并且脱离父子关系,从手爪掉落
Debug.Log("没碰到" + other.name);
ClawTarget = other.gameObject;
if (other.tag == "Goods")
{
if (ClawTarget != null || isCLap == true)
{
ClawTarget.GetComponent<Rigidbody>().isKinematic = false;
ClawTarget.GetComponent<Rigidbody>().useGravity = true;
ClawTarget.GetComponent<Collider>().isTrigger = false;
ClawTarget.transform.SetParent(null);
}
else return;
}
}
void Update()
{
}
}
5、将脚本挂在Pointer5上, 然后新建一个UI Slider 滑杆控件,并挂上脚本监听,效果如图:
到这里夹爪就基本上完成。接下来我们演示一个最简单的夹持 :
四、物体夹持演示
在这部分,我们会新建一个立方体,并给他加上碰撞监测和刚体组件,来实现夹持功能。
1、先将已有的平面设置为提供接触和刚体。
2、新建Cube,并添加上刚体和碰撞监测组件,可添加材质以方便区分。
3、给Cube添加个Tag(标签)方便定向夹取。
即可实现夹持与松爪时物块的自由下落。
最后我们可加入其他的背景环境模型,使得整个游戏界面更加美观,也可以加入其他UI场景的切换使得游戏层次丰富。
总结
写这篇博客的初衷是想记录一下这次做简单项目的一些思路或者感想,也是我第一次在csdn写文章,如果有不足之处欢迎指出。想要源文件的同样可以私信
另外在夹持部分夹爪处做的其实比较粗糙、机械臂各个旋转关节处也未加上更加严谨的关节和刚体组件、仍然存在机械臂运动时碰到平面会穿模等等问题。本次做项目的时间有限,这些问题可以留给其他小伙伴一起商讨解决。