如何解决RayCasting的Box2DXBox2D物理引擎的C#端口问题
我基本上是使用名为Box2DX的C#端口(最初托管在这里:https://code.google.com/archive/p/box2dx/,并导出到此GitHub存储库:https://github.com/colgreen/box2dx)
现在,问题是,仅当所有动态物体都位于模拟物理世界中的特定位置时,光线投射才能按预期工作。很难解释,因此我在引擎随附的Testbed项目中创建了一个测试类,以便精确地重新创建问题。
我将世界设置为包含3个代表地面的静态物体。我还添加了一个大框来代表“玩家”角色和一个小框。它们都是密度为1的动态物体。
在此图像中,您可以看到光线投射未拾取任何固定装置的设置(由红线表示的光线从空中开始,在大型地面固定装置内结束)。它应该检测盒子或至少是静态接地夹具。
现在,奇怪的是,当“玩家”的身体定位在射线源的“上方”的那一刻,射线广播确实起作用了,如下所示:
每当“盒子”物体移动到光线上方时,都会发生同样的事情。
请注意,我也从“玩家”身体的底部垂直向下投射了三道蓝光。 这些射线的行为也很奇怪。在上面的第一个图像中,您可以看到所有三条光线都能正确检测到地面固定装置。但是,一旦玩家的AABB位置如下图所示,光线将不再起作用:
基本上,当玩家的AABB移到站立的任何身体的左边缘上方时,蓝光便停止工作。
我已经花了无数个小时来摆弄这个东西,但我不知道是什么原因导致了这种奇怪的行为。我认为这只是世界上在实际C#端口中查询的一个错误,与我的特定设置无关。
作为参考,这是我来自Testbed项目的自定义测试类的代码:
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,Position = new Box2DX.Common.Vec2(12,2)
//Position = new Box2DX.Common.Vec2(12,-30)
};
polygonDef polygonDefIslandB = new polygonDef
{
Density = 0,Restitution = 0
};
polygonDefIslandB.SetAsBox(9,4);
Body bodyIslandB = _world.CreateBody(islandBodyDefB);
Shape shapeIslandB = bodyIslandB.CreateShape(polygonDefIslandB);
bodyIslandB.SetMassFromShapes();
BodyDef islandBodyDefC = new BodyDef
{
AllowSleep = false,Position = new Box2DX.Common.Vec2(32,15)
//Position = new Box2DX.Common.Vec2(32,-17)
};
polygonDef polygonDefIslandC = new polygonDef
{
Density = 0,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,Position = new Box2DX.Common.Vec2(18,25)
//Position = new Box2DX.Common.Vec2(18,-1)
};
polygonDef BoxDef = new polygonDef
{
Density = 1,Friction = 0.2f,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,Position = new Box2DX.Common.Vec2(5,21)
//Position = new Box2DX.Common.Vec2(5,-4)
};
polygonDef playerShapeDef = new polygonDef
{
Density = 1,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(),_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));
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 },null);
if (shape != null)
{
Vec2 dir = end - origin;
end = origin + dir * lambda;
}
_debugDraw.DrawSegment(origin,end,new Color(0,1,1));
}
}
}
有人遇到过这个问题吗?我不知道该如何解决。我需要光线投射才能为我的游戏项目可靠地工作。任何帮助将不胜感激。
谢谢! :)
解决方法
在这里只是一个疯狂的猜测,因为我从未使用过box2d的C#变体。我发现功能RaycastOne可疑...在原始的box2d中不存在。如果您阅读了Raycast here的文档,它说b2RayCastCallback
对于路径中找到的每个灯具都会被调用多次。 RaycastOne尝试将其简化为仅返回第一个灯具。
我的想法是,通过移动这些盒子,您可以以某种方式更改灯具的内部组织方式,因此RaycastOne报告的第一个灯具就变成了另一个灯具。
我将尝试替换RaycastOne并改为实现b2RayCastCallback
,然后您可以更好地控制要响应的灯具。幸运的话,可以解决这个问题。
更新
我最终将项目迁移到Aether.Physics2D: https://github.com/tainicom/Aether.Physics2D
它是Box2D的一个更干净,更优化的端口,并且该错误不会在此处发生。因此,如果有人发现自己为此感到疯狂,那么这似乎确实是移植库中的错误。如果您想省心一些,请切换到其他实现。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。