219 lines
4.2 KiB
C++
219 lines
4.2 KiB
C++
// 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 convert(const Point& pos)
|
|
{
|
|
return b2Vec2(pos.x / GLOBAL_SCALE, pos.y / GLOBAL_SCALE);
|
|
}
|
|
|
|
Point convert(const b2Vec2& pos)
|
|
{
|
|
return Point(pos.x * GLOBAL_SCALE, pos.y * GLOBAL_SCALE);
|
|
}
|
|
}
|
|
|
|
// 小圆形
|
|
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 = convert(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);
|
|
}
|
|
};
|
|
|
|
// 小方块
|
|
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 = convert(pos);
|
|
|
|
b2Body* body = world->CreateBody(&bodyDef);
|
|
|
|
SetUserData(body);
|
|
|
|
b2PolygonShape shape;
|
|
b2Vec2 sz = convert(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);
|
|
}
|
|
};
|
|
|
|
// 固定的木板
|
|
class Board
|
|
: public GeometryNode
|
|
{
|
|
public:
|
|
Board(b2World* world, const Size& size, const Point& pos)
|
|
{
|
|
SetGeometry(new RectangleGeometry(Point(), size));
|
|
SetStrokeColor(Color::White);
|
|
SetFillColor(Color(0, 0, 0, 0));
|
|
|
|
SetSize(size);
|
|
SetAnchor(0.5f, 0.5f);
|
|
SetRotation(10);
|
|
SetPosition(pos);
|
|
|
|
b2BodyDef groundBodyDef;
|
|
groundBodyDef.position = convert(GetPosition());
|
|
groundBodyDef.angle = 10 * math::constants::PI_F / 180.f;
|
|
|
|
b2Body* groundBody = world->CreateBody(&groundBodyDef);
|
|
|
|
b2PolygonShape groundBox;
|
|
b2Vec2 sz = convert(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 消息
|
|
auto window = Window::Instance();
|
|
SetSize(window->GetSize());
|
|
SetResponsible(true);
|
|
|
|
// 添加消息监听
|
|
AddListener(MouseEvent::Click, Closure(this, &MainScene::Click));
|
|
|
|
// 创建物理世界
|
|
world_ = new b2World(b2Vec2(0, 10));
|
|
|
|
auto board = new Board(world_, Size(GetWidth() - 100, 20), Point(window->GetSize().x / 2, window->GetSize().y - 50));
|
|
AddChild(board);
|
|
|
|
auto 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(convert(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)
|
|
{
|
|
auto circle = E_NEW Circle(world_, Point{ evt.mouse.x, evt.mouse.y });
|
|
AddChild(circle);
|
|
}
|
|
else if (evt.mouse.button == MouseButton::Right)
|
|
{
|
|
auto rect = E_NEW Square(world_, Point{ evt.mouse.x, evt.mouse.y });
|
|
AddChild(rect);
|
|
}
|
|
}
|
|
};
|
|
|
|
int main()
|
|
{
|
|
try
|
|
{
|
|
Application app;
|
|
app.Init();
|
|
|
|
ScenePtr scene = new MainScene;
|
|
app.EnterScene(scene);
|
|
app.Run();
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
::MessageBoxA(nullptr, e.what(), "An exception has occurred!", MB_ICONERROR | MB_TASKMODAL);
|
|
}
|
|
return 0;
|
|
}
|