勇哥注:
夹子的应用有我们常见的气缸。它的基本原理就是Fixed Joint 连接器。
如下图所示,那个Cube添加Fixed Joint链接器后,配置好“已连接实体”,即可以实现链接。
当你移动Cube时,球体会跟随移动


下面构建夹子的简易演示,工程对象如下图所示:

(图1)

(图2)
对base添加夹子脚本

(图3)
先在“场景”视图把夹子对象降下来。

(图4)
然后转到 “游戏”视图,按space键把夹子夹住球体。

(图5)
再转到“场景”把夹子抬上去。

(图6)
转到“游戏”视图后,可以观察到球体被夹子带上来。
此时按space键,可以松开夹子,可以观察到球体就会掉下来。

(图7)
夹子代码:
public class Clip_Function : MonoBehaviour
{
public GameObject clipBase;
public GameObject leftClip;
public GameObject rightClip;
public GameObject targetObject;
public float clipDistance = 0.01f;
public float clipTime = 1f;
public bool grabbed = false;
bool working = false;
GameObject triggedObject;
GameObject clippedObject;
[Range(-1, 1)]
int grabDirection = 0;
[Header("debug")]
public KeyCode debugKeycode;
private void Start()
{
clipBase.AddComponent<FixedJoint>();
clipBase.GetComponent<Rigidbody>().isKinematic = true;
}
public IEnumerator ClipFunction()
{
if (working == false)
{
if (grabDirection == 0)
{
grabDirection = 1;
}
else
{
grabDirection = -grabDirection;
}
working = true;
for (int i = 0; i < 60; i++)
{
leftClip.transform.Translate(new Vector3(0, 0, -grabDirection * clipDistance / 60));
rightClip.transform.Translate(new Vector3(0, 0, grabDirection * clipDistance / 60));
yield return new WaitForSecondsRealtime(clipTime / 60);
}
working = false;
if (clipBase.GetComponent<FixedJoint>().connectedBody == null)
{
if (triggedObject != null)
{
clippedObject = triggedObject;
clipBase.GetComponent<FixedJoint>().connectedBody = clippedObject.GetComponent<Rigidbody>();
grabbed = true;
}
}
else
{
clippedObject.GetComponent<Rigidbody>().isKinematic = true;
clippedObject.GetComponent<Rigidbody>().isKinematic = false;
clippedObject = null;
clipBase.GetComponent<FixedJoint>().connectedBody = null;
grabbed = false;
}
}
}
private void OnTriggerStay(Collider other)
{
if(other.name.Contains(targetObject.name))triggedObject = other.gameObject;
}
public void ClipWorking()
{
if (working == false)
{
StartCoroutine("ClipFunction");
}
}
#region debug
private void Update()
{
if (Input.GetKeyDown(debugKeycode))
{
ClipWorking();
}
}
#endregion
}下面勇哥解释一下代码。
(1)脚本的字段
public GameObject clipBase; //两个夹子中间那个cube,做为触发器来使用了
public GameObject leftClip; //左夹子
public GameObject rightClip; //右夹子
public GameObject targetObject; //要被夹住的对象,可以是任何东西
public float clipDistance = 0.01f; //夹子移动的距离
public float clipTime = 1f; //夹子开合的速度
public bool grabbed = false; //夹住时为true,否则为false
对应下图所示的内容:

(2)控制夹子开合方向
[Range(-1, 1)]
int grabDirection = 0;
Range是一个特性。
(3)调试按键
[Header("debug")]
public KeyCode debugKeycode;
(4) 设置Fixed Joint链接器
private void Start()
{
clipBase.AddComponent<FixedJoint>();
clipBase.GetComponent<Rigidbody>().isKinematic = true;
}
(5)夹子功能主函数
public IEnumerator ClipFunction()
{}
它是一个标准的协和函数,这个是unity里特有的。
(6)触发器的触发函数
private void OnTriggerStay(Collider other)
{
if(other.name.Contains(targetObject.name))triggedObject = other.gameObject;
}
(7)按键调试用的函数
private void Update()
{
if (Input.GetKeyDown(debugKeycode))
{
ClipWorking();
}
}
下面来看下夹子的那个主功能函数ClipFunction()
(1)下在这句是为了防止重复触发。
if (working == false)
(2)下面这几句是夹子开合方向的控制。
if (grabDirection == 0)
{
grabDirection = 1;
}
else
{
grabDirection = -grabDirection;
}
(3)下面是夹子开合动画的绘制。
for (int i = 0; i < 60; i++)
{
leftClip.transform.Translate(new Vector3(0, 0, -grabDirection * clipDistance / 60));
rightClip.transform.Translate(new Vector3(0, 0, grabDirection * clipDistance / 60));
yield return new WaitForSecondsRealtime(clipTime / 60);
}
yield return 这个是协程的标准写法。
它是让循环在规定时间点结束。
(4)夹子开/合时联接器的设置。
如果夹住时链接器生效,小球会被带走; 如果松开夹子时链接器失效,小球就会掉下来。
if (clipBase.GetComponent<FixedJoint>().connectedBody == null)
{
if (triggedObject != null)
{
clippedObject = triggedObject;
clipBase.GetComponent<FixedJoint>().connectedBody = clippedObject.GetComponent<Rigidbody>();
grabbed = true;
}
}
else
{
clippedObject.GetComponent<Rigidbody>().isKinematic = true;
clippedObject.GetComponent<Rigidbody>().isKinematic = false;
clippedObject = null;
clipBase.GetComponent<FixedJoint>().connectedBody = null;
grabbed = false;
}