BOX2D 第十章 世界类

2023-11-10

第十章 世界类

Dec 2nd, 2012| Comments

声明:此文章翻译自Box2D v2.2.0用户手册,仅供学习参考。

  • 关于(About)

b2World类包含了物体和连接器。它管理了模拟相关的所有方面并且允许异步需求(像AABB需求和光线投射(ray-casts))。Box2D中的大部分交互将会使用b2World对象。

  • 创建并销毁一个世界(Creatng and Destroying a World)

创建一个世界相当简单。你只要提供一个重力向量和一个是否允许物体有睡眠状态的布尔值(Boolean)。通常你会使用new和delete关键字来创建和销毁一个世界。

1
2
3
b2World* myWorld = new b2World(gravity, doSleep);
...do stuff...
delete myWorld;
  • 使用世界(Using a World)

世界类包含了创建和销毁物体和连接器的方法工厂。稍后会对这些工厂进行介绍。现在给大家介绍一下与b2World其它方面的交互。

  • 模拟(Simulation)

世界类用来驱动模拟。你指定了一个时间步长,速度以及位置迭代次数。比如说:

1
2
3
4
float32 timeStep = 1.0f/60.0f;
int32 velocityIterations = 10;
int32 positionIterations = 8;
myWorld->Step(timeStep, velocityIterations, positionIterations);

执行这次时间步长之后你可以测试一下物体和连接器相关的信息。更多的你可能将会看到物体的移动,这样你就可以不断的更新和重绘它们。你可以在游戏循环的任何位置来执行。比如说,如果你想在某一帧获取新创建物体的碰撞结果,那么你必须在执行此时间步长之前来创建物体。

就像在HelloWorld章节中所提到的,上面的时间步长你应该使用固定值。在低帧率的场景中通过使用一个大的时间步长可以改进性能。但是一般来说你应该使用一个不能大于1/30秒的时间步长。一个1/60秒的时间步长可以产生一个高质量的模拟。

迭代次数控制了约束求解器扫描世界中所有接触和连接器的次数。更多的迭代总是能够产生更高质量的模拟。但是不要为一个小的时间步长设置一个大的迭代数。60Hz搭配10次迭代要远好于30Hz搭配20次迭代。

下一步,你应该清空所有施加在物体上的力。可以使用b2World::ClearForces方法来完成。这可以让你在同一个力场完成多个分步。

1
myWorld->ClearForces();
  • 探索世界(Exploring the World)

世界包括物体,接触以及连接器。你可以获取世界中的物体,接触以及连接器链表并对它们进行遍历。举例来说,下面这段代码唤醒了世界中的所有物体:

1
2
3
4
for(b2Body* b = myWorld->GetBodyList(); b; b = b->GetNext())
{
    b->SetAwake(true);
}

不幸的是真实的程序可能会更复杂。比如说,下面的代码就中断了。

1
2
3
4
5
6
7
8
for(b2Body* b = myWorld->GetBodyList(); b; b = b->GetNext())
{
    GameActor*  myActor = (GameActor*)b->GetUserData();
    if(myActor->IsDead())
    {
        myWorld->DestroyBody(b); //ERROR: now GetNext returns garbage.
    }
}

在碰到一个已经销毁的物体之前一切运行良好。一旦遇到一个被销毁的物体,下一个指针就会是一个错误的指针。所以调用b2Body::GetNext()方法就会返回一个错误指针。解决办法是在获取下一个指针之前先对其进行拷贝操作。

1
2
3
4
5
6
7
8
9
10
11
b2Body* node = myWorld->GetBodyList();
while(node)
{
    b2Body* b = node;
    node = node->GetNext();
    GameActor* myActor = (GameActor*)b->GetUserData();
    if(myActor->IsDead())
    {
        myWorld->DestroyBody(b);
    }
}

这样就可以安全销毁当前的物体。即便如此你也可能会调用一个游戏方法产生销毁多个物体。这种情况你需要非常小心。这是对特定情况下的解决方案,但是为了方便起见,我会向你展示一个解决此问题的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
b2Body* node = myWorld->GetBodyList();
while(node)
{
    b2Body* b = node;
    node = node->GetNext();
    GameActor* myActor = (GameActor*)b->GetUserData();
    if(myActor->IsDead())
    {
        bool otherBodiesDstroyed = GameCrazyBodyDestroyer(b);
        if(otherBodiesDestroyed)
        {
            node = myWorld->GetBodyList();
        }
    }
}

这样做很明显,GameCrazyBodyDestroyer必须对于哪些物体销毁要非常的诚实。

  • AABB查询(AABB Queries)

有时候你需要在一个区域中决定所有的形状。b2World类提供了一个log(N)的快速算法来使用broad-phase数据结构。在世界坐标系内提供了一个AABB(axis-aligned bounding box),之后实现了b2QueryCallback回调。世界将会调用你的类,通过定制器遍历与查询AABB以确定是否有重叠的AABB。返回true继续查询,否则返回false。例如,接下来的代码找出与一个特定AABB有潜在交互的所有定制器并唤醒所有关联的物体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyQueryCallback : public b2QueryCallback
{
    public:
        bool ReportFixture(b2Fixture* fixture)
        {
            b2Body*  body = fixture->GetBody();
            body->WakeUp();
            // Return true to continue the query.
            return true;
        }
};
...
MyQueryCallback callback;
b2AABB aabb;
aabb.lowerBound.Set(-1.0f, -1.0f);
aabb.upperBound.Set(1.0f, 1.0f);
myWorld->Query(&callback, aabb);

关于回调你不能假定回调的顺序。

  • 光线投射(Ray Casts)

你可以使用光线投射来做瞄准线检查(line-of-sight checks),火枪,等等。你可以通过实现一个回调类来实现光线投射的始点和终点。世界类会调用你的类访问射线中碰到的每一个定制器。回调中提供了定制器,交点,单位法向量(unit normal vector),以及沿着射线通过的的分数距离(fractional distance)。关于回调的次序你不能做任何假设。

你可以通过返回的分数来控制光线投射的延伸。返回分数(fraction)为零代表光线投射应该终止。返回分数(fraction)为一,光线好像没有发生碰撞一样一直延伸。如果从参数列表中返回分数,光线将被截断在当前位置和交点之间。所以你可以投射任何形状,甚至是所有形状,或者根据返回的适当分数投射最近的形状。

你可以返回-1分数值来以此过滤定制器。那么光线投射将会继续延伸就像定制器不存在一样。

这里是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//This class captures the closest hit shape
class MyRayCastCallback : public b2RayCastback
{
    public:
        MyRayCastCallback()
        {
            m_fixture = NULL;
        }
        float32 ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float32 fraction)
        {
            m_fixture = fixture;
            m_point = point;
            m_normal = normal;
            m_fraction = fraction;
            return fraction;
        }
        b2Fixture* m_fixture;
        b2Vec2 m_point;
        b2Vec2 m_normal;
        float32  m_fraction;
};
MyRayCastCallback callback;
b2Vec2 point1(-1.0f, 0.0f);
b2Vec2 point2(3.0f, 1.0f);
myWorld->RayCast(&callback, point1, point2);

警告
由于误差的原因,在静态环境里,光线投射可能会潜在穿过形状之间的小的裂缝。如果这一点在你应用中不能接受,那么就请略微放大你的形状。

1
2
3
4
void SetLinearVelocity(const b2Vec2& v);
b2Vec2 GetLinearVelocity() const;
void SetAngularVelocity(float32 omega);
float32 GetAngularVelocity() const;
  • 力和冲量(Forces and Impulses)

你可以对一个物体作用力,扭矩和冲量。当你作用一个力或者一个冲量的时候,就在世界坐标下的提供了一个负载点。通常的结果会有一个关于质心的扭矩。

1
2
3
4
void ApplyForce(const b2Vec2& force, const b2Vec2& point);
void ApplyTorque(float32 torque);
void ApplyLinearImpulse(const b2Vec2& impulse, const b2Vec2& point);
void ApplyAngularImpulse(float32 impulse);

应用力,扭矩或者冲量唤醒物体。有时候这是不可取的。举例来说,你可能会作用一个恒定的力同时允许物体进入睡眠状态以此来提高性能。在这个场景中你可以使用下面代码。

1
2
3
4
if(myBody->IsAwake() == true)
{
    myBody->ApplyForce(myForce, myPoint);
}
  • 坐标变换(Coordinate Transformations)

物体类(body class)有一些辅助方法来帮助你对坐标和向量在局部和世界坐标系之间变换。如果你不清楚这些概念,请参阅“Essential Mathematics for Games and Interactive Applications“由Jim Van Verth和Lars Bishop合著。这些方法很高效(当内联使用时(when inlined))。

1
2
3
4
b2Vec2 GetWorldPoint(const b2Vec2& localPoint);
b2Vec2 GetWorldVector(const b2Vec2& localVector);
b2Vec2 GetLocalPoint(const b2Vec2& worldPoint);
b2Vec2 GetLocalVector(const b2Vec2& worldVector);
  • 链表(Lists)

你可以遍历一个物体的定制器。如果你想访问定制器的用户数据(user data)这个很有用。

1
2
3
4
5
for(b2Fixture* f = body->GetFixtureList(); f; f = f ->GetNext())
{
    MyFixtureData*  data = (MyFixtureData*)f->GetUserData();
    ...do something with data...
}

类似的你可以遍历物体的连接器链表。

物体也提供了一个接触相关的链表。你可以获取当前接触的链表。不过要小心,因为接触链表也许并不包括所有接触,因为可能并不包含前一个时间步长存在的接触。


转自:http://ohcoder.com/blog/2012/12/02/world/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

BOX2D 第十章 世界类 的相关文章

随机推荐

  • vscode中误删代码文件恢复

    误删了项目某个文件夹 但是Ctrl z又没回来 就很无语 一般在vscode的这个页面会有你删掉的文件 查看你删掉的文件夹路径 然后git checkout就可以了 git checkout a66e87460a2ea04753dd7f39
  • 微服务六种设计模式

    1 聚合设计模式 聚合设计模式常用于报表服务 在微服务系统中报表服务是肯定存在的 2 代理设计模式 在微服务架构中 代理服务 是必然存在的 常用的代理服务是 网关服务 微服务的各个服务是没有状态的 需要通过统一的入口 代理服务 经过权限的校
  • 计算机数制详解及相互转换(二进制、八进制、十进制、十六进制)

    计算机数制详解及相互转换 二进制 八进制 十进制 十六进制 前言 一 计算机中常用的数制 1 二进制 2 八进制 3 十进制 4 十六进制 二 数制转换 1 十进制转二进制 2 二进制转十进制 3 二进制转八进制 4 二进制转十六进制 5
  • C++关于引用的分析

    目录 1 引用的概念 2 引用的特点 1 引用在定义时 必须初始化 2 一个变量可以有多个引用 3 引用一旦引用了一个实体 再不能引用其他实体 3 引用的应用 3 1 做参数 3 2做返回值 3 2 1 函数栈帧及临时变量 3 2 2 引用
  • mysql的事务是什么 mybatis框架中的事务配置 mybatis中的自动提交事务和手动提交事务 深入理解mybatis事务源码 通过对象的地址来理解mysbaits中的会话 对象的首地址

    目录 什么是事务 百度百科的解释 维基百科的解释 逻辑单元 事务的四大特征 操作事务的演示 在当前事务添加数据后查询 事务回滚 rollback 提交事务 commit mybatis中的事务 配置数据库的db properties 配置c
  • xargs -i参数详解

    学习所需 文章转载过来 xargs与find经常结合来进行文件操作 平时删日志的时候只是习惯的去删除 比如 find type f name log xargs rm rf 就将以log结尾的文件删除了 如果我想去移动或者复制就需要使用参数
  • vue3.0全局和按需引入element-plus

    1 先来看一下未添加element plus的干净项目打包后文件大小 2 全局安装element plus npm install element plus save 在main js里写 import createApp from vue
  • nginx 重启/停止/退出失败

    启动Nginx时候报错 nginx error open usr local nginx logs nginx pid failed 2 No such file or directory 这句话的意思就是 你的nginx pid 打开失败
  • linux tcp优雅关闭,优雅地关闭Socket;检测非正常断开的TCP连接

    转载声明 本文所有版权归原作者所有 如本文原作者不认可转载 请联系本空间删除 相关资源 NA 如何优雅地
  • 集群搭建--安装apache-hive-2.3.4

    本文安装的是 apache hive 2 3 4 bin tar gz 其下载地址为 路径 https pan baidu com s 1ZPJxbGdpjW0fPpKUa7RX6Q 提取码 i58e 解压 apache hive 2 3
  • Hash函数加密算法(一)

    一 使用密码学的目的 保密性 防止用户的标识或数据被读取 l 身份验证 确保数据发自特定的一方 数据完整性 防止数据被更改 二 加密算法的分类 1 对称加密 采用对称秘钥的加密系统加密 解密过程均采用同一把秘钥 通信双方必须同时获得这把钥匙
  • windows操作系统上启用SSLv3协议引发的威胁

    一 主机启用SSLv3协议引发的威胁 远程主机受到称为POODLE的中间人 MitM 信息泄露漏洞的影响 该漏洞是由于SSL 3 0在解密使用密码块链接 CBC 模式下的块密码加密的消息时处理填充字节的方式 二 建议处置措施 处置措施 禁用
  • Linux如何修改ip地址

    参考 3 Ways to change ip address in Linux howtouselinux Ubuntu篇 一 Desktop版方法 如果你使用了Desktop版本或者server版上面加了desktop桌面 那么你就尽量使
  • DELPHI支付宝支付代码

    真实业务场景的考虑 按照支付宝或者微信支付的开发手册的说法 一个标准的客户端接入支付业务模型应该是这样的 我忽略时序图 只用文字描述 用户登录客户端 选择商品 然后点击客户端支付 客户端收集商品信息 然后调用自己业务平台的预付款接口 业务平
  • 基于Jenkins+git+gradle的android持续集成

    本文参考了 http my oschina net uboluo blog 157483 http java dzone com articles automating continuous 以前都是通过IDE eclipse or And
  • PageHelper分页查询数据错乱,有些数据不显示,和数据库查询对不上

    今天遇到一个很意外的错误 使用PageHelper分页查询时 分页的数据总是和数据库查询的对不上 分页代码如下 public ReturnInfo getManage RequestPage
  • 机器学习——几种分类算法的汇总

    https www cnblogs com Zhi Z p 8912396 html
  • Python 字符串的常用方法

    视频版教程 Python3零基础7天入门实战视频教程 下标索引操作和前面的列表 元组用法一样 虽然字符串不能被修改 但是返回一个操作过的新字符串 所以方法还不少 我先学习下常用方法 1 index 元素 方法 查找字符串中指定元素的下标 不
  • Java语言 设计一个动物声音“模拟器”(用接口实现)

    设计一个动物声音 模拟器 希望模拟器可以模拟许多动物的叫声 要求如下 编写接口Animal Animal接口有两个抽象方法cry 和getAnimalName 即要求实现该接口的各种具体的动物类给出自己的叫声和种类名称 编写模拟器类Simu
  • BOX2D 第十章 世界类

    第十章 世界类 Dec 2nd 2012 Comments 声明 此文章翻译自Box2D v2 2 0用户手册 仅供学习参考 关于 About b2World类包含了物体和连接器 它管理了模拟相关的所有方面并且允许异步需求 像AABB需求和