Unity - 如果对象与子对象发生碰撞,如何锚定到对象
Unity - How to Anchor to an Object if it Collides With a Child Object
我正在开发的游戏中有一个伸缩尖峰小工具。尖刺应该是玩家的手臂附件,一旦尖刺尖端(最深的子对象)接触到某物,玩家就可以粘在墙上。本质上,我希望它“刺入”墙壁并将播放器固定到位。
我最接近的方法是将“接触对模式”设置为“启用运动学运动学对”,然后在尖端添加一个运动学 RigidBody 和一个 FixedJoint。这是尖峰本身的代码:
private IEnumerator _ExpandSequence()
{
mExpanding = true;
if (mCollapsing)
{
yield return new WaitUntil(() => !mCollapsing);
}
mBaseGoal = transform.localPosition + mBaseDistance * Vector3.up;
mConeGoal = mSpikeSections[5].localPosition + mConeDistance * Vector3.up;
while (transform.localPosition.y > mBaseSpikeEnd || mSpikeSections[1].localPosition.y > mCylinderEnd
|| mSpikeSections[5].localPosition.y > mConeEnd)
{ //only using the first cylinder section in the condition since they all move the same distance
transform.localPosition = Vector3.MoveTowards(transform.localPosition, mBaseGoal, mSpeed);
mSpikeSections[1].localPosition = mSpikeSections[2].localPosition = mSpikeSections[3].localPosition =
mSpikeSections[4].localPosition = Vector3.MoveTowards(mSpikeSections[1].localPosition, Vector3.up * mCylinderDistance, mSpeed);
mSpikeSections[5].localPosition = Vector3.MoveTowards(mSpikeSections[5].localPosition, mConeGoal, mSpeed);
if (Mathf.Approximately(transform.localPosition.y, mBaseSpikeEnd) &&
Mathf.Approximately(mSpikeSections[1].localPosition.y, mCylinderEnd) &&
Mathf.Approximately(mSpikeSections[5].localPosition.y, mConeEnd))
{
transform.localPosition = mBaseGoal;
mSpikeSections[5].localPosition = mConeGoal;
break;
}
yield return null;
}
mExpanding = false;
mExtended = true;
}
这是尖峰碰撞的代码:
private void OnCollisionEnter(Collision col)
{
if (col.gameObject.tag == "Enemy" || col.gameObject.tag == "Environment")
{
//Anchor
mFj.connectedBody = col.rigidbody;
}
else
{
//TODO
}
}
这是玩家资产的层次结构:
目前,当尖刺与表面碰撞时,我可以在编辑器中看到连接的物体已注册
尽管如此,玩家仍会反弹并能够自由移动,就好像关节不存在一样。谁能帮我找出为什么播放器没有固定在墙上?
TL;DR:如何让具有 RigidBody 的子对象通过 FixedJoint 连接到另一个对象?
首先,在tip上创建两个FixedJoint组件,并将它们分配给脚本中的字段。将玩家的刚体分配给脚本中的一个字段。将尖端的刚体和碰撞器分配给脚本中的字段。
其次,当碰撞发生时,关闭 isKinematic,这样尖端会影响关节并受关节影响(例如,如果敌人移动,它会跟随)
第三,当你想结束锚点时,你应该能够再次将 isKinematic 设置为 true,将其两个关节连接的身体分配给 null,并在必要时禁用其碰撞器以避免
[SerializeField] FixedJoint mFixedJointTarget; // 1st fixed joint on tip
[SerializeField] FixedJoint mFixedJointPlayer; // 2nd fixed joint on tip
[SerializeField] Rigidbody myRB; // rigidbody of tip
[SerializeField] Rigidbody playerRB; // player's rigidbody
[SerializeField] Collider myColl; // if necessary to avoid re-attachment
private void OnCollisionEnter(Collision col)
{
if (col.gameObject.tag == "Enemy" || col.gameObject.tag == "Environment")
{
StartAnchor(col.rigidbody);
}
}
void StartAnchor(Rigidbody targetRB)
{
myRB.isKinematic = false;
mFixedJointTarget.connectedBody = targetRB;
mFixedJointPlayer.connectedBody = playerRB;
}
void StopAnchor()
{
myRB.isKinematic = true;
mFixedJointTarget.connectedBody = null;
mFixedJointPlayer.connectedBody = null;
// re-enable before shooting again, of course
// may not be necessary depending on what else you do when you stop anchoring.
myColl.enabled = false;
}
如果你愿意,你可以对尖端永远不会运动进行同样的操作,你只需要 Destroy
关节而不是将 connectedBody
设置为空,并且还创建关节 仅 当发生碰撞时,这可能如下所示:
//on drill with useGravity = false
public class DrillShot : MonoBehaviour
{
FixedJoint mFixedJointTarget; // 1st fixed joint on tip
FixedJoint mFixedJointPlayer; // 2nd fixed joint on tip
Rigidbody rb;
[SerializeField] Rigidbody playerRB; // player's rigidbody
Renderer rend;
[SerializeField] float speed = 10f;
void Start()
{
rb = GetComponent<Rigidbody>();
rend = GetComponent<Renderer>();
}
private void OnCollisionEnter(Collision col)
{
if (col.gameObject.tag == "Enemy" || col.gameObject.tag == "Environment")
{
StartAnchor(col.rigidbody);
}
}
void StartAnchor(Rigidbody targetRB)
{
if (mFixedJointTarget != null)
{
Destroy(mFixedJointTarget);
}
if (mFixedJointPlayer != null)
{
Destroy(mFixedJointPlayer);
}
mFixedJointPlayer = gameObject.AddComponent<FixedJoint>();
mFixedJointPlayer.connectedBody = playerRB;
mFixedJointTarget = gameObject.AddComponent<FixedJoint>();
mFixedJointTarget.connectedBody = targetRB;
}
void StopAnchor()
{
if (mFixedJointTarget != null)
{
Destroy(mFixedJointTarget);
}
if (mFixedJointPlayer != null)
{
Destroy(mFixedJointPlayer);
}
rend.enabled = false;
}
void Shoot()
{
rend.enabled = true;
transform.position = playerRB.position + Vector3.right;
rb.velocity = Vector3.right * speed;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.E))
{
Shoot();
}
else if (Input.GetKeyDown(KeyCode.R))
{
StopAnchor();
}
}
}
// on wall, with rigidbody tagged "Environment",
// xyz freeze rotation mass 100, use gravity = false
public class MoveRandomly : MonoBehaviour
{
Vector3 curTarget;
Vector3 startPos;
[SerializeField] float switchTargetPeriod = 2f;
[SerializeField] float targetRadius = 1f;
[SerializeField] float dampener = 0.05f;
Rigidbody rb;
float t = 0f;
void Start()
{
rb = GetComponent<Rigidbody>();
startPos = transform.position;
}
void FixedUpdate()
{
t -= Time.deltaTime;
if (t < 0)
{
t = switchTargetPeriod;
curTarget = startPos + Random.insideUnitSphere * targetRadius;
}
Vector3 diff = curTarget - transform.position;
rb.AddForce(diff * dampener, ForceMode.VelocityChange);
}
}
我正在开发的游戏中有一个伸缩尖峰小工具。尖刺应该是玩家的手臂附件,一旦尖刺尖端(最深的子对象)接触到某物,玩家就可以粘在墙上。本质上,我希望它“刺入”墙壁并将播放器固定到位。
我最接近的方法是将“接触对模式”设置为“启用运动学运动学对”,然后在尖端添加一个运动学 RigidBody 和一个 FixedJoint。这是尖峰本身的代码:
private IEnumerator _ExpandSequence()
{
mExpanding = true;
if (mCollapsing)
{
yield return new WaitUntil(() => !mCollapsing);
}
mBaseGoal = transform.localPosition + mBaseDistance * Vector3.up;
mConeGoal = mSpikeSections[5].localPosition + mConeDistance * Vector3.up;
while (transform.localPosition.y > mBaseSpikeEnd || mSpikeSections[1].localPosition.y > mCylinderEnd
|| mSpikeSections[5].localPosition.y > mConeEnd)
{ //only using the first cylinder section in the condition since they all move the same distance
transform.localPosition = Vector3.MoveTowards(transform.localPosition, mBaseGoal, mSpeed);
mSpikeSections[1].localPosition = mSpikeSections[2].localPosition = mSpikeSections[3].localPosition =
mSpikeSections[4].localPosition = Vector3.MoveTowards(mSpikeSections[1].localPosition, Vector3.up * mCylinderDistance, mSpeed);
mSpikeSections[5].localPosition = Vector3.MoveTowards(mSpikeSections[5].localPosition, mConeGoal, mSpeed);
if (Mathf.Approximately(transform.localPosition.y, mBaseSpikeEnd) &&
Mathf.Approximately(mSpikeSections[1].localPosition.y, mCylinderEnd) &&
Mathf.Approximately(mSpikeSections[5].localPosition.y, mConeEnd))
{
transform.localPosition = mBaseGoal;
mSpikeSections[5].localPosition = mConeGoal;
break;
}
yield return null;
}
mExpanding = false;
mExtended = true;
}
这是尖峰碰撞的代码:
private void OnCollisionEnter(Collision col)
{
if (col.gameObject.tag == "Enemy" || col.gameObject.tag == "Environment")
{
//Anchor
mFj.connectedBody = col.rigidbody;
}
else
{
//TODO
}
}
这是玩家资产的层次结构:
目前,当尖刺与表面碰撞时,我可以在编辑器中看到连接的物体已注册
尽管如此,玩家仍会反弹并能够自由移动,就好像关节不存在一样。谁能帮我找出为什么播放器没有固定在墙上?
TL;DR:如何让具有 RigidBody 的子对象通过 FixedJoint 连接到另一个对象?
首先,在tip上创建两个FixedJoint组件,并将它们分配给脚本中的字段。将玩家的刚体分配给脚本中的一个字段。将尖端的刚体和碰撞器分配给脚本中的字段。
其次,当碰撞发生时,关闭 isKinematic,这样尖端会影响关节并受关节影响(例如,如果敌人移动,它会跟随)
第三,当你想结束锚点时,你应该能够再次将 isKinematic 设置为 true,将其两个关节连接的身体分配给 null,并在必要时禁用其碰撞器以避免
[SerializeField] FixedJoint mFixedJointTarget; // 1st fixed joint on tip
[SerializeField] FixedJoint mFixedJointPlayer; // 2nd fixed joint on tip
[SerializeField] Rigidbody myRB; // rigidbody of tip
[SerializeField] Rigidbody playerRB; // player's rigidbody
[SerializeField] Collider myColl; // if necessary to avoid re-attachment
private void OnCollisionEnter(Collision col)
{
if (col.gameObject.tag == "Enemy" || col.gameObject.tag == "Environment")
{
StartAnchor(col.rigidbody);
}
}
void StartAnchor(Rigidbody targetRB)
{
myRB.isKinematic = false;
mFixedJointTarget.connectedBody = targetRB;
mFixedJointPlayer.connectedBody = playerRB;
}
void StopAnchor()
{
myRB.isKinematic = true;
mFixedJointTarget.connectedBody = null;
mFixedJointPlayer.connectedBody = null;
// re-enable before shooting again, of course
// may not be necessary depending on what else you do when you stop anchoring.
myColl.enabled = false;
}
如果你愿意,你可以对尖端永远不会运动进行同样的操作,你只需要 Destroy
关节而不是将 connectedBody
设置为空,并且还创建关节 仅 当发生碰撞时,这可能如下所示:
//on drill with useGravity = false
public class DrillShot : MonoBehaviour
{
FixedJoint mFixedJointTarget; // 1st fixed joint on tip
FixedJoint mFixedJointPlayer; // 2nd fixed joint on tip
Rigidbody rb;
[SerializeField] Rigidbody playerRB; // player's rigidbody
Renderer rend;
[SerializeField] float speed = 10f;
void Start()
{
rb = GetComponent<Rigidbody>();
rend = GetComponent<Renderer>();
}
private void OnCollisionEnter(Collision col)
{
if (col.gameObject.tag == "Enemy" || col.gameObject.tag == "Environment")
{
StartAnchor(col.rigidbody);
}
}
void StartAnchor(Rigidbody targetRB)
{
if (mFixedJointTarget != null)
{
Destroy(mFixedJointTarget);
}
if (mFixedJointPlayer != null)
{
Destroy(mFixedJointPlayer);
}
mFixedJointPlayer = gameObject.AddComponent<FixedJoint>();
mFixedJointPlayer.connectedBody = playerRB;
mFixedJointTarget = gameObject.AddComponent<FixedJoint>();
mFixedJointTarget.connectedBody = targetRB;
}
void StopAnchor()
{
if (mFixedJointTarget != null)
{
Destroy(mFixedJointTarget);
}
if (mFixedJointPlayer != null)
{
Destroy(mFixedJointPlayer);
}
rend.enabled = false;
}
void Shoot()
{
rend.enabled = true;
transform.position = playerRB.position + Vector3.right;
rb.velocity = Vector3.right * speed;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.E))
{
Shoot();
}
else if (Input.GetKeyDown(KeyCode.R))
{
StopAnchor();
}
}
}
// on wall, with rigidbody tagged "Environment",
// xyz freeze rotation mass 100, use gravity = false
public class MoveRandomly : MonoBehaviour
{
Vector3 curTarget;
Vector3 startPos;
[SerializeField] float switchTargetPeriod = 2f;
[SerializeField] float targetRadius = 1f;
[SerializeField] float dampener = 0.05f;
Rigidbody rb;
float t = 0f;
void Start()
{
rb = GetComponent<Rigidbody>();
startPos = transform.position;
}
void FixedUpdate()
{
t -= Time.deltaTime;
if (t < 0)
{
t = switchTargetPeriod;
curTarget = startPos + Random.insideUnitSphere * targetRadius;
}
Vector3 diff = curTarget - transform.position;
rb.AddForce(diff * dampener, ForceMode.VelocityChange);
}
}