32 Data

gksrudtlr
|2025. 1. 12. 18:35

Data

XML, JSON

  • 현재 예제에서는 파일 포멧이 따로 없고 메모리상에 정보만을 가지고 있다

  • 이를 특정 파일 포멧에 저장하고 그 포멧안에 있는 정보를 메모리에 저장하여 표현을 할 수 있게 할것이다

  • 우리가 특정 파일 포멧을 설정해도 되지만 그것보단 많이 사용하는 XML이나 JSON 형태의 파일을 많이 사용한다

  • 이 파일을 사용하기 위해서는 특정 라이브러리를 사용해야 하는대 C++은 제공되는 라이브러리가 없기 때문에 외부 라이브러리중 tinyxml2라는 라이브러리를 사용할 것이다

    tinyxml 추가

  • 이 라이브러리를 다운받아 99.Headers/Utils 폴더에 넣어 추가해준 뒤 빌드를 해준다

  • 이때 미리컴파일된 헤더가 문제가 생길 것인데 이는 .cpp 파일에 미리 컴파일된 헤더를 추가하거나 비리 컴파일된 헤더를 사용하지 않게 해주면 해결된다

  • 마찬가지로 미리 컴파일된 헤더에 헤더를 추가하여 어디서든 사용할 수 있게 만들어준다

    pch.h
    ...
    #include "tinyxml2.h"
    using namespace tinyxml2;
    ...

    XML Save, Load

  • 이번 실습에서는 간단하게 xml을 save하고 load하는 것을 할것인데, 이러한 정보들을 채운 클래스가 Animation뿐이므로 이를 이용해 실습을 할것이다

  • XML 파일은 노드를 만들어 트리를 만들어준다 생각하면 되는데, 이런식으로 계층 구조를 만들어주게 되는 것이다

    XML Save

    Animation.cpp
    ...
    void Animation::Save(const wstring& path)
    {
      ResourceBase::Save(path);
    
      tinyxml2::XMLDocument doc;
      XMLElement* root = doc.NewElement("Animation");
      doc.LinkEndChild(root);
    
      string nameStr(GetName().begin(), GetName().end());
      root->SetAttribute("Name", nameStr.c_str());
      root->SetAttribute("Loop", bLoop);
      root->SetAttribute("TexturePath", "TODO");
    
      for (const Keyframe& keyframe : m_vKeyframes)
      {
          XMLElement* node = doc.NewElement("Keyframe");
          root->LinkEndChild(node);
    
          node->SetAttribute("OffsetX", keyframe.offset.x);
          node->SetAttribute("OffsetY", keyframe.offset.y);
          node->SetAttribute("SizesetX", keyframe.size.x);
          node->SetAttribute("SizesetY", keyframe.size.y);
          node->SetAttribute("Time", keyframe.time);
      }
    
      string pahtStr(path.begin(), path.end());
      XMLError result = doc.SaveFile(pahtStr.c_str());
      assert(result == XMLError::XML_SUCCESS);
    }
    ...
  • XMLDocument 객체를 만들어 XML 문서를 생성할 준비를 해준다

  • doc.NewElement("Animation")으로 새로운 XML 요소를 만들어 주는데 요소의 태그 이름은 Animation으로 이 값을 XMLElement* 에 저장해준다

  • 저장된 변수를 doc.LinkEndChild에 넣어 XML의 최상위 자식으로 추가되는 것이다

  • XML에서는 char* 를 사용해 문자열을 나타내기 때문에 wstring을 사용하는 Animation의 이름은 string으로 바꿔 .c_str을 사용해 저장해줘야한다

  • 이때 element에 속성 값을 저장하기 위해 root->SetAttribute 함수를 사용해 속성의 이름, 속성 값을 넘겨주면 된다

  • Animation 요소의 속성 값들인 Name, Loop 여부, Texture의 위치를 셋팅해준다

  • 또한 Animation마다 keyframe이 존재할 것이므로 for each문을 통해 매개변수 Keyframe 요소로 만들어 root의 자식으로 추가해 속성값들을 가져와 저장해준다

    실제 저장

  • Animation을 XML로 저장할 함수를 만들었으니 직접 실행해 XML 파일이 생성이 됐는지 확인해 볼것이다

    ResourceMGR.cpp
    ...
    void ResourceMGR::CreateDefaultAnimation()
    {
    ...
      //XML
      animation->Save(L"TestAnimation.xml");
    }
  • ResourceMGR에서 Animation을 만들어주는 함수에서 Animation을 만들고 Add를 통해 추가해준 뒤 Save 함수를 콜해 저장해 줄것인데 이때 저장될 이름과 확장자를 path로 넘겨준다

  • 실행을 하게되면 xml 파일이 잘 생성된 것을 볼 수 있다

    XML Load

  • Load는 Save와 대칭적이니 Save에서 했던 반대방향으로 해주면 된다

    Animation.cpp
    void Animation::Load(const wstring& path)
    {
      ResourceBase::Load(path);
    
      tinyxml2::XMLDocument doc;
      string pathStr(path.begin(), path.end());
      XMLError error = doc.LoadFile(pathStr.c_str());
      assert(error == XMLError::XML_SUCCESS);
    
      XMLElement* root = doc.FirstChildElement();
      string nameStr = root->Attribute("Name");
      m_sName = wstring(nameStr.begin(), nameStr.end());
    
      bLoop = root->BoolAttribute("Loop");
      m_sPath = path;
    
      //Load Texture
      XMLElement* node = root->FirstChildElement();
      for (; node != nullptr; node = node->NextSiblingElement())
      {
          Keyframe keyframe;
    
          keyframe.offset.x = node->FloatAttribute("OffsetX");
          keyframe.offset.y = node->FloatAttribute("OffsetY");
          keyframe.size.x = node->FloatAttribute("SizeX");
          keyframe.size.y = node->FloatAttribute("SizeY");
          keyframe.time = node->FloatAttribute("Time");
    
          AddKeyframe(keyframe);
      }
    }
  • SMLDocument를 만들어 path를 char* 형으로 바꾼 값으로 파일을 읽어오기 위해 LoadFile을 사용해준다

  • 이번에는 doc에 저장된 요소중 첫번째 요소를 FirstChildElement로 가져와 그 안에 있는 요소를 가져와 반대로 wstring 형태로 만들어 m_sName에 저장해 줄것이다

  • bLoop에도 역시 BoolAttribute를 이용해 요소를 가져와 값을 저장해주고, m_sPath에는 함수에서 전달받은 path를 저장해준다

  • doc에 저장된 첫번째 요소의 속성을 다 저장했으면 texture에대한 정보인 keyframe을 가져와 저장해 주기 위해 root에 FirstChildElement로 root에 연결된 자식 중 첫번째 요소를 가져와 저장해준다

  • 반복문을 통해 맨 마지막 요소까지 돌면서 요소안에 속성을 keyframe에 저장한 뒤 AddKeyframe을 통해 keyframe을 추가해주는데 이때 다음 요소로 넘어가기 위해 NextSiblingElement함수를 사용해주면 된다

    Load 사용해보기

    void ResourceMGR::CreateDefaultAnimation()
    {
    ...
      //XML
      animation->Save(L"TestAnimation.xml");
    
      shared_ptr<Animation> anim2 = make_shared<Animation>();
      animation->Load(L"TestAnimation.xml");
    }
  • Load 함수를 사용해보기 위해 XML을 Save 했던 ResourceMGR에서 Animation을 하나 새로 만들어 그 객체로 Load 함수를 불러줄 것이다

  • 이때 path는 Save로 만들어진 XML 파일의 이름을 넘겨주어 저장한 애니메이션 그대로 Load되게 해주는 것이다

    2D 종료

  • 이렇게 DirectX에서 2D로 간단한 실습 예제가 끝이났다

  • 물론 완벽한 엔진이 된건 아니지만 이를 통해 파이프라인이라던지 Scene을 그리거나 Object를 추가하고 애니메이션을 그리는 등의 실습을 통해 3D로 가기 전 기초를 다지는 느낌으로 강의가 진행됐었다

  • 이해가 100로 다 된것은 아니지만 앞으로의 3D 강의를 정독하고 다시한번 더 강의를 보면서 더욱더 이해를 할 수 있게 노력할 것이다

'DirectX' 카테고리의 다른 글

34 사각형 띄우기  (0) 2025.01.16
33 새 프로젝트 시작  (0) 2025.01.16
30 Material, Mesh  (1) 2025.01.12
29 Render Manager  (0) 2025.01.12
28 ResourceManager  (0) 2025.01.12