松散的末端

用户数据

这个b2Fixture ,b2Body公司,和b2Joint类允许您将用户数据附加为uintptr\。当您检查Box2D数据结构并想确定它们如何与游戏引擎中的对象相关时,这很方便。

例如,典型的做法是将actor指针附加到该actor上的刚体。这将设置循环引用。如果你有演员,你就能得到尸体。如果你有尸体,你就能找到演员。

GameActor* actor = GameCreateActor();
b2BodyDef bodyDef;
bodyDef.userData.pointer = reinterpret_cast<uintptr_t>(actor);
actor->body = myWorld->CreateBody(&bodyDef);

也可以使用它来保存整数值而不是指针。

以下是一些需要用户数据的示例:

  • 使用碰撞结果对参与者施加伤害。
  • 如果玩家在轴对齐的方框内,则播放脚本事件。
  • 当Box2D通知您一个关节将被破坏时,访问游戏结构。

请记住,用户数据是可选的,您可以在其中放入任何内容。但是,你应该始终如一。例如,如果要在一个实体上存储一个actor指针,那么应该在所有实体上保留一个actor指针。不要将actor指针存储在一个主体上,而将foo指针存储在另一个主体上。将actor指针转换为foo指针可能会导致崩溃。

默认情况下,用户数据指针为0。

对于fixture,您可以考虑定义一个用户数据结构来存储特定于游戏的信息,例如材质类型、效果挂钩、声音挂钩等。

struct FixtureUserData
{
    int materialIndex;
    // ...
};

FixtureUserData myData = new FixtureUserData;
myData->materialIndex = 2;

b2FixtureDef fixtureDef;
fixtureDef.shape = &someShape;
fixtureDef.userData.pointer = reinterpret_cast<uintptr_t>(myData);

b2Fixture* fixture = body->CreateFixture(&fixtureDef);
// ...

delete fixture->GetUserData();
body->DestroyFixture(fixture);

自定义用户数据

您可以定义嵌入Box2D数据结构中的自定义数据结构。这是通过定义B2_USER_SETTINGS并提供文件b2用户设置.h. 看到了吗b2_settings.h了解详情

隐性破坏

Box2D不使用引用计数。所以如果你毁了一具尸体,它就真的消失了。访问指向已销毁实体的指针具有未定义的行为。换句话说,你的程序可能会崩溃并烧毁。为了帮助解决这些问题,调试构建内存管理器使用FDFDFDFD填充已销毁的实体。在某些情况下,这有助于更容易地发现问题。

如果销毁Box2D实体,则需要确保删除对已销毁对象的所有引用。如果只有对实体的单个引用,这很容易。如果有多个引用,可以考虑实现一个handle类来包装原始指针。

通常在使用Box2D时,您将创建和销毁许多实体、形状和关节。Box2D在某种程度上自动化了这些实体的管理。如果销毁实体,则所有关联的形状和关节都将自动销毁。这叫做隐性破坏。

销毁实体时,其所有附着的形状、关节和触点都将被销毁。这叫做隐性破坏。任何与这些关节和/或触点相连的物体都会被唤醒。这个过程通常很方便。但是,您必须意识到一个关键问题:

注意安全:当一个实体被破坏时,附着在该实体上的所有固定装置和接头都将自动销毁。必须使指向这些形状和关节的任何指针无效。否则,如果以后尝试访问或破坏这些形状或关节,则程序将死掉。

为了帮助您使关节指针无效,Box2D提供了一个名为 b2你可以实现并提供给你的世界对象。然后,世界对象将通知您关节将被隐式破坏

请注意,当接头或固定装置被明确销毁时,没有通知。在这种情况下,所有权是明确的,您可以当场执行必要的清理。如果愿意,可以调用自己的实现 b2使清除代码保持集中

在许多情况下,隐性破坏是一种极大的便利。它也会使你的程序崩溃。您可以将指向形状和关节的指针存储在代码中的某个位置。当相关联的实体被销毁时,这些指针将成为孤立的。如果考虑到关节通常是由与相关实体的管理无关的代码部分创建的,则情况会变得更糟。例如,测试台创建一个 b2MouseJoint公司用于交互式操纵屏幕上的实体。

Box2D提供了一个回调机制,在隐式销毁发生时通知应用程序。这使应用程序有机会使孤立指针无效。本手册后面将介绍这种回调机制。

您可以实现b2DestructionListener这就允许b2World公司当某个形状或关节因关联实体被破坏而被隐式破坏时通知您。这将有助于防止代码访问孤立指针。

class MyDestructionListener : public b2DestructionListener
{
    void SayGoodbye(b2Joint* joint)
    {
        // remove all references to joint.
    }
};

然后,可以将销毁侦听器的实例注册到world对象。您应该在全局初始化期间执行此操作。

myWorld->SetListener(myDestructionListener);

像素和坐标系

回想一下,Box2D使用MKS(米、千克和秒)单位和弧度表示角度。你可能有困难使用米,因为你的游戏是以像素表示的。为了在试验台上处理这个问题,我有全部游戏以米为单位工作,只需使用OpenGL视口转换将世界缩放到屏幕空间。

float lowerX = -25.0f, upperX = 25.0f, lowerY = -5.0f, upperY = 25.0f;
gluOrtho2D(lowerX, upperX, lowerY, upperY);

如果你的游戏必须以像素为单位,那么当你从Box2D传递值时,你应该把你的长度单位从像素转换成米。同样,您应该将从Box2D接收到的值从米转换为像素。这将提高物理模拟的稳定性。

你必须想出一个合理的换算系数。我建议你根据角色的大小做出选择。假设您决定每米使用50像素(因为您的角色高75像素)。然后可以使用以下公式将像素转换为米:

xMeters = 0.02f * xPixels;
yMeters = 0.02f * yPixels;

反过来:

xPixels = 50.0f * xMeters;
yPixels = 50.0f * yMeters;

你应该考虑在游戏代码中使用MKS单位,在渲染时只需转换为像素。这将简化游戏逻辑,并减少出错的机会,因为渲染转换可以隔离为少量代码。

如果使用转换因子,则应尝试全局调整它,以确保没有任何中断。你也可以尝试调整它来提高稳定性。

调试图纸

您可以实现b2DebugDraw类来获得物理世界的详细绘图。以下是可用实体:

  • 形状轮廓
  • 联合连通性
  • 宽相位轴对齐边界框(AABBs)
  • 质心

debug_draw

这是绘制这些物理实体的首选方法,而不是直接访问数据。原因是,许多必要的数据都是内部的,可能会发生变化。

试验台使用调试绘图工具和接触监听器绘制物理实体,因此它是如何实现调试绘图以及如何绘制接触点的主要示例。

局限性

Box2D使用几种近似方法有效地模拟刚体物理。这带来了一些限制。

以下是当前的限制:

  1. 把沉重的物体堆在轻得多的物体上是不稳定的。当质量比超过10:1时,稳定性降低。
  2. 如果一个较轻的物体支撑着一个较重的物体,由关节连接的身体链可能会伸展。例如,一个破坏球连接到一系列轻量物体上可能不稳定。当质量比超过10:1时,稳定性降低。
  3. 形状与形状的碰撞通常有0.5cm左右的斜坡。
  4. 连续碰撞不会处理关节。所以你可以看到关节在快速移动的物体上伸展。
  5. Box2D使用辛欧拉积分方案。它不再现弹丸的抛物线运动,只有一阶精度。但它速度快,稳定性好。
  6. Box2D使用迭代解算器来提供实时性能。你不会得到精确的刚性碰撞或像素完美的精确度。增加迭代次数将提高精度。

results matching ""

    No results matching ""