// Copyright (C) 2019 Nomango #include "easy2d.h" #include "Box2D/Box2D.h" using namespace easy2d; // // Box2D 非常好地适应了 米/千克/秒 的单位, 所以 // 它并不是以像素为单位进行计算. // 我们在平衡 Box2D 和 Easy2D 世界的度量时, 需要 // 进行一些转换. // namespace { const float GLOBAL_SCALE = 100.0f; b2Vec2 Vec2Convert(const Point& pos) { return b2Vec2(pos.x / GLOBAL_SCALE, pos.y / GLOBAL_SCALE); } Point Vec2Convert(const b2Vec2& pos) { return Point(pos.x * GLOBAL_SCALE, pos.y * GLOBAL_SCALE); } } // 小圆形 E2D_DECLARE_SMART_PTR(Circle); class Circle : public Sprite { public: Circle(b2World* world, const Point& pos) { Load(L"circle.png"); SetAnchor(0.5f, 0.5f); SetScale(0.7f); b2BodyDef bodyDef; bodyDef.type = b2_dynamicBody; bodyDef.position = Vec2Convert(pos); b2Body* body = world->CreateBody(&bodyDef); SetUserData(body); b2CircleShape shape; shape.m_radius = GetWidth() / GLOBAL_SCALE / 2 * 0.7f; b2FixtureDef fixtureDef; fixtureDef.shape = &shape; fixtureDef.density = 1.0f; fixtureDef.friction = 0.3f; body->CreateFixture(&fixtureDef); body->SetUserData(this); } }; // 小方块 E2D_DECLARE_SMART_PTR(Square); class Square : public Sprite { public: Square(b2World* world, const Point& pos) { Load(L"square.png"); SetAnchor(0.5f, 0.5f); SetScale(0.7f); b2BodyDef bodyDef; bodyDef.type = b2_dynamicBody; bodyDef.position = Vec2Convert(pos); b2Body* body = world->CreateBody(&bodyDef); SetUserData(body); b2PolygonShape shape; b2Vec2 sz = Vec2Convert(GetSize() / 2 * 0.7f); shape.SetAsBox(sz.x, sz.y); b2FixtureDef fixtureDef; fixtureDef.shape = &shape; fixtureDef.density = 1.0f; fixtureDef.friction = 0.3f; body->CreateFixture(&fixtureDef); body->SetUserData(this); } }; // 固定的木板 E2D_DECLARE_SMART_PTR(Board); class Board : public GeometryNode { public: Board(b2World* world, const Size& size, const Point& pos) { GeometryPtr geo = new RectangleGeometry(Point(), size); SetGeometry(geo); SetStrokeColor(Color::White); SetFillColor(Color(0, 0, 0, 0)); SetSize(size); SetAnchor(0.5f, 0.5f); SetRotation(10); SetPosition(pos); b2BodyDef groundBodyDef; groundBodyDef.position = Vec2Convert(GetPosition()); groundBodyDef.angle = 10 * math::constants::PI_F / 180.f; b2Body* groundBody = world->CreateBody(&groundBodyDef); b2PolygonShape groundBox; b2Vec2 sz = Vec2Convert(Point{ size.x / 2, size.y / 2 }); groundBox.SetAsBox(sz.x, sz.y); groundBody->CreateFixture(&groundBox, 0.0f); } }; class MainScene : public Scene { b2World* world_; public: MainScene() { // 设置可响应状态, 使场景可以接收到鼠标 Click 消息 SetResponsible(true); // 添加消息监听 AddListener(Event::Click, Closure(this, &MainScene::Click)); // 创建物理世界 world_ = new b2World(b2Vec2(0, 10)); BoardPtr board = new Board(world_, Size(GetWidth() - 100, 20), Point(GetWidth() / 2, GetHeight() - 50)); AddChild(board); CirclePtr circle = new Circle(world_, Point(320, 240)); AddChild(circle); } ~MainScene() { if (world_) delete world_; } void OnUpdate(Duration dt) override { // 更新 Box2D 世界 world_->Step(dt.Seconds(), 6, 2); // 更新每一个物理对象的位置和旋转角度 b2Body* body = world_->GetBodyList(); while (body) { Node* node = (Node*)body->GetUserData(); b2Body* next = body->GetNext(); if (node) { const b2Vec2& pos = body->GetPosition(); node->SetPosition(Vec2Convert(pos)); node->SetRotation(body->GetAngle() * 180.f / math::constants::PI_F); // 移除掉落到场景外的物体 if (node->GetPosition().y > GetHeight() + 50) { body->SetUserData(0); world_->DestroyBody(body); node->RemoveFromParent(); } } body = next; } } void Click(Event const& evt) { if (evt.mouse.button == MouseButton::Left) { CirclePtr circle = new Circle(world_, Point{ evt.mouse.x, evt.mouse.y }); AddChild(circle); } else if (evt.mouse.button == MouseButton::Right) { SquarePtr rect = new Square(world_, Point{ evt.mouse.x, evt.mouse.y }); AddChild(rect); } } }; int main() { try { Application app; Options options(L"Box2D Demo"); app.Init(options); ScenePtr scene = new MainScene; app.EnterScene(scene); app.Run(); } catch (std::exception& e) { ::MessageBoxA(nullptr, e.what(), "An exception has occurred!", MB_ICONERROR | MB_OK); } return 0; }