수업


📘 주제

TinyXML2를 이용한 Animation 리소스의 XML 직렬화 및 역직렬화 시스템 구현

이 강의의 핵심은, 메모리 상에만 존재하던 Animation 데이터를 외부 XML 파일로 저장(Save)하고, 이를 다시 불러와서(Load) 메모리 객체로 복원하는 직렬화 시스템을 직접 구현하는 것이다.
이를 통해 추후 툴에서 만든 애니메이션 리소스를 에디터가 자동으로 로드하거나, 기획자가 만든 데이터 파일을 게임이 불러와서 사용할 수 있게 된다.


📗 개념

🔹 직렬화 (Serialization)

  • 객체 데이터를 텍스트 또는 바이너리 파일로 변환하여 저장 가능한 형태로 만드는 작업.
  • 예: Animation의 Keyframe 정보를 XML로 저장.

🔹 역직렬화 (Deserialization)

  • 저장된 데이터를 다시 읽어와 객체 형태로 복원하는 작업.
  • 예: 저장된 XML 파일에서 Keyframe들을 읽어와 Animation을 복원.

🔹 XML vs JSON

  • XML: 태그 기반, 계층 구조 표현에 강함, 가독성 좋음 → 이번 강의에서 사용.
  • JSON: 가볍고 빠름, 웹에서 많이 사용됨, 파싱 성능이 좋음.
  • 대부분의 회사에서는 기획자가 엑셀로 작성한 데이터를 XML/JSON으로 변환해 사용.

🔹 tinyxml2

  • C++에서는 표준 XML 파서가 없기 때문에, 외부 라이브러리인 tinyxml2를 사용함.
  • 간단하고 경량이며, XML을 트리 구조로 생성/파싱 가능.

📘 용어 정리

용어설명
직렬화메모리 객체를 파일로 저장 가능한 형태로 변환
역직렬화파일에서 데이터를 읽어와 객체 형태로 복원
XML트리 구조 기반의 태그 데이터 표현 포맷
TinyXML2C++용 경량 XML 처리 라이브러리
XMLElementXML 노드를 나타내는 객체
AttributeXML 노드에 포함된 key-value 형태의 속성
Keyframeoffset, size, time 정보를 가진 애니메이션 프레임

📙 코드 분석

✅ 1. tinyxml2 적용

📌 적용 방법

  1. tinyxml2.h, tinyxml2.cpp 다운로드 후 프로젝트에 복사
  2. Visual Studio에서 Utils 필터에 추가
  3. tinyxml2.cpp → 속성 → "미리 컴파일된 헤더 사용 안 함" 설정
    • 또는 파일 상단에 #include "pch.h" 직접 작성
  4. pch.h에 아래 추가:
#include "tinyxml2.h"
using namespace tinyxml2;

✅ 2. Animation 저장 함수 구현 (Save)

void Animation::Save(const wstring& path)
{
    tinyxml2::XMLDocument doc;

    // 루트 노드 생성: <Animation>
    XMLElement* root = doc.NewElement("Animation");
    doc.LinkEndChild(root);

    // 이름(string으로 변환 필요) 및 속성 설정
    string nameStr(GetName().begin(), GetName().end());
    root->SetAttribute("Name", nameStr.c_str());
    root->SetAttribute("Loop", _loop);
    root->SetAttribute("TexturePath", "TODO"); // 경로 설정은 추후

    // Keyframe들을 XML 노드로 저장
    for (const auto& keyframe : _keyframes)
    {
        XMLElement* node = doc.NewElement("Keyframe");
        root->LinkEndChild(node);

        node->SetAttribute("OffsetX", keyframe.offset.x);
        node->SetAttribute("OffsetY", keyframe.offset.y);
        node->SetAttribute("SizeX", keyframe.size.x);
        node->SetAttribute("SizeY", keyframe.size.y);
        node->SetAttribute("Time", keyframe.time);
    }

    // 파일 저장
    string pathStr(path.begin(), path.end());
    auto result = doc.SaveFile(pathStr.c_str());
    assert(result == XMLError::XML_SUCCESS);
}

🔍 해설

  • doc.NewElement(...): XML 태그를 생성
  • SetAttribute(...): 노드에 key-value 속성 설정
  • LinkEndChild(...): 자식 노드를 붙임
  • SaveFile(...): XML 파일로 저장
  • TODO: Texture 경로는 추후 지정

✅ 3. 저장 테스트 – ResourceManager

void ResourceManager::CreateDefaultAnimation()
{
    shared_ptr<Animation> animation = make_shared<Animation>();
    animation->SetName(L"SnakeAnim");
    animation->SetTexture(Get<Texture>(L"Snake"));
    animation->SetLoop(true);

    animation->AddKeyframe({ Vec2{0.f,100.f}, Vec2{100.f,100.f}, 0.1f });
    animation->AddKeyframe({ Vec2{100.f,100.f}, Vec2{100.f,100.f}, 0.1f });
    animation->AddKeyframe({ Vec2{200.f,100.f}, Vec2{100.f,100.f}, 0.1f });
    animation->AddKeyframe({ Vec2{300.f,100.f}, Vec2{100.f,100.f}, 0.1f });

    Add(animation->GetName(), animation);

    // 저장 실행
    animation->Save(L"TestAnimation.xml");
}

🔍 결과

  • 저장된 TestAnimation.xml 예시:
<Animation Name="SnakeAnim" Loop="true" TexturePath="TODO">
    <Keyframe OffsetX="0" OffsetY="100" SizeX="100" SizeY="100" Time="0.1"/>
    <Keyframe OffsetX="100" OffsetY="100" SizeX="100" SizeY="100" Time="0.1"/>
    ...
</Animation>

✅ 4. Animation 로드 함수 구현 (Load)

void Animation::Load(const wstring& 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");
    _name = wstring(nameStr.begin(), nameStr.end());

    _loop = root->BoolAttribute("Loop");
    _path = path;

    // Keyframe 정보 읽기
    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);
    }
}

🔍 해설

  • LoadFile(...): XML 로드
  • FirstChildElement(): 루트 <Animation> 접근
  • Attribute(...): 속성 값 가져오기
  • NextSiblingElement(): 다음 <Keyframe> 순회
  • AddKeyframe(...): 메모리에 복원

✅ 5. 로드 테스트

shared_ptr<Animation> anim2 = make_shared<Animation>();
anim2->Load(L"TestAnimation.xml");
  • 위 XML 파일을 다시 읽어 anim2 객체에 복원
  • 중단점으로 보면 _keyframes에 정상적으로 데이터가 로드됨

✅ 핵심 요약

  • Animation 정보를 외부 XML 파일로 저장/불러오기 가능한 구조를 완성
  • TinyXML2를 활용해 C++에서도 손쉽게 XML 조작 가능
  • Keyframe들의 위치(offset), 크기(size), 지속시간(time)이 모두 저장됨
  • 추후 이 구조를 기반으로 툴 연동, 에디터에서 애니메이션 자동 저장/불러오기, 리소스 관리 시스템 통합이 가능해짐
  • 모든 리소스(Material, Mesh, Shader 등)도 동일한 방식으로 확장 가능

profile
李家네_공부방

0개의 댓글