【发布时间】:2020-04-12 16:05:45
【问题描述】:
我有一个根据键箭头移动的对象,例如角色控制器。每次移动的Update() 都会抛出一次扫描测试,但如果你一直按下按钮,它就会通过。顺便说一句,我没有使用 Unity 物理,所以任何暗示使用 OnCollision() voids 的解决方案都不起作用
void Update()
{
float horizontal = Input.GetAxisRaw("Horizontal") * moveSpeed;
velocity.x = horizontal;//y axis adds gravity, but is omitted here to focus on the relevant
Vector3 movement = velocity * Time.deltaTime;
if (movement != Vector3.zero)
{
RaycastHit hit;
if (rb.SweepTest(movement, out hit, movement.magnitude))
{
if (hit.collider)
{
Vector3 moveToColl = movement / movement.magnitude * hit.distance;//gives hit.distance vector
transform.position += moveToColl;
movement = Vector3.zero;//because movement has already been done.
velocity = movement;
}
}
}
this.transform.Translate(movement, Space.World);
}
因为我认为问题是游戏对象与对撞机重叠,所以我尝试在进行扫描测试之前将游戏对象移动到后面(因为扫描测试,出于某种原因,不允许使用图层蒙版),然后增加它的长度以保持同样的碰撞。
Vector3 behindVector = movement / 100;
transform.position -= behindVector;
float maxDistance = movement.magnitude;
maxDistance += maxDistance / 100;// same as movement.magnitude + behindVector.magnitude
// 相同,但是 maxDistance 替换了扫描测试长度中的 motion.magnitude。 虽然这显着减少了问题,但仍然存在物体会穿过墙壁的框架,特别是如果距离很近或在改变方向和跳跃之后(对不起:我不太确定。我试图弄清楚什么是它,但我并没有真正明白。如果您靠近墙壁并尝试通过,然后向相反方向移动并重试,似乎更常见,特别是如果velocity.y也被修改,这使得我认为这与向量的大小有关)。 我尝试的另一个解决方案是:
if (hit.collider)
{
Vector3 moveToColl = movement / movement.magnitude * hit.distance;//gives hit.distance vector
moveToColl *= 0.9f;
float minimum = 0.001f;
if (moveToColl.magnitude > minimum)
{
transform.position += moveToColl;
}
movement = Vector3.zero;//these two are kept outside
velocity = movement;
}
因此,将对象移动得比预期的要小可以防止重叠,但是如果 0.9f 更大,例如 0.999f,则碰撞基本上再次为零。如果我们改用 0.6f,它几乎可以完美运行,但这有两个问题。首先,我什至不完全确定我是否摆脱了这个问题,看起来就是这样。其次,这种移动是错误的,如果浮动更小(这同样可以改善碰撞),那么两个对象之间的差距会更加明显,即使不是那么大。
我什至同时尝试了这两种方法,但它给了我一个奇怪的物体振动,因为这两行修改了它的位置:transform.position -= behindVector;// 和 transform.position += moveToColl;
编辑:由于问题是由于浮动缺乏精度和碰撞器重叠引起的,我想到了另一种解决方案,但我需要最后一部分的帮助。 它是关于从 moveToColl 向量中减去 Vector3 偏移量。首先,我们假设 X 轴为 0.1f(方向需要与 moveToColl.x 方向相反,所以如果为负,则为正)并使用此公式得到 Y 值:0.1f * moveToColl.y / moveToColl.x。所以我们以 Vector3 结束(0.1f,Y 值,weIgnoreZAxisInThisExample)。我们从 moveToColl 向量中减去它,我们得到一个保持相同方向的新向量,但不会在 X 和 Y 中移动那些额外的 0.1f 以保持该方向。如果我们想要另一个轴,公式是相同的,但我们反转所有轴。第一个例子适用于墙壁。因为无论 y 速度如何,它都减去 0.1f(假设该值足够)。扫描测试在跳跃时更频繁地错过碰撞的原因是 y 的变化减少了 x 偏移(因为我们将向量乘以 0.9f,所以 0.1f 减去了方向而不是 x 轴。现在是持续的)。但这仅适用于完美的垂直或水平线。如果是斜率,则偏移量错误。我发现偏移量需要垂直于表面,因为最近的距离总是垂直的。靠墙效果很好,因为 x 轴是垂直的,所以它就像直接减去偏移量并保持方向一样简单。但现在不是轴对齐的。 enter image description here
对不起,偏移量是-0.1,0.1。我忘记放置 - 并且它走向相反的方向。例如,该公式将给出紫色向量来获得偏移量(我知道我真的绘制了 x 和 y 偏移量,因为我真的想要那些)。正确的向量将从命中开始。指向和结束绿线和黑线相交的地方(这就是我要寻找的)。绿色偏移量内部还有一个橙色点,这是错误的,因为它与蓝线之间的距离明显小于预期的距离;因此,如果公式给出了这一点,那就太糟糕了(尽管我不完全确定这是否可能,所以如果你知道,请告诉我)。无论如何,距离越大,越容易看出没有碰撞。这就是为什么我想要尽可能近的距离。顺便说一句,0.1f 只是一个例子。有谁知道您可以安全检测到碰撞的最近距离是多少?
【问题讨论】:
-
使用 Unity Physics 会更好......话虽如此......你的对撞机是什么样的?你用的是什么类型的对撞机?
-
此外,当测试开始于已经“碰撞”的对象时,SweepTest 往往会失败......结合“浮动”舍入问题和计算问题......这意味着有时你的sweeptest 将从一个已经在碰撞盒内的对象开始,即使是很小的一部分....所以这会使 SeepTest 失败....所以你不能仅仅依赖 SweepTest
标签: c# unity3d collision-detection