在碰撞回调中防止 Unity Physics 中的碰撞力
Prevent Collision Forces in Unity Physics in Collision Callback
如何防止碰撞在 Unity 中施加力?我正在使用 2D 物理并希望将箭头插入板条箱。我可以很容易地在碰撞回调中移除刚体和碰撞器,但似乎仍然对箭头施加了一帧碰撞力,导致位置和旋转略有跳跃。碰撞回调中刚体上的设置 isKinematic 似乎也不会阻止施加这一帧力。
我希望告诉 Unity 不要对碰撞应用物理。
在箭头的生命周期中使用运动学不是一种选择,因为箭头需要真实地飞行直到它击中某物。
这是处理碰撞的板条箱对象的代码:
protected virtual void HandleCollision(ArrowScript arrow, Collision2D coll)
{
StickArrow(arrow, coll);
if (DestroyAfterSeconds >= 0.0f)
{
Destroy(arrow.gameObject, DestroyAfterSeconds);
}
}
private void OnCollisionEnter2D(Collision2D coll)
{
ArrowScript script = coll.gameObject.GetComponent<ArrowScript>();
if (script != null)
{
HandleCollision(script, coll);
}
}
private bool StickArrow(ArrowScript arrow, Collision2D coll)
{
Vector2 surfaceNormal = coll.contacts[0].normal;
float surfaceAngle = Mathf.Atan2(surfaceNormal.y, surfaceNormal.x);
float arrowAngle = Mathf.PI + (arrow.transform.eulerAngles.z * Mathf.Deg2Rad);
float angleDifference = Mathf.Abs(BowAndArrowUtilities.DifferenceBetweenAngles(surfaceAngle, arrowAngle));
float penetration = arrow.PercentPenetration * PenetrationPercentageModifier * (1.0f - angleDifference);
if (penetration <= MinimumPenetrationPercentage)
{
arrow.PercentPenetration = 0.0f;
return false;
}
// Make the arrow a child of the thing it's stuck to
arrow.transform.parent = transform;
arrow.gameObject.transform.Translate(new Vector3(-penetration * arrow.Length, 0.0f, 0.0f));
SpriteRenderer thisSpriteRenderer = GetComponent<SpriteRenderer>();
if (thisSpriteRenderer != null)
{
arrow.GetComponent<SpriteRenderer>().sortingLayerID = thisSpriteRenderer.sortingLayerID;
arrow.GetComponent<SpriteRenderer>().sortingOrder = Mathf.Max(0, thisSpriteRenderer.sortingOrder - 1);
}
BowAndArrowUtilities.PlayRandomSound(arrow.CollisionAudioClips, penetration * 5.0f);
// destroy physics objects from the arrow (rigid bodies, colliders, etc.). This unfortunately doesn't prevent this frame from apply force (rotation, position) to the arrow.
arrow.DestroyPhysicsObjects();
return true;
}
Unity 版本为 5.3.4。
我最后把箭头变成了扳机。在 OnTriggerEnter2D 内部,然后我在箭头指向的方向上执行一个圆形投射,宽度为箭头精灵。触发器不受 Unity 物理计算的影响。
private void OnTriggerEnter2D(Collider2D coll)
{
ArrowScript script = coll.gameObject.GetComponent<ArrowScript>();
if (script != null)
{
Vector2 dir = -script.ArrowHead.transform.right;
// ray cast with the arrow size y value (thickness of arrow)
RaycastHit2D[] hits = Physics2D.CircleCastAll(script.ArrowHead.transform.position, script.Size.y, dir);
foreach (RaycastHit2D hit in hits)
{
// collider2d is a member variable assigned in Start that is the Collider2D for this object
if (hit.collider == collider2d)
{
HandleCollision(script, hit.normal);
break;
}
}
}
}
您的问题是在解决所有碰撞后调用 OnCollisionEnter 和 OnTriggerEnter。
在不影响任何东西的情况下解决这个问题的最简单方法是更改框、箭头或两者的重量。
将板条箱的重量设置为高值,将箭头的重量设置为低值。
另一种方法是使用触发碰撞器,正如您所做的那样。然而,触发对撞机有问题的副作用。例如,它不会在板条箱上调用 OnCollisionEnter 或 OnTriggerEnter。您将必须执行箭头脚本中的所有逻辑,这不是什么大问题。
然而,还有很多其他丑陋的技巧。您可以在撞击后将箱子的速度设置为 0,但如果您在箱子移动时击中它,它会冻结箱子。您可以使用碰撞信息来取消施加在板条箱上的力以解决碰撞。您可以保存每帧板条箱的最后速度,并在 OnCollision 调用期间将其重新应用于刚体。
我不建议这些,但它们是可能的。
如何防止碰撞在 Unity 中施加力?我正在使用 2D 物理并希望将箭头插入板条箱。我可以很容易地在碰撞回调中移除刚体和碰撞器,但似乎仍然对箭头施加了一帧碰撞力,导致位置和旋转略有跳跃。碰撞回调中刚体上的设置 isKinematic 似乎也不会阻止施加这一帧力。
我希望告诉 Unity 不要对碰撞应用物理。
在箭头的生命周期中使用运动学不是一种选择,因为箭头需要真实地飞行直到它击中某物。
这是处理碰撞的板条箱对象的代码:
protected virtual void HandleCollision(ArrowScript arrow, Collision2D coll)
{
StickArrow(arrow, coll);
if (DestroyAfterSeconds >= 0.0f)
{
Destroy(arrow.gameObject, DestroyAfterSeconds);
}
}
private void OnCollisionEnter2D(Collision2D coll)
{
ArrowScript script = coll.gameObject.GetComponent<ArrowScript>();
if (script != null)
{
HandleCollision(script, coll);
}
}
private bool StickArrow(ArrowScript arrow, Collision2D coll)
{
Vector2 surfaceNormal = coll.contacts[0].normal;
float surfaceAngle = Mathf.Atan2(surfaceNormal.y, surfaceNormal.x);
float arrowAngle = Mathf.PI + (arrow.transform.eulerAngles.z * Mathf.Deg2Rad);
float angleDifference = Mathf.Abs(BowAndArrowUtilities.DifferenceBetweenAngles(surfaceAngle, arrowAngle));
float penetration = arrow.PercentPenetration * PenetrationPercentageModifier * (1.0f - angleDifference);
if (penetration <= MinimumPenetrationPercentage)
{
arrow.PercentPenetration = 0.0f;
return false;
}
// Make the arrow a child of the thing it's stuck to
arrow.transform.parent = transform;
arrow.gameObject.transform.Translate(new Vector3(-penetration * arrow.Length, 0.0f, 0.0f));
SpriteRenderer thisSpriteRenderer = GetComponent<SpriteRenderer>();
if (thisSpriteRenderer != null)
{
arrow.GetComponent<SpriteRenderer>().sortingLayerID = thisSpriteRenderer.sortingLayerID;
arrow.GetComponent<SpriteRenderer>().sortingOrder = Mathf.Max(0, thisSpriteRenderer.sortingOrder - 1);
}
BowAndArrowUtilities.PlayRandomSound(arrow.CollisionAudioClips, penetration * 5.0f);
// destroy physics objects from the arrow (rigid bodies, colliders, etc.). This unfortunately doesn't prevent this frame from apply force (rotation, position) to the arrow.
arrow.DestroyPhysicsObjects();
return true;
}
Unity 版本为 5.3.4。
我最后把箭头变成了扳机。在 OnTriggerEnter2D 内部,然后我在箭头指向的方向上执行一个圆形投射,宽度为箭头精灵。触发器不受 Unity 物理计算的影响。
private void OnTriggerEnter2D(Collider2D coll)
{
ArrowScript script = coll.gameObject.GetComponent<ArrowScript>();
if (script != null)
{
Vector2 dir = -script.ArrowHead.transform.right;
// ray cast with the arrow size y value (thickness of arrow)
RaycastHit2D[] hits = Physics2D.CircleCastAll(script.ArrowHead.transform.position, script.Size.y, dir);
foreach (RaycastHit2D hit in hits)
{
// collider2d is a member variable assigned in Start that is the Collider2D for this object
if (hit.collider == collider2d)
{
HandleCollision(script, hit.normal);
break;
}
}
}
}
您的问题是在解决所有碰撞后调用 OnCollisionEnter 和 OnTriggerEnter。
在不影响任何东西的情况下解决这个问题的最简单方法是更改框、箭头或两者的重量。
将板条箱的重量设置为高值,将箭头的重量设置为低值。
另一种方法是使用触发碰撞器,正如您所做的那样。然而,触发对撞机有问题的副作用。例如,它不会在板条箱上调用 OnCollisionEnter 或 OnTriggerEnter。您将必须执行箭头脚本中的所有逻辑,这不是什么大问题。
然而,还有很多其他丑陋的技巧。您可以在撞击后将箱子的速度设置为 0,但如果您在箱子移动时击中它,它会冻结箱子。您可以使用碰撞信息来取消施加在板条箱上的力以解决碰撞。您可以保存每帧板条箱的最后速度,并在 OnCollision 调用期间将其重新应用于刚体。
我不建议这些,但它们是可能的。