如何在Farseer物理引擎中创建一个水平平台,使得只有当球在平台上方时,才会与该平台发生碰撞?

编程语言 2026-07-10

我已经尝试了很多不同的方法来创建一个平台,只有当球在平台上方时才会与球发生碰撞,但偶尔它不起作用,球在从上方落到平台时会穿过平台。

Body Platform, Ball;

Ball = BodyFactory.CreateCircle(world, 0.10f, 2.0f);
Ball.BodyType = BodyType.Dynamic;
Ball.Position = new Vector2(BallPosition.X, BallPosition.Y);
Ball.Rotation = 0f;
Ball.IsSensor = false;

Platform = BodyFactory.CreateEdge(world, new Vector2(StartPosition.X, StartPosition.Y), new Vector2(EndPosition.X, EndPosition.Y));             
Platform.BodyType = BodyType.Static;
Platform.IsSensor = false;
Platform.IgnoreCollisionWith(Ball);

我在球接近平台且位于平台下方时调用Platform.IgnoreCollisionWith(Ball),以便球可以穿过平台。

当球完全在平台上方时,我调用Platform.RestoreCollisionWith(Ball),以便球在触碰平台时不再能够穿过平台。

我该怎么做,才能在球从上方落到平台时不偶尔穿过平台?

解决方案

让物理引擎检测碰撞,然后再决定是否接受它们。你正在使用
IgnoreCollisionWith / RestoreCollisionWith。它在物理引擎之外运行,导致随机穿透和帧之间的时序不匹配。

为了解决你偶发的问题,启用连续碰撞检测 ball.IsBullet = true;

然后在物理步骤中,只要球触碰到平台就运行以下内容:

platform.OnCollision += ...

这是你的方向检查:如果球向上移动,允许穿透;如果球在下落,允许碰撞 bool movingUp = vy < 0; 记住,如果Y 轴向下增加,请使用 vy > 0

如果球仍然在下面,忽略碰撞,并在它从下方进入时防止发生卡死,请使用 bool belowPlatform = ballTop < platformY

为了确保在球完全位于平台上方时也忽略碰撞,从而防止边缘碰撞,你可以进行水平检查:

bool notOverPlatform =
    other.Position.X + radius < left ||
    other.Position.X - radius > right;

下面是把它们整合在一起的方法。

// Create ball
Body ball = BodyFactory.CreateCircle(world, 0.10f, 2.0f);
ball.BodyType = BodyType.Dynamic;
ball.Position = new Vector2(BallPosition.X, BallPosition.Y);
ball.IsBullet = true; // IMPORTANT: prevents tunneling

// Create platform (edge)
Body platform = BodyFactory.CreateEdge(
    world,
    new Vector2(StartPosition.X, StartPosition.Y),
    new Vector2(EndPosition.X, EndPosition.Y)
);
platform.BodyType = BodyType.Static;

// Cache platform bounds
float left = Math.Min(StartPosition.X, EndPosition.X);
float right = Math.Max(StartPosition.X, EndPosition.X);
float platformY = StartPosition.Y; // assumes horizontal platform

// Collision filtering (one-way platform)
platform.OnCollision += (fixtureA, fixtureB, contact) =>
{
    Body other = fixtureA.Body == platform ? fixtureB.Body : fixtureA.Body;

    // Only care about the ball
    if (other != ball)
        return true;

    float radius = 0.10f;

    float ballBottom = other.Position.Y - radius;
    float ballTop = other.Position.Y + radius;
    float vy = other.LinearVelocity.Y;

    // Adjust this depending on your coordinate system:
    // If Y increases downward, falling = vy > 0
    bool movingUp = vy < 0;

    bool belowPlatform = ballTop < platformY;
    bool notOverPlatform =
        other.Position.X + radius < left ||
        other.Position.X - radius > right;

    // Disable collision if:
    // - ball is moving upward (jumping through)
    // - ball is below the platform
    // - ball is not horizontally over platform
    if (movingUp || belowPlatform || notOverPlatform)
    {
        return false; // ignore collision
    }

    // Otherwise, allow collision (landing on platform)
    return true;
};
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章