首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Box2DX ( Box2D物理引擎的C#端口)与RayCasting的问题

Box2DX ( Box2D物理引擎的C#端口)与RayCasting的问题
EN

Stack Overflow用户
提问于 2020-09-12 19:25:19
回答 2查看 653关注 0票数 2

我在Box2D物理引擎的C#端口上遇到了一个非常奇怪的问题。

我基本上使用的是名为Box2DX的C#端口(最初托管在这里:https://code.google.com/archive/p/box2dx/,并导出到这个GitHub存储库:https://github.com/colgreen/box2dx)

现在,问题是,只有当所有动力学物体都处于模拟物理世界中的特定位置时,光线投射才能按预期工作。这很难解释,所以我在引擎附带的TestBed项目中创建了一个测试类,以便准确地重新创建问题。

我将世界设置为包含3个静态物体来表示地面。我还添加了一个代表“玩家”角色的大方框和一个小方框。这两个都是密度为1的动态体。

在这个图像中,你可以看到光线投射没有拾取任何设备的设置(光线由红线表示,从半空开始,结束于大型地面设备)。它应该检测到盒子,或者至少检测到静电接地装置。

RayCast Not Working

现在,奇怪的部分是,光线投射确实起作用了,当“播放器”身体定位在光线投射的原点“上方”时,如下所示:

RayCast Working

当“长方体”身体移动到光线上方时,同样的事情也会发生。

RayCast working again

请注意,我还从“玩家”身体的底部垂直向下投射了三条蓝色射线。这些射线的行为也很奇怪。在上面的第一幅图像中,您可以看到所有三条光线都正确地检测到了地面设备。但是,当播放器的AABB定位如下图所示时,光线投射将不再起作用:

Blue RayCasts not working

基本上,当玩家的AABB移动到它所站的任何身体的左侧边缘时,蓝色光线就会停止工作。

Blue Rays Not Working

我已经摆弄了无数个小时了,我不知道是什么导致了这种奇怪的行为。我认为这只是一个在实际C#端口中查询的bug,与我的特定设置无关。

作为参考,下面是来自TestBed项目的自定义测试类的代码:

代码语言:javascript
复制
public class CustomRayCastTest : Test
{
    public static CustomRayCastTest Create() => new CustomRayCastTest();

    private Body _player;

    public CustomRayCastTest()
    {
        _world.Gravity = new Box2DX.Common.Vec2(0, -6.25f);

        BodyDef islandBodyDefA = new BodyDef
        {
            AllowSleep = false,
            Angle = 0,
            FixedRotation = true,
            IsBullet = false,
            //UserData = new object(),
            IsSleeping = false,
            Position = new Box2DX.Common.Vec2(1, 15)
            //Position = new Box2DX.Common.Vec2(1, -17)
        };

        PolygonDef polygonDefIslandA = new PolygonDef
        {
            Density = 0,
            Friction = 0.4f,
            IsSensor = false,
            Restitution = 0
        };

        polygonDefIslandA.SetAsBox(6, 3);

        Body bodyIslandA = _world.CreateBody(islandBodyDefA);
        Shape shapeIslandA = bodyIslandA.CreateShape(polygonDefIslandA);

        bodyIslandA.SetMassFromShapes();

        BodyDef islandBodyDefB = new BodyDef
        {
            AllowSleep = false,
            Angle = 0,
            FixedRotation = true,
            IsBullet = false,
            //UserData = new object(),
            IsSleeping = false,
            Position = new Box2DX.Common.Vec2(12, 2)
            //Position = new Box2DX.Common.Vec2(12, -30)
        };

        PolygonDef polygonDefIslandB = new PolygonDef
        {
            Density = 0,
            Friction = 0.4f,
            IsSensor = false,
            Restitution = 0
        };

        polygonDefIslandB.SetAsBox(9, 4);

        Body bodyIslandB = _world.CreateBody(islandBodyDefB);
        Shape shapeIslandB = bodyIslandB.CreateShape(polygonDefIslandB);

        bodyIslandB.SetMassFromShapes();

        BodyDef islandBodyDefC = new BodyDef
        {
            AllowSleep = false,
            Angle = 0,
            FixedRotation = true,
            IsBullet = false,
            //UserData = new object(),
            IsSleeping = false,
            Position = new Box2DX.Common.Vec2(32, 15)
            //Position = new Box2DX.Common.Vec2(32, -17)
        };

        PolygonDef polygonDefIslandC = new PolygonDef
        {
            Density = 0,
            Friction = 0.4f,
            IsSensor = false,
            Restitution = 0
        };

        polygonDefIslandC.SetAsBox(6, 3);

        Body bodyIslandC = _world.CreateBody(islandBodyDefC);
        Shape shapeIslandC = bodyIslandC.CreateShape(polygonDefIslandC);

        bodyIslandC.SetMassFromShapes();

        // Box
        BodyDef boxBodyDef = new BodyDef
        {
            AllowSleep = false,
            Angle = 0,
            FixedRotation = true,
            IsBullet = false,
            //UserData = new object(),
            IsSleeping = false,
            Position = new Box2DX.Common.Vec2(18, 25)
            //Position = new Box2DX.Common.Vec2(18, -1)
        };

        PolygonDef boxDef = new PolygonDef
        {
            Density = 1,
            Friction = 0.2f,
            IsSensor = false,
            Restitution = 0
        };

        boxDef.SetAsBox(1, 1);

        Body boxBody = _world.CreateBody(boxBodyDef);
        Shape boxShape = boxBody.CreateShape(boxDef);

        boxBody.SetMassFromShapes();

        // Player
        BodyDef playerBodyDef = new BodyDef
        {
            AllowSleep = false,
            Angle = 0,
            FixedRotation = true,
            IsBullet = false,
            //UserData = new object(),
            IsSleeping = false,
            Position = new Box2DX.Common.Vec2(5, 21)
            //Position = new Box2DX.Common.Vec2(5, -4)
        };

        PolygonDef playerShapeDef = new PolygonDef
        {
            Density = 1,
            Friction = 0.2f,
            IsSensor = false,
            Restitution = 0
        };

        playerShapeDef.SetAsBox(1, 2);

        Body playerBody = _world.CreateBody(playerBodyDef);
        Shape playerShape = playerBody.CreateShape(playerShapeDef);

        playerBody.SetMassFromShapes();

        _player = playerBody;

    }

    public override void Keyboard(Keys key)
    {
        base.Keyboard(key);

        if(key == Keys.D)
        {
            _player.ApplyForce(new Vec2(7f * _player.GetMass(), 0), _player.GetWorldPoint(Vec2.Zero));
        }
        else if(key == Keys.A)
        {
            _player.ApplyForce(new Vec2(-7f * _player.GetMass(), 0), _player.GetWorldPoint(Vec2.Zero));
        }

        if(key == Keys.W)
        {
            _player.ApplyImpulse(new Vec2(0, _player.GetMass() * 20f), _player.GetWorldPoint(Vec2.Zero));
        }

    }

    public override void Step(Settings settings)
    {
        base.Step(settings);

        Vec2 rayStart = new Vec2(22, 40);
        Vec2 rayEnd = new Vec2(17, 1);

        Segment ray = new Segment
        {
            P1 = rayStart,
            P2 = rayEnd
        };

        var shape = _world.RaycastOne(ray, out float lambda, out Vec2 normal, false, null);

        if(shape != null)
        {
            Vec2 dir = rayEnd - rayStart;

            rayEnd = rayStart + dir * lambda;

        }

        _debugDraw.DrawSegment(rayStart, rayEnd, new Color(1, 0, 0));

        CastPlayerRays();

    }

    private void CastPlayerRays()
    {
        const int rayCount = 3;
        const float playerWidth = 2;
        const float playerHeight = 4;
        const float inset = 0.2f;
        const float rayLength = 2;

        for (int i = 0; i < rayCount; i++)
        {
            Vec2 origin = new Vec2(-(playerWidth / 2 - inset), -(playerHeight / 2 - inset));

            origin.X += i * (playerWidth / (rayCount));

            origin = _player.GetWorldPoint(origin);

            Vec2 end = origin + new Vec2(0, -1) * rayLength;

            var shape = _world.RaycastOne(new Segment { P1 = origin, P2 = end }, out float lambda, out Vec2 normal, false, null);

            if (shape != null)
            {

                Vec2 dir = end - origin;

                end = origin + dir * lambda;

            }

            _debugDraw.DrawSegment(origin, end, new Color(0, 1, 1));

        }


    }

}

有没有人遇到过这个问题?我不知道如何解决这个问题。我需要光线投射为我的游戏项目可靠地工作。任何帮助都将不胜感激。

谢谢!:)

EN

回答 2

Stack Overflow用户

发布于 2020-09-12 20:33:03

这里只是一个大胆的猜测,因为我从来没有使用过box2d的C#变体。我发现函数RaycastOne可疑...它在原始box2d中不存在。如果您阅读过Raycast here的文档,就会发现在路径中找到的每个fixture都会多次调用b2RayCastCallback。RaycastOne可能会尝试将其简化为只返回第一个fixture。

我的想法是,通过移动这些框,您以某种方式改变了fixture的内部组织方式,因此RaycastOne报告的第一个fixture只是得到了一个不同的fixture。

我会尝试替换RaycastOne,而不是实现b2RayCastCallback,这样您就可以更好地控制您想要对其进行反应的fixture。只要有一点运气,就可以解决这个问题。

票数 0
EN

Stack Overflow用户

发布于 2020-09-12 23:57:51

更新

我最终将我的项目迁移到了Aether.Physics2D:https://github.com/tainicom/Aether.Physics2D

它是一个更干净、更优化的Box2D端口,错误不会在这里发生。因此,如果其他任何人发现自己对此感到疯狂,这似乎真的是移植库中的一个bug。如果您想节省一些精力,请切换到不同的实现。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63859807

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档