跳转至

一、添加子模块

二、实现细节

  • 实现物体效果需要rigidbody和box2dcollider两个组件
  • 添加了rigidbody和box2dcollider两个组件,需要修改面板以及序列化代码
  • 以后要实现:每次运行结束后可以重置物体的位置(保存上下文)

运行原理浅谈

  • 物体附有包围盒、标识什么类型的rigidbody等
    1. 创建一个2D环境(设置重力)
    2. 点击运行
    3. 由定义好的参数box2D计算模拟物体的下一帧的位置
    4. 然后把模拟的位置给物体的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

新增几个组件的绘制