Hazel game engine (108) start, end, copy scene

If there are errors in the code, terminology, etc. in the text, please correct me

foreword

  • of this program

    1. In order to realize click to run, copy the current scene to become the running scene, and click to end to destroy the current scene.
    2. Duplicate entity in current scene

    The most important thing is how to copy a scene. To copy a scene, you need to copy all the entities in the current scene and the components they contain, but the current entt library does not contain the API for copying components, so we need to manually write the copied entities .

  • How to implement the replication scenario

    entt's registry plays a key role entt::registry

    1. Create a new scene, create an entity with the same name and uuid as the old scene for the new scene , and use map to store the relationship (the uuid of the old entity corresponds to the new entity)
    2. Traverse the old entities of all uuid components in the old scene, and use the uuid-map of the old entities to correspond to the new entities
    3. Get all the components of the old entity , and then use the API to copy all the components of the old entity to the new entity . The copied components will include the attribute values ​​​​of the copied components
  • How to achieve copy entity

    1. Create a new entity with the same name
    2. Detect whether the old entity has a corresponding component, and add or modify the corresponding new entity component if there is one
  • implementation details

    When running the scene, pay attention to setting the context of the panel as the new scene, and set it back to the editing scene ( old scene) after stopping.

Key code + code flow

  • Registry add or replace components

    T& component = m_Scene->m_Registry.emplace_or_replace<T>(m_EntityHandle, std::forward<Args>(args)...);
    
  • copy scene

    • Click Run in the editorlayer, execute the Copy function of Scene to copy the current scene, and set the context of the panel to the new scene

      void EditorLayer::OnScenePlay()
      {
              
              
          if (!m_EditorScene) {
              
              
              HZ_CORE_WARN("editorscene is null");
              return;
          }
          m_SceneState = SceneState::Play;
      
          // 复制新场景给活动场景
          m_ActiveScene = Scene::Copy(m_EditorScene);
          m_ActiveScene->OnRuntimeStart(); // 复制完新场景就开始运行
          // 当前上下文是新场景----------------------------
          m_SceneHierarchyPanel.SetContext(m_ActiveScene);
      }
      
    • Execute the Copy function of Scene to copy the current scene

      Ref<Scene> Scene::Copy(Ref<Scene> other)
      {
              
              
          // 1.1创建新场景
          Ref<Scene> newScene = CreateRef<Scene>();
      
          newScene->m_ViewportWidth = other->m_ViewportWidth;
          newScene->m_ViewportHeight = other->m_ViewportHeight;
      
          auto& srcSceneRegistry = other->m_Registry;
          auto& dstSceneRegistry = newScene->m_Registry;
          std::unordered_map<UUID, entt::entity> enttMap;
      
          auto idView = srcSceneRegistry.view<IDComponent>();
          for (auto e : idView) {
              
              
              UUID uuid = srcSceneRegistry.get<IDComponent>(e).ID;
              const auto& name = srcSceneRegistry.get<TagComponent>(e).Tag;
              // 1.2为新场景创建和旧场景同名和uuid的实体
              Entity newEntity = newScene->CreateEntityWithUUID(uuid, name);
              // 1.3并用**map存入(旧实体的uuid对应新实体)的关系**
              enttMap[uuid] = (entt::entity)newEntity;// UUID类需要哈希
          }
      
          // 拷贝组件,除了IDcomponent与tagcomponent,因CreateEntityWithUUID创建了
          CopyComponent<TransformComponent>(dstSceneRegistry, srcSceneRegistry, enttMap);
          CopyComponent<SpriteRendererComponent>(dstSceneRegistry, srcSceneRegistry, enttMap);
          CopyComponent<CameraComponent>(dstSceneRegistry, srcSceneRegistry, enttMap);
          CopyComponent<NativeScriptComponent>(dstSceneRegistry, srcSceneRegistry, enttMap);
          CopyComponent<Rigidbody2DComponent>(dstSceneRegistry, srcSceneRegistry, enttMap);
          CopyComponent<BoxCollider2DComponent>(dstSceneRegistry, srcSceneRegistry, enttMap);
      
          return newScene;
      }
      // 为复制场景的实体的组件的辅助方法
      template<typename Component>
      static void CopyComponent(entt::registry& dst, entt::registry& src, const std::unordered_map<UUID, entt::entity>& enttMap) {
              
              
          auto view = src.view<Component>();
          // 2.1遍历旧场景所有uuid组件的旧实体
          for (auto e : view) {
              
              
              UUID uuid = src.get<IDComponent>(e).ID;
              HZ_CORE_ASSERT(enttMap.find(uuid) != enttMap.end());
              // 2.2用** 旧实体的uuid - map - 对应新实体 * *
              entt::entity dstEnttID = enttMap.at(uuid);
              // 3.1获取旧实体的组件
              auto& component = src.get<Component>(e);
              // 3.2然后用API,** 复制旧实体的组件给新实体**
              dst.emplace_or_replace<Component>(dstEnttID, component);// 添加或替换,保险
          }
      }
      
  • copy entity

    • Press the shortcut key ctrl+d in the editorlayer to execute the OnDuplicateEntity function to copy the currently selected entity

      void EditorLayer::OnDuplicateEntity(){
              
              
          // 编辑场景时 可以复制实体
          if (m_SceneState != SceneState::Edit) {
              
              
              return;
          }
          Entity selectedEntity = m_SceneHierarchyPanel.GetSelectedEntity();
          if (selectedEntity) {
              
              
              m_EditorScene->DuplicateEntity(selectedEntity);
          }
      }
      
    • Scene's DuplicateEntity function performs specific operations

      1. Create a new entity with the same name as the old entity
      2. copy component
      void Scene::DuplicateEntity(Entity entity){
              
              
          // 1.创建旧实体同名的新实体
          std::string name = entity.GetName();
          Entity newEntity = CreateEntity(name);
          // 2.复制组件
          CopyComponentIfExists<TransformComponent>(newEntity, entity);
          CopyComponentIfExists<SpriteRendererComponent>(newEntity, entity);
          CopyComponentIfExists<CameraComponent>(newEntity, entity);
          CopyComponentIfExists<NativeScriptComponent>(newEntity, entity);
          CopyComponentIfExists<Rigidbody2DComponent>(newEntity, entity);
          CopyComponentIfExists<BoxCollider2DComponent>(newEntity, entity);
      }
      // 为复制实体的辅助方法
      template<typename Component>
      static void CopyComponentIfExists(Entity dst, Entity src) {
              
              
          if (src.HasComponent<Component>()) {
              
              
              dst.AddOrReplaceComponent<Component>(src.GetComponent<Component>());
          }
      }
      

Effect

1.3 run

1.4 run

  • small point of attention

    Here the camera also has a box2d component. Note that the z of the camera is 5, which means it is in front of the green obstacle, and it can also be simulated with the green obstacle during operation.

    Adjust the white entity z to any value, and it can also interact with green obstacles!

    But the camera is a perspective projection, so the z size of the white entity will affect the perception of the white entity. If z is too small, the entity will appear to shrink. If z is too large to exceed the z of the camera, the entity will not appear.

    Please add a picture description

The bug encountered by Cherno

  • The hierarchy panel does not display entities

    The context scene of the panel is not set, so the entity of the scene cannot be obtained

  • Ctrl+D copy entity error

    Issue Overview: Citing Issues

    problem analysis:

    Duplicate the entity in the Editorlayer ctrl+d, call the DuplicateEntity function of the scene, and create a new entity with the same name as the old entity

    void Scene::DuplicateEntity(Entity entity)
    {
          
          
        Entity newEntity = CreateEntity(entity.GetName());
    

    Execute the CreateEntityWithUUID function in CreateEntity

    Entity Scene::CreateEntity(const std::string& name)
    {
          
          
        // 创建新的uuid
        return CreateEntityWithUUID(UUID(), name);
    }
    Entity Scene::CreateEntityWithUUID(UUID uuid, const std::string& name)
    {
          
          
        // 添加默认组件
        Entity entity = {
          
           m_Registry.create(),this };
        entity.AddComponent<TransformComponent>();
        entity.AddComponent<IDComponent>(uuid); // 使用存在的uuid,不创建新的
        // 下面这一行!!!!!!!!!!!!!!!
        auto& tag = entity.AddComponent<TagComponent>();
        tag.Tag = name.empty() ? "Entity" : name;
        return entity;
    }
    

    When the CreateEntityWithUUID function executes to the auto& tag = entity.AddComponent(); code, it will cause the parameter name to fail to obtain the string.

    problem causes:

    1. Entity's GetName is a string to get TagComponent

      const std::string& GetName() {
              
               return GetComponent<TagComponent>().Tag; }
      
    2. The parameter strings of CreateEntity and CreateEntityWithUUID are all reference types

    3. When the auto& tag = entity.AddComponent(); code is executed, a new TagComponent component is added, and the entt library will move the component to the memory location, so the address referenced by the string will be invalid

    Solution:

    Assign to local string variable after Scene's DuplicateEntity function GetName()

    void Scene::DuplicateEntity(Entity entity)
    {
          
          
        std::string name = entity.GetName();
        Entity newEntity = CreateEntity(name);
    

Guess you like

Origin blog.csdn.net/qq_34060370/article/details/132265977