我在 Unity 中有一个非常基本的播放器,我编写了它来使用
Physics.ComputePenetration(...)
与对象进行碰撞,并且效果相对较好。问题是我想要有关玩家撞到碰撞体的哪里的更多详细信息。
一些注意事项:
首先,这个项目的想法是看看我是否可以使用
CharacterController
从头开始复制 ComputePenetration
的功能。我目前正在尝试复制内置的 OnControllerColliderHit(...)
函数,它允许我获取命中发生的位置。
其次,我没有使用
SphereCollider
。如果是的话,那么我所要做的就是获取返回的 direction
向量并将其乘以球体的半径(以及其他一些东西)。但遗憾的是,我正在使用 CapsuleCollider
(也许有一种方法可以做同样的事情,但对于 CapsuleCollider
,但我还没有找到)。
我尝试过的:
我最初的想法是采用玩家击中的碰撞器
hitCollider
,并执行以下代码片段:
Vector3 point = hitCollider.ClosestPoint(player.transform.position);
获取碰撞对撞机上最近的点。从那里我可以打电话
Vector3 finalPoint = player.collider.ClosestPoint(point);
在玩家上获得其最接近碰撞对撞机的最近点。这很令人困惑,但效果很好。然而,只有一个缺点:我计划使用 ProBuilder 进行大量关卡设计,并且您无法在未将
ClosestPoint
设置为 true(大多数网格体)的 MeshCollider
上调用 convex
在 ProBuilder 中。
我想到了其他一些事情,比如也许我可以反转方向,然后做一些(不那么)黑客数学来获得接触点等,但似乎没有什么可以作为成熟的解决方案。
结束语:
我开始认为这在技术上是不可能的。如果你仔细想想,这是真的,因为当两个对象相交时,没有单个点来定义它,而是一个完整的体积来定义它。但获得该交易量的平均位置将(也许?)是解决方案。
有什么想法吗?
提前谢谢您!
对于其他好奇的人,我找到了解决方案。当
Physics.ComputePenetration(...)
和 ourCollider
之间的 selectedCollider
检测到碰撞时,请确保首先进行反渗透,然后执行以下操作:
调用
Physics.CapsuleCast(...)
,但确保 point1
、point2
和 radius
的值略小于原始胶囊的大小(可能是 0.001)。这是因为 CapsuleCast
会认为它与 selectedCollider
相交,因此会直接穿过它,这是您不希望的。然后只需将 direction
设为从 Physics.ComputePenetration(...)
输出的负方向即可。
所以你的代码最终看起来像这样:
var origin = this.transform.position + this.center;
var offset = new Vector3(0.0f, this.height / 2.0f - this.radius - someTinyValue, 0.0f);
if (Physics.CapsuleCast(
origin + offset,
origin - offset,
this.radius - someTinyValue,
-direction,
out var hit))
{
// Do whatever with the `hit` variable
}
这个方法效果很好,但可能有点慢。 (至少我有时会看到瞬间的交叉点)
public bool IsGrounded { get; set; } = true;
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag(_groundTagName))
{
IsGrounded = true;
_landingSpeed = _fireSpeed;
if(_IsPhysicsComputePenetration == true)
{ //Here is the exact point to calculate intersection level
//Player collider and transformation infos, other collider and transformation infos required to find exact intersection vector and scale
Physics.ComputePenetration(_collider, transform.position, transform.rotation,
other, other.transform.position, other.transform.rotation,
out _penetrationDirection, out _penetrationDistance);
Debug.Log("Grounded!");
AlignGameObjectToSurfaceAfterCollision();
}
}
}
private void AlignGameObjectToSurfaceAfterCollision()
{
transform.position += _penetrationDirection * _penetrationDistance;
}