通用的设备运动同步
通过获取实时采集运动位置,发送到unity程序中,通过比例运算,转换成模型的运动位置,实现虚实同步。
在工业设备中,复杂的运动进行分析、分解,最本质的的运动就是平移和转动,在空间上就是3个维度的平移和旋转,包括:x轴平移,y轴平移,z轴平移,x轴旋转,Y轴旋转,Z轴旋转。
当然根据接收的数据不同,可能有的是一维运动,有的是多个维度合并一起运动。
实现实控虚的主要几个点:
- 初始化位置映射,实际设备确定一个零点,在模型需要找到对应零点,记录当前位置
- 比例换算,模型显示跟实际尺寸存在非完全一致或者单位换算的问题,对部件进行平移需要换算,旋转角度则不需要
- 旋转中心点调整
实现运动控制脚本:
主要实现一个运动控制基类MoveBase
和3个维度平移和旋转控制(6个脚本)
基类MoveBase
实现
首先实现初始化
InitPosition
变量用于存储当前部件模型的初始化位置,虚函数InitLocation()
用于根据InitPosition
值和不同类型的运动进行不同初始化,具体初始化内容在子类中实现。
/// <summary>
/// 初始化位置
/// </summary>
public float InitPosition;
/// <summary>
/// 初始化位置参数
/// </summary>
public virtual void InitLocation()
{
}
比例换算:
在平移中:
(模型位置 − 模型初始位置) 模型可运动长度 = 实际位置 实际可运动长度 \frac{(模型位置-模型初始位置)}{模型可运动长度}=\frac{实际位置}{实际可运动长度} 模型可运动长度(模型位置−模型初始位置)=实际可运动长度实际位置
在旋转:
模型当前角度 = 模型偏转零点 + 转动角度 模型当前角度=模型偏转零点+转动角度 模型当前角度=模型偏转零点+转动角度
设置了四个参数,DataPosition
是传过来的位置,就是实际位置;NowPosition
是当前位置,就是模型当前位置;MoveLength
是模型可运动长度;ActualLength
是实际可运动长度;MoveLength
和ActualLength
只有在平移运动式有效,如果是旋转则默认设置为0,不处理。
/// <summary>
/// 传过来的位置
/// </summary>
protected float DataPosition;
/// <summary>
/// 当前位置
/// </summary>
public float NowPosition;
/// <summary>
/// 模型可移动长度,比例计算
/// </summary>
public float MoveLength = 1;
/// <summary>
/// 实际可移动长度
/// </summary>
public float ActualLength = 1;
运算公式:
模型实时位置 = 模型初始化位置 + 实际位置 ∗ 模型可移动长度 实际可移动长度 模型实时位置=模型初始化位置+\frac{实际位置*模型可移动长度}{实际可移动长度} 模型实时位置=模型初始化位置+实际可移动长度实际位置∗模型可移动长度
在脚本FixedUpdate()
中实时更新模型位置,先使用公式计算当前模型位置,然后调用UpdateModelPosition()
更新位置,具体更新在对应子类中转换为坐标更新或者角度旋转。
private void FixedUpdate()
{
if (IsChange())
{
NowPosition = InitPosition + DataPosition * MoveLength / ActualLength;
UpdateModelPosition();
}
}
/// <summary>
/// 更新位置
/// </summary>
public virtual void UpdateModelPosition()
{
}
MoveBase
基类完整脚本:
/// <summary>
/// 运动控制基类(平移、旋转)
/// </summary>
public class MoveBase : MonoBehaviour
{
/// <summary>
/// 运动控制对象
/// </summary>
public Transform MoveObject;
/// <summary>
/// 传过来的位置
/// </summary>
protected float DataPosition;
/// <summary>
/// 初始化位置
/// </summary>
public float InitPosition;
/// <summary>
/// 当前位置
/// </summary>
public float NowPosition;
/// <summary>
/// 模型移动长度,比例计算
/// </summary>
public float MoveLength = 1;
/// <summary>
/// 实际移动长度
/// </summary>
public float ActualLength = 1;
/// <summary>
/// 设置实时数据
/// </summary>
/// <param name="pos"></param>
public virtual void SetDataPos(float pos)
{
DataPosition = pos;
}
public bool IsChange()
{
if (NowPosition != DataPosition)
{
return true;
}
else
{
return false;
}
}
void Start()
{
MoveObject = gameObject.transform;
}
/// <summary>
/// 初始化位置参数
/// </summary>
public virtual void InitLocation()
{
}
private void FixedUpdate()
{
if (IsChange())
{
NowPosition = InitPosition + DataPosition * MoveLength / ActualLength;
UpdateModelPosition();
}
}
/// <summary>
/// 更新位置
/// </summary>
public virtual void UpdateModelPosition()
{
}
}
各个维度控制脚本:
X轴平移:
public class XMove : MoveBase
{
public override void InitLocation()
{
MoveObject.localPosition = new Vector3(InitPosition, MoveObject.localPosition.y, MoveObject.localPosition.z);
}
public override void UpdateModelPosition()
{
MoveObject.localPosition = new Vector3(NowPosition, MoveObject.localPosition.y, MoveObject.localPosition.z);
}
}
Y轴平移:
public class YMove : MoveBase
{
public override void InitLocation()
{
MoveObject.localPosition = new Vector3(MoveObject.localPosition.x, InitPosition, MoveObject.localPosition.z);
}
public override void UpdateModelPosition()
{
MoveObject.localPosition = new Vector3(MoveObject.localPosition.x, NowPosition, MoveObject.localPosition.z);
}
}
Z轴平移:
public class ZMove : MoveBase
{
public override void InitLocation()
{
MoveObject.localPosition = new Vector3(MoveObject.localPosition.x, MoveObject.localPosition.y, InitPosition);
}
public override void UpdateModelPosition()
{
MoveObject.localPosition = new Vector3(MoveObject.localPosition.x, MoveObject.localPosition.y, NowPosition);
}
}
X轴旋转:
public class XRotate : MoveBase
{
public override void InitLocation()
{
MoveObject.localRotation = Quaternion.Euler(InitPosition, MoveObject.localPosition.y, MoveObject.localPosition.z);
}
public override void UpdateModelPosition()
{
MoveObject.localRotation = Quaternion.Euler(NowPosition, MoveObject.localPosition.y, MoveObject.localPosition.z);
}
}
Y轴旋转:
public class YRotate : MoveBase
{
public override void InitLocation()
{
MoveObject.localRotation = Quaternion.Euler(MoveObject.localPosition.x, InitPosition, MoveObject.localPosition.z);
}
public override void UpdateModelPosition()
{
MoveObject.localRotation = Quaternion.Euler(MoveObject.localPosition.x, NowPosition, MoveObject.localPosition.z);
}
}
Z轴旋转:
public class ZRotate : MoveBase
{
public override void InitLocation()
{
MoveObject.localRotation = Quaternion.Euler(MoveObject.localPosition.x, MoveObject.localPosition.y, InitPosition);
}
public override void UpdateModelPosition()
{
MoveObject.localRotation = Quaternion.Euler(MoveObject.localPosition.x, MoveObject.localPosition.y, NowPosition);
}
}
测试:
创建两个Cube物体用于测试
在MoveCube上面挂上Zmove脚本,并且修改脚本参数用于模拟运动
写一个测试的控制脚本,将他挂在随便一个物体上(相机或者Cube),将MoveCube物体复制给Move参数,然后运行,就可以看到物体运动。
public class MoveTest : MonoBehaviour
{
public MoveBase Move;
int pos = 0;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void FixedUpdate()
{
pos++;
Move.SetDataPos(pos);
}
}
效果: