一、添加子模块¶
二、实现细节¶
- 实现物体效果需要rigidbody和box2dcollider两个组件
- 添加了rigidbody和box2dcollider两个组件,需要修改面板以及序列化代码
- 以后要实现:每次运行结束后可以重置物体的位置(保存上下文)
运行原理浅谈¶
- 物体附有包围盒、标识什么类型的rigidbody等
- 创建一个2D环境(设置重力)
- 点击运行
- 由定义好的参数box2D计算模拟物体的下一帧的位置
- 然后把模拟的位置给物体的transform
- 有脚本的box2D物理运行顺序——有待搞清楚
-
- Script-Physic-Render顺序: 脚本影响pyhsic然后渲染,当前帧得到结果
- Physic-Script-Render顺序 先Physic-脚本-渲染,则当前渲染的是上一帧的物理模拟计算的结果
-
三、核心原理¶
Components¶
新增刚体和碰撞器组件 刚体: - 刚体类型:默认Static - 固定旋转 - runtimebody 碰撞器: - 偏移 - 大小 - 密度? - 摩擦力 - 归还? - 运行时的夹具
// Physics
struct Rigidbody2DComponent
{
enum class BodyType { Static = 0, Dynamic, Kinematic };
BodyType Type = BodyType::Static;
bool FixedRotation = false;
// Storage for runtime
void* RuntimeBody = nullptr;
Rigidbody2DComponent() = default;
Rigidbody2DComponent(const Rigidbody2DComponent&) = default;
};
struct BoxCollider2DComponent
{
glm::vec2 Offset = { 0.0f, 0.0f };
glm::vec2 Size = { 0.5f, 0.5f };
// TODO(Yan): move into physics material in the future maybe
float Density = 1.0f;
float Friction = 0.5f;
float Restitution = 0.0f;
float RestitutionThreshold = 0.5f;
// Storage for runtime
void* RuntimeFixture = nullptr;
BoxCollider2DComponent() = default;
BoxCollider2DComponent(const BoxCollider2DComponent&) = default;
};
Scene.h¶
加入box2d的World类
class b2World;
Scene
void OnRuntimeStart();
void OnRuntimeStop();
private:
b2World* m_PhysicsWorld = nullptr;
Scene.cpp¶
采用脚本->Physics->渲染架构
// Box2D
#include "box2d/b2_world.h"
#include "box2d/b2_body.h"
#include "box2d/b2_fixture.h"
#include "box2d/b2_polygon_shape.h"
static b2BodyType Rigidbody2DTypeToBox2DBody(Rigidbody2DComponent::BodyType bodyType)
{
switch (bodyType)
{
case Rigidbody2DComponent::BodyType::Static: return b2_staticBody;
case Rigidbody2DComponent::BodyType::Dynamic: return b2_dynamicBody;
case Rigidbody2DComponent::BodyType::Kinematic: return b2_kinematicBody;
}
CORE_DEBUG_ASSERT(false, "Unknown body type");
return b2_staticBody;
}
void Scene::OnRuntimeStart()
{
//1.设置一个物理世界,重力为-9.8f
m_PhysicsWorld = new b2World({ 0.0f, -9.8f });
//1.1为当前场景视图所有具有物理组件的实体创建b2Body
auto view = m_Registry.view<Rigidbody2DComponent>();
for (auto e : view)
{
//
Entity entity = { e, this };
auto& transform = entity.GetComponent<TransformComponent>();
auto& rb2d = entity.GetComponent<Rigidbody2DComponent>();
// 2.1 主体定义用来指定动态类型和参数
b2BodyDef bodyDef;
bodyDef.type = Rigidbody2DTypeToBox2DBody(rb2d.Type);
bodyDef.position.Set(transform.Translation.x, transform.Translation.y);
bodyDef.angle = transform.Rotation.z;
// 2.2 由b2BodyDef创建主体
b2Body* body = m_PhysicsWorld->CreateBody(&bodyDef);
body->SetFixedRotation(rb2d.FixedRotation);// 是否固定旋转
rb2d.RuntimeBody = body;
if (entity.HasComponent<BoxCollider2DComponent>())
{
auto& bc2d = entity.GetComponent<BoxCollider2DComponent>();
// 3.1定义盒子包围盒
b2PolygonShape boxShape;
boxShape.SetAsBox(bc2d.Size.x * transform.Scale.x, bc2d.Size.y * transform.Scale.y);// 包围盒跟随物体的size而变化
// 3.2定义fixture,fixture包含定义的包围盒
b2FixtureDef fixtureDef;
fixtureDef.shape = &boxShape;
fixtureDef.density = bc2d.Density;
fixtureDef.friction = bc2d.Friction;
fixtureDef.restitution = bc2d.Restitution;
fixtureDef.restitutionThreshold = bc2d.RestitutionThreshold;
body->CreateFixture(&fixtureDef);
}
}
void Scene::OnRuntimeStop()
{
delete m_PhysicsWorld;
m_PhysicsWorld = nullptr;
}
physics写在渲染前面
// Physics
//根据物理更新transform
{
const int32_t velocityIterations = 6;
const int32_t positionIterations = 2;
m_PhysicsWorld->Step(ts, velocityIterations, positionIterations);
// Retrieve transform from Box2D
auto view = m_Registry.view<Rigidbody2DComponent>();
for (auto e : view)
{
Entity entity = { e, this };
auto& transform = entity.GetComponent<TransformComponent>();
auto& rb2d = entity.GetComponent<Rigidbody2DComponent>();
b2Body* body = (b2Body*)rb2d.RuntimeBody;
const auto& position = body->GetPosition();
transform.Translation.x = position.x;
transform.Translation.y = position.y;
transform.Rotation.z = body->GetAngle();
}
}
以及模版的特化
template<>
void Scene::OnComponentAdded<Rigidbody2DComponent>(Entity entity, Rigidbody2DComponent& component)
{
}
template<>
void Scene::OnComponentAdded<BoxCollider2DComponent>(Entity entity, BoxCollider2DComponent& component)
{
}
SceneSerializer¶
主要就是添加vec2的各种支持
以及对bodytype的序列化和反序列化的工具函数
static std::string RigidBody2DBodyTypeToString(Rigidbody2DComponent::BodyType bodyType)
{
switch (bodyType)
{
case Rigidbody2DComponent::BodyType::Static: return "Static";
case Rigidbody2DComponent::BodyType::Dynamic: return "Dynamic";
case Rigidbody2DComponent::BodyType::Kinematic: return "Kinematic";
}
CORE_DEBUG_ASSERT(false, "Unknown body type");
return {};
}
static Rigidbody2DComponent::BodyType RigidBody2DBodyTypeFromString(const std::string& bodyTypeString)
{
if (bodyTypeString == "Static") return Rigidbody2DComponent::BodyType::Static;
if (bodyTypeString == "Dynamic") return Rigidbody2DComponent::BodyType::Dynamic;
if (bodyTypeString == "Kinematic") return Rigidbody2DComponent::BodyType::Kinematic;
CORE_DEBUG_ASSERT(false, "Unknown body type");
return Rigidbody2DComponent::BodyType::Static;
}
EditorLayer¶
游戏运行时开始时再启动
void EditorLayer::OnScenePlay()
{
m_SceneState = SceneState::Play;
m_ActiveScene->OnRuntimeStart();
}
void EditorLayer::OnSceneStop()
{
m_SceneState = SceneState::Edit;
m_ActiveScene->OnRuntimeStop();
}
SceneHierachyPanel¶
新增几个组件的绘制