DirectX

30. 엔진구조_Animation

devRiripong 2024. 1. 28.
반응형

올려주신 프로젝트에서 Snake.bmp파일을 복붙 해서 코드가 있는 폴더로 가져온다.

 

유니티에서 sprite, multiple로 설정하고 100x100픽셀로 잘라서 애니메이션으로 쓰는 기능을 제공하고 있다. 언리얼도 플립북이라는 기능으로 잘라서 원하는 만큼 애니메이션화 할 수 있는 기능을 갖고 있다.

 

그거를 만들어 볼 것이다.

 

WinAPI 때 비슷한 방식으로 하긴 했다.

uv좌표로 원하는 영역을 맞춰주면 된다.

유니티에서 원하는 스프라이트를 Hierarchy로 드래그 앤 드롭했을 때 애니메이션 파일이 생성되는데,

그 파일을 살펴보면 SpriteRenderer라는 Sprite를 그려주는 역할을 하는 아이 하나와 Animator라는 애가 들어가 있다는 것을 볼 수 있다.

Animator라는 애는 Sprite에 Snake_0이라는 애를 들고 있는데, 애가 애니메이션과 관련된 정보를 담고 있다고 보면 된다.

중요한 건 Animator라는 컴포넌트를 들고 있다는 거고, 이게 어떻게 보면 하나의 cd 플레이어라고 생각하면 된다. Snake_0는 음반자체라고 비유 수 있다.

 

유니티에서 Animation을 제공하기 위해서는 Animator와 Animation이 구분된다.

언리얼 엔진도 마찬가지다. 플립북 액터라는 상속받는 클래스를 이용해서 작업을 할 수 있다.

결국 Animation이라는 정보와 Animation을 틀어주는 Animator라는 컴포넌트가 있다는 게 결론이다.

일단 Animation 클래스로 가서 작업을 해본다.

 

1. Animation 클래스

1) Animatinon 클래스 작성

#pragma once
#include "ResourceBase.h"

struct Keyframe
{
	// 어디부터 어디까지 그릴 것인지
	Vec2 offset = Vec2{ 0.f, 0.f }; 
	Vec2 size = Vec2{ 0.f, 0.f }; 

	// 몇 초 통안 그릴 것인지
	float time = 0.f; 
};

class Texture;  

class Animation : public ResourceBase
{
	using Super = ResourceBase;
public:
	Animation(); 
	virtual ~Animation(); 

	virtual void Load(const wstring& path) override; 
	virtual void Save(const wstring& path) override; 

	void SetLoop(bool loop) { _loop = loop; }
	bool IsLoop() { return _loop; }

	void SetTexture(shared_ptr <Texture> texture) { _texture = texture; }
	shared_ptr<Texture> GetTexure() { return _texture; } 
	Vec2 GetTextureSize();

	const Keyframe& GetKeyframe(int32 index); 
	int32 GetKeyframeCount(); 
	void AddKeyframe(const Keyframe& keyframe);

private:
	bool _loop = false; 
	shared_ptr<Texture> _texture; 
	vector<Keyframe> _keyframes; 

};
#include "pch.h"
#include "Animation.h"
#include "Texture.h"

Animation::Animation() : Super(ResourceType::Animation)
{
}

Animation::~Animation()
{
}

void Animation::Load(const wstring& path)
{
}

void Animation::Save(const wstring& path)
{
}

Vec2 Animation::GetTextureSize()
{
	return _texture->GetSize();
}

const Keyframe& Animation::GetKeyframe(int32 index)
{
	return _keyframes[index];
}

int32 Animation::GetKeyframeCount()
{
	return static_cast<int32>(_keyframes.size()); 
}

void Animation::AddKeyframe(const Keyframe& keyframe)
{
	_keyframes.push_back(keyframe); 
}

2) Texture 클래스에 GetSize함수 작성

Animation을 작업하다가 Animation ::GetTextureSize에서 _texture→GetSize가 필요해서

Texture 클래스에 GetSize를 만든다.

Vec2 _size = { 0.f, 0.f };
Vec2 GetSize() { return _size; }

이걸 Texture.h에 추가하고,

Texture::Create에서

_size.x = md.width; 
_size.y = md.height;

이렇게 각각 기입을 하면 된다.

빌드를 하면 빌드가 된다.

 

2. Animator 클래스 

Animation을 사용하려면 Animator라는 애가 있어야 한다.

음반을 재생할 수 있는 새로운 부품이 있어야 하는 것이다.

1) Animator 클래스 작성

00.Engine / Component / Animatoin 필터에 Component를 기본 크래스로 하는 Animator 클래스를 생성한다.

#pragma once
#include "Component.h"
#include "Animation.h"

class Animator : public Component
{
	using Super = Component; 

public: 
	Animator(); 
	virtual ~Animator(); 

	void Init(); 
	void Update(); 
	
	shared_ptr<Animation> GetCurrentAnimation(); 
	const Keyframe& GetCurrentKeyframe(); 

	void SetAnimation(shared_ptr<Animation> animation) { _currentAnimation = animation;  }

private: 
	float _sumTime = 0.f; 

	int32 _currentKeyframeIndex = 0;
	shared_ptr<Animation> _currentAnimation; 
};
#include "pch.h"
#include "Animator.h"
#include "Game.h"
#include "TimeManager.h"

Animator::Animator()
	:Super(ComponentType::Animator)
{
}

Animator::~Animator()
{
}

void Animator::Init()
{
}

void Animator::Update()
{
	shared_ptr<Animation> animation = GetCurrentAnimation(); 
	if (animation == nullptr)
		return; 

	const Keyframe& keyframe = animation->GetKeyframe(_currentKeyframeIndex); 

	float deltaTime = TIME->GetDeltaTime(); 
	_sumTime += deltaTime; 

	if (_sumTime >= keyframe.time)
	{
		_currentKeyframeIndex++; 
		int32 totalCount = animation->GetKeyframeCount(); 
		
		if (_currentKeyframeIndex >= totalCount)
		{
			if (animation->IsLoop())
				_currentKeyframeIndex = 0;
			else
				_currentKeyframeIndex = totalCount - 1; 
		}

		_sumTime = 0.f; 
	}
}

shared_ptr<Animation> Animator::GetCurrentAnimation()
{
	return _currentAnimation; 
}

const Keyframe& Animator::GetCurrentKeyframe()
{
	return _currentAnimation->GetKeyframe(_currentKeyframeIndex); 
}

유일하게 바뀌는 건 현재 어떤 애를 틀어야 하는지 정보만 KeyframeIndex로 바뀐다.

 

빌드를 하면 잘 된다.

 

애니메이션을 틀 때 여러 단계가 있다.

일단 ResourceManager로 가서 리소스가 있는지를 확인해야 한다.

 

2) ResourceManager::CreateDefaultTexture()에서 애니메이션에 쓸 Texture를 생성해서  _resources에 넣기

ResourceManager::CreateDefaultTexture()에 추가해 줬던 새로운 메쉬를 넣어 준다.

void ResourceManager::CreateDefaultTexture()
{
	{
		auto texture = make_shared<Texture>(_device);
		texture->SetName(L"chiikawa");
		texture->Create(L"chiikawa.png");
		Add(texture->GetName(), texture);
	}
	{
		auto texture = make_shared<Texture>(_device);
		texture->SetName(L"Snake");
		texture->Create(L"Snake.bmp");
		Add(texture->GetName(), texture);
	}
}

 

3) ResourceManager::CreateDefaultAnimation() 에서 각 Keyframe의 수치를 작성해 넣기

ResourceManager::CreateDefaultAnimation()을 만들어 줘야 한다.

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

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

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

	// XML
}

애니메이션을 로드하는 부분은 이렇게 만들어 주면 된다.

이제 SceneManager에 가서 오브젝트를 관리하는데 가야 한다.

 

4) SceneManager::LoadTestScene()에서 monster 오브젝트에 animator 컴포넌트 추가하고, animator 컴포넌트에 _resources에 넣어줬던 "Snake"라는 Animation을 Get 해서 animator 컴포넌트에 세팅하기

shared_ptr <Scene> SceneManager::LoadTestScene()에서 //Monster부분에 Animator를 넣어야 한다.

shared_ptr<Scene> SceneManager::LoadTestScene()
{
	shared_ptr<Scene> scene = make_shared<Scene>(); 

	// Camera
	{
		shared_ptr<GameObject> camera = make_shared<GameObject>(_graphics->GetDevice(), _graphics->GetDeviceContext());
		{
			camera->GetOrAddTransform();
			camera->AddComponent(make_shared<Camera>());
			scene->AddGameObject(camera); 
		}
	}

	// Monster
	{
		shared_ptr<GameObject> monster = make_shared<GameObject>(_graphics->GetDevice(), _graphics->GetDeviceContext());
		{
			monster->GetOrAddTransform();
			auto meshRenderer = make_shared<MeshRenderer>(_graphics->GetDevice(), _graphics->GetDeviceContext());
			monster->AddComponent(meshRenderer);

			auto material = RESOURCES->Get<Material>(L"Default"); 
			meshRenderer->SetMaterial(material); 

			auto mesh = RESOURCES->Get<Mesh>(L"Rectangle"); 
			meshRenderer->SetMesh(mesh); 
		}		
		{
			auto animator = make_shared<Animator>(); 
			monster->AddComponent(animator); 
			auto anim = RESOURCES->Get<Animation>(L"SnakeAnim"); 
			animator->SetAnimation(anim); 
		}
		scene->AddGameObject(monster);
	}

	return scene; 
}

이렇게 해주고,

#include "Animator.h"

이렇게도 해준다.

이렇게 애니메이션을 추가했다.

빌드가 잘 된다.

 

3. Animation 적용하기

 

영역을 집어주긴 했지만 적용하는 코드를 만들지 않았다.

 

1) RenderHelper.h에서 struct AnimationData를 만들어 Animation 데이터 잡기 

RenderHelper로 가서 애니메이션 데이터를 잡아 줄 것이다. 쉐이더도 이것에 맞게끔 수정을 해줘야 한다는 얘기가 된다.

RenderHelper.h에

struct AnimationData
{
	Vec2 spriteOffset; 
	Vec2 spriteSize; 
	Vec2 textureSize;
        float useAnimation; 
	float padding;
};

전체 텍스쳐 사이즈에서 잘라서 쓸 오프셋과 사이즈가 얼마인지 넣어주고 그다음에 16바이트 정렬을 해줘야 한다. useAnimation까지 사용하고 싶은 것이다.

shader 코드에서 애니메이션을 사용할지 말지에 대해 0, 1로 넘겨줄 것인데 shader 문법엔 boolean이 없다.

그리고 16바이트 정렬을 위해 padding을 추가해 준 것이다.

 

2) RenderManager.h에서 _animationData, _animationBuffer를 선언하고, AnimationData를 _animationBuffer로 복사하는 RenderManager::PushAnimationData() 정의하고, Anim::Init에서 _animationBuffer를 만들기 

그리고 RenderManager에서 관련된 모든 것들을 통합해서 관리하고 있었다.

void PushAnimationData();

가 추가된다.

그리고 비워둔 //Animation 부분에

// Animation
	AnimationData _animationData; 
	shared_ptr<ConstantBuffer<AnimationData>> _animationBuffer;

이러면 GPU 쪽에 애니메이션 정보를 넘겨주고 싶을 때도 이 아이를 이용해서 넘겨주면 된다.

PushAnimationData함수를 구현하면

void RenderManager::PushAnimationData()
{
	_animationBuffer->CopyData(_animationData);
}

RenderManager::Init에

_animationBuffer = make_shared<ConstantBuffer<AnimationData>>(_device, _deviceContext);
_animationBuffer->Create();

이렇게 AnimationData를 이용해 _animationBuffer를 만들어 주고,

이렇게 버퍼를 넘겨줄 준비가 끝났다.

 

3) RenderManager::RenderObjects()에서 Animator 컴포넌트가 있는지 확인 후 있으면 _animationData를 채워서 _animationBuffer에 복사한다. 그리고 pipeline에서 VS에 animationBuffer를 PS에 texture를 넣기

없으면 그냥 animationData를 0으로 설정해서 넘겨준다.

RenderManager::RenderObjects()에서 지금까지는 meshRenderer 컴포넌트가 있는지 확인을 했다.

void RenderManager::RenderObjects()
{
	for (const shared_ptr<GameObject>& gameObject : _renderObjects)
	{
		shared_ptr<MeshRenderer> meshRenderer = gameObject->GetMeshRenderer();
		if (meshRenderer == nullptr)
			continue;

근데 여기에 동시에 Animator 컴포넌트가 있는지 확인해서 Animation을 틀어줄 것인지를 확인할 것이다.

 

3_1) GameObject.h에 GetAnimator 함수 구현하기

GameObject.h에

shared_ptr<Animator> GetAnimator();
class Animator;

를 전방선언 하고,

GameObject.cpp에 구현부를 작성한다.

shared_ptr<Animator> GameObject::GetAnimator()
{
	shared_ptr<Component> component = GetFixedComponent(ComponentType::Animator);
	return static_pointer_cast<Animator>(component);
}

 

RenderManager.cpp에

#include "Animator.h"

를 추가하고

 

3_2) Default.hlsl에 cbuffer AnimationData 만들기 

Default.hlsl에서 대칭적으로 애니메이션을 넣어줘야 한다.

cbuffer AnimationData : register(b2)
{ 
    float2 spriteOffset; 
    float2 spriteSize; 
    float2 textureSize; 
    float useAnimation; 
}

여기선 굳이 dummy로 안 맞춰줘도 된다.

 

3_3) GetAnimator를 해서 animator가 있으면 _animationData를 채워서 PushAnimationData로 _animationBuffer에 복사한다. 그리고 pipeline에 vertexShader엔 animationBuffer를 pixelShader엔 texture를 넣는다. 

없으면 그냥 animationData를 0으로 설정해서 넘겨준다.

 

void RenderManager::RenderObjects()에서

void RenderManager::RenderObjects()
{
	for (const shared_ptr<GameObject>& gameObject : _renderObjects)
	{
		shared_ptr<MeshRenderer> meshRenderer = gameObject->GetMeshRenderer();
		if (meshRenderer == nullptr)
			continue;

		shared_ptr<Transform> transform = gameObject->GetTransform();
		if (transform == nullptr)
			continue;

		// SRT
		_transformData.matWorld = transform->GetWorldMatrix();
		PushTransformData();

		// Animation 
		shared_ptr<Animator> animator = gameObject->GetAnimator(); 
		if (animator)
		{
			const Keyframe& keyframe = animator->GetCurrentKeyframe(); 
			_animationData.spriteOffset = keyframe.offset; 
			_animationData.spriteSize = keyframe.size; 
			_animationData.textureSize = animator->GetCurrentAnimation()->GetTextureSize();
			_animationData.useAnimation = 1.f; 
			PushAnimationData(); 

			_pipeline->SetConstantBuffer(2, SS_VertexShader, _animationBuffer);
			_pipeline->SetTexture(0, SS_PixelShader, animator->GetCurrentAnimation()->GetTexure()); 
		}
		else
		{
			_animationData.spriteOffset = Vec2(0.f, 0.f);
			_animationData.spriteSize = Vec2(0.f, 0.f); 
			_animationData.textureSize = Vec2(0.f, 0.f); 
			_animationData.useAnimation = 0.f;
			PushAnimationData(); 

			_pipeline->SetConstantBuffer(2, SS_VertexShader, _animationBuffer);
			_pipeline->SetTexture(0, SS_PixelShader, meshRenderer->GetTexture());
		}

		// IA - VS - RS - PS - OM
		PipelineInfo info;
		info.inputLayout = meshRenderer->GetInputLayout();
		info.vertexShader = meshRenderer->GetVertexShader();
		info.pixelShader = meshRenderer->GetPixelShader();
		info.rasterizerState = _rasterizerState;
		info.blendState = _blendState;
		_pipeline->UpdatePipeline(info);

		_pipeline->SetVertexBuffer(meshRenderer->GetMesh()->GetVertexBuffer());
		_pipeline->SetIndexBuffer(meshRenderer->GetMesh()->GetIndexBuffer());

		_pipeline->SetConstantBuffer(0, SS_VertexShader, _cameraBuffer);
		_pipeline->SetConstantBuffer(1, SS_VertexShader, _transformBuffer);

		// _pipeline->SetTexture(0, SS_PixelShader, meshRenderer->GetTexture());
		_pipeline->SetSamplerState(0, SS_PixelShader, _samplerState);

		_pipeline->DrawIndexed(meshRenderer->GetMesh()->GetIndexBuffer()->GetCount(), 0, 0);
	}
}

빌드를 하면 에러가 발생한다.

GameObject.cpp에

#include "Animator.h"

를 추가하면 해결된다.

중단점을 찍어서

void RenderManager::RenderObjects()

// Animation 
		shared_ptr<Animator> animator = gameObject->GetAnimator(); 
		if (animator)
		{
			const Keyframe& keyframe = animator->GetCurrentKeyframe();

여기 안까지 실행이 되는지 보자.

들어온다.

하지만 출력은 안된다. 왜냐하면, 아직 shader코드를 완성하지 않았기 때문이다.

Default.hlsl에 

cbuffer AnimationData : register(b2)
{ 
    float2 spriteOffset; 
    float2 spriteSize; 
    float2 textureSize; 
    float useAnimation; 
}

이 부분이 들어오게 될 것이고

// IA-VS-RS-PS-OM
// 위치와 관련된 변화
VS_OUTPUT VS(VS_INPUT input)
{ 
    VS_OUTPUT output; 
    
    // WVP
    float4 position = mul(input.position, matWorld); // W
    position = mul(position, matView); // V
    position = mul(position, matProjection); // P 
    
//  output.position = input.position + offset; 
    output.position = position; 
    output.uv = input.uv;  
    
    return output; 
}

이 코드를 수정해 줘야 한다.

지금 상태로 실해하면

이렇게 뜬다.

그냥 이미지 전체를 보여주고 있는 것에 불과하다.

uv좌표를 지금처럼 항상 넘겨받은 uv로 해주면 되는 게 아니라

if (useAnimation == 1.0f)
    { 
        
    }

이렇게 애니를 사용할 거다라 한다면 uv 좌표를 보정해 주면 된다.

 

4) Defaul.hlsl의 VS 에서 uv 값을 전체 텍스쳐 크기와 쓸 sprite 의 size와 offset 비율에 맞게 설정하기

Defaul.hlsl의 VS

// IA-VS-RS-PS-OM
// 위치와 관련된 변화
VS_OUTPUT VS(VS_INPUT input)
{ 
    VS_OUTPUT output; 
    
    // WVP
    float4 position = mul(input.position, matWorld); // W
    position = mul(position, matView); // V
    position = mul(position, matProjection); // P 
    
//  output.position = input.position + offset; 
    output.position = position; 
    output.uv = input.uv;  
    
    if (useAnimation == 1.0f)
    { 
        output.uv *= spriteSize / textureSize; 
        output.uv += spriteOffset / textureSize; 
    }
    
    return output;
}

이렇게 수정하면

이렇게 애니메이션이 틀어지는 것을 알 수 있다.

 

참고설명)

이 코드는 버텍스 쉐이더(Vertex Shader)의 일부로 보입니다. 버텍스 쉐이더는 3D 모델의 각 정점(Vertex)에 대한 데이터를 처리하고, 이를 스크린 좌표로 변환하는 역할을 합니다. 이 과정에서 `output.uv` 값은 텍스처 매핑(Texture Mapping)을 위해 사용됩니다.

텍스처 매핑은 3D 모델의 표면에 텍스처 이미지를 매핑하는 과정입니다. 여기서 `output.uv`는 텍스처의 UV 좌표를 나타냅니다. UV 좌표는 텍스처 이미지 위의 위치를 나타내는 2D 좌표 시스템입니다.

코드의 이 부분을 살펴보면:

```c
output.uv *= spriteSize / textureSize; 
output.uv += spriteOffset / textureSize;
```

1. `output.uv *= spriteSize / textureSize;` - 여기서 `spriteSize`는 사용하고자 하는 스프라이트(텍스처의 하위 섹션)의 크기를 나타내며, `textureSize`는 전체 텍스처 이미지의 크기를 나타냅니다. `spriteSize / textureSize`는 스프라이트가 전체 텍스처에서 차지하는 비율을 계산합니다. 이 비율을 UV 좌표에 곱하면, UV 좌표는 텍스처의 해당 스프라이트 섹션 내로 제한됩니다.

2. `output.uv += spriteOffset / textureSize;` - `spriteOffset`는 전체 텍스처 내에서 스프라이트의 위치를 나타냅니다. `spriteOffset / textureSize`는 텍스처 내에서의 상대적 위치를 계산합니다. 이 값을 UV 좌표에 더함으로써, UV 좌표가 올바른 스프라이트 섹션으로 이동됩니다.

즉, 이 코드는 전체 텍스처 내에서 특정 스프라이트를 정확히 매핑하기 위해 UV 좌표를 조정합니다. `textureSize`로 나누는 이유는 UV 좌표가 0에서 1 사이의 값으로 정규화(Normalize)되어 있기 때문에, 이 비율을 사용하여 스프라이트의 정확한 위치와 크기를 계산하기 위함입니다.

 

4. 응용

1) Monster를 하나 더 등장시키고 싶다면 복사해서  Position세팅하기

만약 SecneManager.cpp의 SceneManager::LoadTestScene()에서 Monster를 하나 더 등장시키는데 위치를 다른 데로 이동시키고 싶다면

//Monster 코드를 하나 복붙 해주고 윗부분에

// Monster
	{
		shared_ptr<GameObject> monster = make_shared<GameObject>(_graphics->GetDevice(), _graphics->GetDeviceContext());
		monster->GetOrAddTransform()->SetPosition(Vec3{ 1.f, 1.f, 0.f });

이렇게 해주면 원하는 위치를 넘겨줄 수 있다.

실행하면 이렇게 두 마리가 나온다.

 

2) MonoBehaviour 상속받은 CameraMove 클래스의 Update에서 카메라 움직이는 코드 넣고, SceneManager::LoadTestScene에서 CameraMove 컴포넌트를 camera 오브젝트에 넣기

여기서 더 해보고 싶은 건 카메라를 움직이면 어떻게 될까?

01.Main 필터에 MonoBehaviour를 상속받은 CameraMove 클래스를 만든다.

#pragma once
#include "MonoBehaviour.h"
class CameraMove :  public MonoBehaviour
{
public: 
	virtual void Update() override; 
};
#include "pch.h"
#include "CameraMove.h"
#include "GameObject.h"

void CameraMove::Update()
{
	auto pos = GetTransform()->GetPosition(); 
	pos.x += 0.01f; 
	GetTransform()->SetPosition(pos);
}

이렇게 pos의 x를 조금씩 증가시켰다고 하면,

이걸 나중에 ScecneManager에서 Camera를 만들 때

shared_ptr <Scene> SceneManager::LoadTestScene()에

#include "CameraMove.h"
shared_ptr<Scene> SceneManager::LoadTestScene()
{
	shared_ptr<Scene> scene = make_shared<Scene>(); 

	// Camera
	{
		shared_ptr<GameObject> camera = make_shared<GameObject>(_graphics->GetDevice(), _graphics->GetDeviceContext());
		{
			camera->GetOrAddTransform();
			camera->AddComponent(make_shared<Camera>());
			scene->AddGameObject(camera); 
		}
		{
			camera->AddComponent(make_shared<CameraMove>()); 
		}
	}

방금 만들어준 것을 넣어준다.

실행을 하면 카메라가 오른쪽으로 이동하기 때문에 뱀이 왼쪽으로 이동하는 것처럼 보인다.

Graphics.h에서

float _clearColor[4] = { 0.5f, 0.5f, 0.5f, 0.5f };

이렇게 바탕을 회색으로 바꿔서 실행하면

이렇게 뱀이 왼쪽으로 움직이는 걸 볼 수 있다.

이런 식으로 응용해서 만들 수가 있다.

 

콘텐츠 코드를 만들 때는 00. Engine 쪽은 신경 쓰지 않고, 01. Main쪽에 MonoBehaviour를 상속받은 무엇인가를 만들어서 붙인 다음에 코드를 작업하면 된다.

일단 카메라 이동하게 한 GameMove 클래스와 SceneManager의 LoadTestScene에서 CameraMove 컴포넌트를 camera오브젝트에 추가했던 부분은 삭제한다.

WinApi랑 별 다른 차이가 없다.

 

중요한 건 hlsl부분인데 문법이 비슷하니 쓰면서 익히면 된다.

 

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

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

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

	// XML
}

여기에서 일일이 keyframe 값을 넣어주고 있는데

mfc나 툴을 만들 능력이 생기면 불러 읽어서 유니티처럼 툴로 뭔가를 할 수 있게 제공하는 게 필요하다.

큰 규모의 게임을 만들면 애니메이션을 채워주는 걸 툴로 만드느는게 목적이다.

애니메이션은 FBX로 사용하게 되겠지만 그거 말고도 맵툴이나 할 게 많다.

 

다음 시간에는 데이터를 가지고 하는 거 까지 해본다.

반응형

'DirectX' 카테고리의 다른 글

32. DirectX11 3D 입문_프로젝트 설정  (0) 2024.01.31
31. 엔진구조_Data  (0) 2024.01.29
29. 엔진구조_Material  (0) 2024.01.26
28. 엔진구조_ RenderManager  (0) 2024.01.23
27. 엔진구조_ResourceManager  (0) 2024.01.22

댓글