刚刚开始使用 Unity,所以请耐心等待 :) 我们正在构建一款类似塞尔达 (Zelda) 的游戏 - 自上而下,没有物理/重力,地图上有瓦片地图。我们不想使用动态刚体,因为它背后的物理原理。
图块地图有“墙”,带有 CompositeCollider2D 和静态刚体,我们的玩家有 CircleCollider2D 和运动学刚体。玩家和墙壁都有零摩擦的物理材质。
运动刚体碰撞检测的最佳方法是什么?我们有几个概念,例如Physics2D.CircleCastAll,但最终我们的玩家卡在墙壁中或卡在角落(玩家应该在拐角处平滑移动)或卡在两个重叠的碰撞器中。
我们想出的最佳解决方案是这样的(玩家有时仍然会遇到碰撞器,但至少不会再被卡住)。
// MoveDirection is the movement input
movement = MoveDirection * Speed * Time.deltaTime;
List<RaycastHit2D> results = new List<RaycastHit2D>();
List<RaycastHit2D> resultsX = new List<RaycastHit2D>();
List<RaycastHit2D> resultsY = new List<RaycastHit2D>();
if (movement.x != 0)
{
actorCollider.Cast(new Vector2(movement.x, 0), filter, resultsX, movement.magnitude);
results.AddRange(resultsX);
}
if (movement.y != 0)
{
actorCollider.Cast(new Vector2(0, movement.y), filter, resultsY, movement.magnitude);
results.AddRange(resultsY);
}
foreach (RaycastHit2D hit in results)
{
Vector2 normal = hit.normal;
float projection = Vector2.Dot(movement, normal);
if (projection < 0)
{
if (hit.distance > 0)
{
projection += Mathf.Max(hit.distance - 0.01f, 0);
}
movement -= projection * normal;
}
}
// move player
newPosition = body.position + movement;
// make sure the player is positioned at pixel perfect positions
newPosition.x = (Mathf.Round(newPosition.x * 16) / 16);
newPosition.y = (Mathf.Round(newPosition.y * 16) / 16);
body.MovePosition(newPosition);
// this part moves the player back in case it is in a collider.
List<Collider2D> colliderList = new List<Collider2D>();
if (actorCollider.OverlapCollider(filter, colliderList) > 0)
{
foreach (Collider2D hit in colliderList)
{
var distance = hit.Distance(actorCollider);
body.MovePosition(body.position + (distance.normal * Mathf.Abs(distance.distance)));
}
}
播放器设置:
墙壁设置:
不知怎么的,还是感觉太复杂了。这是进行碰撞检测的最佳方法还是我们做得完全错误并且有更好/更简单的方法?请记住,我们希望能够以某种方式绕过直角,而不是被困在上面。并且有重叠的碰撞器或彼此相邻的碰撞器。
感谢您的宝贵时间!
如果我理解你想要正确做的事情,那么你就让这件事变得过于复杂了。如果场景中有两个带有碰撞器的对象,如果您让 Unity 为您进行移动,它们将不会“相互碰撞”。
我的意思是,您可以将 RigidBody2D 组件附加到您的播放器,并使用类似
Player.Transform.position = new Vector3(Player.Transform.position.x + 1, Player.Transform.position.y, Player.transform.position.z)
的东西在代码中引用它,而不是使用类似 myRigidBody = GetComponent<RigidBody2D>();
的东西来执行运动,然后在您的运动函数中,您只需使用一些东西像 myRigidBody.velocity = new Vector3(moveSpeed, myRigidBody.velocity.y, myRigidBody.velocity.z)
一样移动玩家。然后,只要玩家有碰撞器,您希望它不进入的对象也有碰撞器,并且所有碰撞器都没有勾选“isTrigger”,那么玩家就不会进入任何场景。
我确实注意到你的“像素校正”类型代码,虽然我自己从未使用过类似的东西,但我认为你可以在这些东西之上使用它,它会工作得很好。