Box2D C++ 教程-画自己的图像

声明:本教程翻译自:Box 2D C++ turorials - Drawing your own objects,仅供学习参考。

  • 画自己的图像

从之前的话题中可以看出,很显然使用debug draw不能做出视觉上具有吸引力的游戏。通常我们都会使用我们自己的方法在场景中画图像,然后访问Box2d获取物体的物理信息以此知道在哪里画图像。在本次话题中,我们将会专门定义一个类用作渲染游戏实体,然后我们来看看如何保证实体的物理状态。

现在所有实体类将会进行自身渲染,但是之后我们在其它话题中使用。那些话题中的焦点就不再是渲染图形,所以这里我们在一个圆弧上,画了一张带有微笑的笑脸,并且保证其能够正确移动和旋转。

那么在本话题中,让我们创建一个四方形空的区域作为开始。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
FooTest() {
    //a static body
    b2BodyDef myBodyDef;
    myBodyDef.type = b2_staticBody;
    myBodyDef.position.Set(0, 0);
    b2Body* staticBody = m_world->CreateBody(&myBodyDef);

    //shape definition
    b2PolygonShape polygonShape;

    //fixture definition
    b2FixtureDef myFixtureDef;
    myFixtureDef.shape = &polygonShape;

    //add four walls to the static body
    polygonShape.SetAsBox( 20, 1, b2Vec2(0, 0), 0);//ground
    staticBody->CreateFixture(&myFixtureDef);
    polygonShape.SetAsBox( 20, 1, b2Vec2(0, 40), 0);//ceiling
    staticBody->CreateFixture(&myFixtureDef);
    polygonShape.SetAsBox( 1, 20, b2Vec2(-20, 20), 0);//left wall
    staticBody->CreateFixture(&myFixtureDef);
    polygonShape.SetAsBox( 1, 20, b2Vec2(20, 20), 0);//right wall
    staticBody->CreateFixture(&myFixtureDef);
}

pic

让我们调用我们的游戏实体类Ball并然其滚动和弹跳:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//outside and before the FooTest class
class Ball {
public:
    //class member variables
    b2Body* m_body;
    float m_radius;

public:
    Ball(b2World* world, float radius) {
    m_body = NULL;
    m_radius = radius;
}
    ~Ball() {}
};
//FooTest class member variable
std::vector<Ball*> balls;

(为了完成最后的编译,你需要在文件顶部添加#include语句。)注意现在在实体内直接存储了Box2D物体的引用,我们会让每一个实体自身存储其对应其相应的物理实体的引用。为了渲染一个漂亮的笑脸,为Ball类添加一个渲染方法。这里有一个需要注意的地方,那就是让笑脸的中心作为(0,0)点,但是不让其旋转。默认的渲染半径设置为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
26
//Ball::render
void render() {
    glColor3f(1,1,1);//white

    //nose and eyes
    glPointSize(4);
    glBegin(GL_POINTS);
    glVertex2f( 0, 0 );
    glVertex2f(-0.5, 0.5 );
    glVertex2f( 0.5, 0.5 );
    glEnd();

    //mouth
    glBegin(GL_LINES);
    glVertex2f(-0.5,  -0.5 );
    glVertex2f(-0.16, -0.6 );
    glVertex2f( 0.16, -0.6 );
    glVertex2f( 0.5,  -0.5 );
    glEnd();

    //circle outline
    glBegin(GL_LINE_LOOP);
    for (float a = 0; a < 360 * DEGTORAD; a += 30 * DEGTORAD)
        glVertex2f( sinf(a), cosf(a) );
    glEnd();
}

这里只有两件事需要做。在FooTest构造函数中,在空的区域设置完成之后,添加一个Ball对象实体到场景中(如果你希望添加析构函数的话,在析构函数里应该对所添加的Ball对象进行删除)。

1
2
3
//add ball entity to scene in constructor
Ball* ball = new Ball(m_world, 1);
balls.push_back( ball );

最后,为了真正画出小球实体我们需要添加Step()方法。如果你把这个方法放到Test::Step()之后,那么小球会显示在debug draw所画的图像之上。

1
2
3
//in Step() function, after Test::Step()
for (int i = 0; i < balls.size(); i++)
    balls[i]->render();

pic

现在我们有了一个小球实体,但是它显示在默认的(0,0)点并且我们不会希望我们的物理引擎这么做的,是吧?让我们在Ball类的构造函数中对球的圆弧进行设置。现在可以明显的看出为什么Ball的构造函数需要传递b2World指针了吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Ball class constructor
Ball(b2World* world, float radius) {
    m_body = NULL;
    m_radius = radius;

    //set up dynamic body, store in class variable
    b2BodyDef myBodyDef;
    myBodyDef.type = b2_dynamicBody;
    myBodyDef.position.Set(0, 20);
    m_body = world->CreateBody(&myBodyDef);

    //add circle fixture
    b2CircleShape circleShape;
    circleShape.m_p.Set(0, 0);
    circleShape.m_radius = m_radius; //use class variable
    b2FixtureDef myFixtureDef;
    myFixtureDef.shape = &circleShape;
    myFixtureDef.density = 1;
    m_body->CreateFixture(&myFixtureDef);
}

pic

好的,现在场景中有了一个物理实体,但是我们的笑脸并没有被画到物理实体的位置。为了达到这个目的,让我们为Ball类在渲染之前添加另外一个方法来设置OpenGL的平移变换。取决于你所使用的API,可能有比这里列举的更好的方法来完成这个功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
//in Ball class
void renderAtBodyPosition() {
    //get current position from Box2D
    b2Vec2 pos = m_body->GetPosition();
    float angle = m_body->GetAngle();

    //call normal render at different position/rotation
    glPushMatrix();
    glTranslatef( pos.x, pos.y, 0 );
    glRotatef( angle * RADTODEG, 0, 0, 1 );//OpenGL uses degrees here
    render();//normal render at (0,0)
    glPopMatrix();
}

别忘了在更新渲染方法Step()中调用新创建的renderAtBodyPosition()。现在即便你关掉debug draw显示(在testbed右边控制面板中取消所勾选的‘Shapes’勾选框),你自定义的渲染代码仍然会渲染出一个笑脸小球,并显示在正确的位置以及进行正确的旋转。

pic

为了更加有趣,我们可以在小球以不同速度移动的时候改变其颜色。例如,像这样设置颜色:

1
2
3
4
5
//in Ball::render
b2Vec2 vel = m_body->GetLinearVelocity();
float red = vel.Length() / 20.0;
red = min( 1, red );
glColor3f(red,0.5,0.5);

那么往场景中添加一堆小球如何呢?只要在FooTest构造函数中多循环几次就行:

1
2
3
4
5
//in FooTest constructor
for (int i = 0; i < 20; i++) {
    Ball* ball = new Ball(m_world, 1);
    balls.push_back( ball );
}

pic

既然我们在Ball类中考虑了球的大小变量,或许我们可以让小球的大小也变成随机的:

1
2
3
4
5
for (int i = 0; i < 20; i++) {
    float radius = 1 + 2 * (rand()/(float)RAND_MAX); //random between 1 - 3
    Ball* ball = new Ball(m_world, radius);
    balls.push_back( ball );
}

pic

啊哦…有不对劲的地方,小球之间的碰撞貌似不再正确。如果你想构造一个复杂一点场景,你需要考虑这一问题,然而你已经自信满满的让debug draw在你花哨的图形面前消失。但是既然我们手中有debug draw,而且它可以作为第二方案来查看物理引擎到底发生了什么:

pic

好吧,虽然再次使用debug draw让人有些纠结,但是我只是想说明用debug draw能够更有效的保证开发进度。最后,问题出在物理实体的大小上,所以我们只要调整一下图形的缩放代码就ok了:

1
2
3
4
5
6
7
//inside Ball::renderAtBodyPosition
glPushMatrix();
glTranslatef( pos.x, pos.y, 0 );
glRotatef( angle * RADTODEG, 0, 0, 1 );
glScalef( m_radius, m_radius, 1 ); //add this to correct size
render();
glPopMatrix();

pic

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值