勇哥注:
夹子的应用有我们常见的气缸。它的基本原理就是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;
}

