1
\$\begingroup\$

For context, this is inside my custom 2D game engine. Inside the editor code, when the 'play scene' button is clicked, the scene creates it's b2World and then creates all of the bodies necessary. Then in the update method the editor checks if it should update the scene and calls Scene::updateRuntime(const float& dt) if the scene is currently playing which should update the correct entities

What is Wrong:

I initialize b2World and create it with a gravity of -9.8, but when I call the Step() method and then update the corresponding entities, none of the positions change even though my log shows that the correct updates are running and my data is correct.

Please note: I tried to pull a minimally reproduceable example out of the project, but I'm building this engine to learn and so my code is unfortunately a little too coupled at the moment (hopefully this changes in the future) and attempting to separate this tiny area of scene updates from the rest of my code base proved to be more than I could handle. Because of this I am trying to include as much information as I can so please forgive me.

What I have tried:

  1. I have printed out and logged the timestep (averaging about 0.016-0.017), position data, and random other data which appears correct
  2. I have ran my debugger and stepped through the code repeatedly, but nothing is wrong except for the lack of physics
  3. I made a new project outside of my game engine just to track the numbers from box2d. This example is copied and pasted from the docs and that, of course, worked just fine, so I copied the relevant lines back into my engine (almost verbatim) and it didn't work anymore.

extra things -

  1. b2World->GetGravity() gives the correct value
  2. I can successfully manipulate my Entity's positions in the manner I show in the code (updating the physics body's position and then setting the entity position equal to the physics body position) and the only time it doesn't successfully update is if I try and do it with b2World::Step()

Here is the scene class Scene.hpp

class Scene {
        core::AssetLibrary<graphics::VertexArray> Meshes;
        b2World* world;

        std::map<int , Entity> entities;
        entt::registry entRegistry;
        int numEnts , totalEntsCreated;
        bool playing;
        std::string name , path;

        friend class Entity;
        friend class ScriptedEntity;
        friend class SceneSettingGUI;
        friend class SceneSerializer;

        public:
            Scene();
            ~Scene();

            void updateFromEditor();
            void updateRuntime(const float& dt);
            void render();

            inline bool isPlaying() const { return playing; }
            void playScene();
            void pauseScene();
            void stopScene();

            Entity createEnt(const std::string& name = "{BLANK ENTITY}");
            void destroyEntity(Entity& ent);
            Entity& getEntity(entt::entity handle);
            Entity& getMainCameraEntity();
            Entity getNullEnt();
            inline entt::registry& Entts() { return entRegistry; }
            inline int getNumEnts() const { return numEnts; }  
            inline int getTotalEntsMade() const { return totalEntsCreated; }

            inline core::AssetLibrary<graphics::VertexArray>& getVertLib() { return Meshes; }
            inline std::string getName() const { return name; }
            inline std::string getPath() const { return path; }

            inline void setSceneName(const std::string& name) { this->name = name; }
            inline void setScenePath(const std::string& path) { this->path = path; }

            void createSprite();
    };

Scene.cpp

static b2BodyType MachyPhysicsTypeToBox2DType(PhysicsBody2DComponent::PhysicsType type) {
        switch (type) {
            case PhysicsBody2DComponent::PhysicsType::Static: return b2_staticBody; break;
            case PhysicsBody2DComponent::PhysicsType::Kinematic: return b2_kinematicBody; break;
            case PhysicsBody2DComponent::PhysicsType::Dynamic: return b2_dynamicBody; break;

            default:
                MACHY_ASSERT(false , "Unknown Physics Type");
                return b2_staticBody;
            break;    
        }
    }

    Scene::Scene() : numEnts(0) , totalEntsCreated(0) , playing(false) , name("{Blank Scene}") {
        Entity nullEnt;
        nullEnt.handle = entt::null;

        entities[0] = nullEnt;

        std::shared_ptr<graphics::VertexArray> VA = core::FileSystem::loadVertexFile(sceneBaseMeshPath);
        std::shared_ptr<graphics::VertexArray> tVA = core::FileSystem::loadVertexFile(sceneTexMeshPath);
        
        Meshes.load(VA->getName() , VA);
        Meshes.load(tVA->getName() , tVA);

        world = nullptr;
    }

    Scene::~Scene() {
        entRegistry.each([&] (auto entity) { entRegistry.destroy(entity); });
    }

    void Scene::updateFromEditor() {
        return;
    }

    void Scene::updateRuntime(const float& dt) {

        entRegistry.view<NativeScript>().each([=](auto entity , auto& script) {
                if (script.instance == nullptr) {
                    script.instance = script.BindScript();
                    script.instance->entity = { entity };
                    script.instance->entity.setContext(this);
                    script.instance->onCreation();
                }
                script.instance->onUpdate();
            });

        entRegistry.view<CameraComponent , PositionComponent>().each([=](auto entity , auto& cam , auto& pos) {
                pos.pos.x = cam.cameraPos.x;
                pos.pos.y = cam.cameraPos.y;
                pos.pos.z = cam.cameraPos.z;
                pos.rotation.x = cam.cameraRotation;
                cam.camera->setHeight(pos.pos.z);
            });

        const int32 velIterations = 8;
        const int32 posIterations = 3;

        float timestep = 1.0f / 60.0f;
        world->SetAllowSleeping(false);
        world->Step(timestep , velIterations , posIterations);

        auto view = entRegistry.view<PhysicsBody2DComponent>();
        for (auto& ent : view) {
            Entity entity{ ent };
            entity.setContext(this);

            auto& pos = entity.GetComponent<PositionComponent>();
            auto& physBody = entity.GetComponent<PhysicsBody2DComponent>();

            b2Body* physics = (b2Body*)physBody.runTimePhysics;
            const auto position = physics->GetPosition();
            pos.pos.x = position.x;
            pos.pos.y = position.y;
            pos.rotation.z = physics->GetAngle();
        }

        return;
    }

    void Scene::render()  {
        
        auto sprites = entRegistry.view<RenderComponent , PositionComponent>();
        auto cameras = entRegistry.view<CameraComponent>();

        if (playing) {
            if (cameras.size() > 0)
                for (auto& ent : cameras)
                    if (entRegistry.valid(ent)) {
                        Entity entity{ ent };
                        entity.setContext(this);
                        auto& cam = entity.GetComponent<CameraComponent>();
                        MachY::Instance().getRM().submit(MACHY_SUBMIT_RENDER_CMND(PushCamera , cam.camera));
                    }
        }

        for (const auto ent : sprites) {
            if (entRegistry.valid(ent)) {
                Entity entity{ ent };
                entity.setContext(this);
                if (entity.HasComponent<RenderComponent>() && entity.HasComponent<RenderComponent>()){
                    auto& renderable = entity.GetComponent<RenderComponent>();
                    auto& position = entity.GetComponent<PositionComponent>();

                    glm::mat4 model = position.getModel();
                    if (renderable.skeleton.get() != nullptr && renderable.material.get() != nullptr)
                        MachY::Instance().getRM().submit(MACHY_SUBMIT_RENDER_CMND(RenderVertexArrayMaterial , renderable.skeleton , renderable.material , model));
                }
            }
        }

        if (playing) {
            if (cameras.size() > 0)
                for (auto& ent : cameras)
                    if (entRegistry.valid(ent)) {
                        MachY::Instance().getRM().submit(MACHY_SUBMIT_RENDER_CMND(PopCamera));
                    }
        }

        return;
    }

    void Scene::playScene() {
        playing = true;

        b2Vec2 grav{ 0.f , -9.8f };
        world = new b2World(grav);

        auto view = entRegistry.view<PhysicsBody2DComponent>();
        for (auto& ent : view) {

            MACHY_INFO("Creating Physics Simulation");
            
            Entity entity{ ent };
            entity.setContext(this);

            auto& pos = entity.GetComponent<PositionComponent>(); 
            auto& physics = entity.GetComponent<PhysicsBody2DComponent>();
            
            b2BodyDef newBodyDef;
            newBodyDef.type = MachyPhysicsTypeToBox2DType(physics.type);
            newBodyDef.position.Set(pos.pos.x , pos.pos.y);
            newBodyDef.angle = pos.rotation.z;

            b2Body* newBody = world->CreateBody(&newBodyDef);
            newBody->SetFixedRotation(physics.fixedRotation);
            physics.runTimePhysics = newBody;
        }

        return;
    }

    void Scene::pauseScene() {
        playing = false;

        delete world;
        world = nullptr;

        return;
    }

    void Scene::stopScene() {
        playing = false;

        return;
    }

    Entity Scene::createEnt(const std::string& name) {
        Entity newEnt(entRegistry.create());
        newEnt.setContext(this);

        auto& id = entRegistry.emplace<EntityID>(newEnt.get());
        auto& pos = entRegistry.emplace<PositionComponent>(newEnt.get());

        numEnts++;
        totalEntsCreated++;

        id.ID = totalEntsCreated;
        id.name = name;

        entities[id.ID] = newEnt;

        return newEnt;
    }

    void Scene::destroyEntity(Entity& ent) {
        if (entRegistry.valid(ent.get())) {
            entRegistry.destroy(ent.get());
            ent.nullify();
            numEnts--;
        }
        return;
    }

    Entity& Scene::getEntity(entt::entity handle) {
        auto& id1 = entRegistry.get<EntityID>(handle);
        for (auto& entItr : entities) 
            if (entRegistry.valid(entItr.second.get())) {
                auto& id2 = entRegistry.get<EntityID>(entItr.second.get());
                if (id2.ID == id1.ID) {
                    return entities[id1.ID];
                }
            }

        return entities[0];
    }

    Entity& Scene::getMainCameraEntity() {
        auto view = entRegistry.view<game::EntityID , game::CameraComponent>();
        for (auto ent : view) {
            const auto& id = view.get<game::EntityID>(ent);
            const auto& camera = view.get<game::CameraComponent>(ent);
            if (camera.camera != nullptr)
                return entities[id.ID];
        }

        return entities[0];
    }

    Entity Scene::getNullEnt() {
        return entities[0];
    }

Here are the relevant Entity Component entityComponents.hpp

/// position
    struct PositionComponent {
        glm::vec3 pos , size , rotation;

        PositionComponent() : pos(glm::vec3(0.f)) , size(glm::vec3(1.f)) , rotation(glm::vec3(0.f)) {}

        glm::mat4 getModel() {
            glm::mat4 rot = glm::toMat4(glm::quat(rotation));
            glm::mat4 model = glm::mat4(1.f);
            model = glm::translate(model , pos) * rot * glm::scale(model , size);
            return model;
        }
    };

    /// sprites
    struct RenderComponent {
        std::shared_ptr<graphics::VertexArray> skeleton;
        std::shared_ptr<graphics::Material> material;
        glm::vec3 color;
        std::string meshName , matName;

        RenderComponent() = default;
        RenderComponent(const RenderComponent&) = default;
        RenderComponent(std::shared_ptr<graphics::VertexArray> skeleton , std::shared_ptr<graphics::Material> material) 
            : skeleton(skeleton) , material(material) , color(glm::vec3(0.f)) {}
    };

    /// cameras
    struct CameraComponent {
        std::shared_ptr<graphics::Camera> camera;
        glm::vec3 cameraPos;
        float cameraRotation;

        CameraComponent() : cameraPos(glm::vec3(0.f)) , cameraRotation(0.f) {
            camera = std::make_shared<graphics::Camera>();
            camera->setHeight(3.f);
        }
    };

    // physics
    struct PhysicsBody2DComponent {
        enum class PhysicsType{ Static = 0 , Kinematic , Dynamic };
        PhysicsType type;
        bool fixedRotation;

        void* runTimePhysics;

        PhysicsBody2DComponent() : type(PhysicsType::Static) , runTimePhysics(nullptr) , fixedRotation(false) {}
        PhysicsBody2DComponent(const PhysicsBody2DComponent&) = default;
    };

    // scripting
    struct NativeScript {
        ScriptedEntity* instance = nullptr;
        bool bound;

        ScriptedEntity*(*BindScript)();
        void (*DestroyScript)(NativeScript*);
        
        NativeScript() : instance(nullptr) , bound(false) {}
        ~NativeScript() { DestroyScript(this); }

        template <typename T>
        void Bind() {
            BindScript = []() { return static_cast<ScriptedEntity*>( new T() ); };
            DestroyScript = [](NativeScript* script) { delete script->instance; script->instance = nullptr; };

            bound = true;
        }
    };
\$\endgroup\$
2
  • \$\begingroup\$ What does this line do? Entity entity{ ent }; Is it copying an Entity and it's components? If that's so, it looks like your code modifies a copy of the object and the discards it, instead of moving the actual object. \$\endgroup\$
    – Vaillancourt
    Dec 20, 2022 at 2:29
  • \$\begingroup\$ @Vaillancourt it copies the uint32 handle of the entity not the actual entity, I got this working though and need to update the question with an answer. It turns out I was handling my enums for b2Body type wrong haha. \$\endgroup\$
    – YamMan
    Dec 21, 2022 at 1:07

1 Answer 1

0
\$\begingroup\$

There was an issue with my enum, probably smart to sure your enums are in the right order lmao

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .