FrameWork / GameObject / Member / Animation.h
#pragma once
class Animation
{
public:
UINT frameMax;
UINT boneMax;
Matrix** arrFrameBone;// 프레임갯수* 본갯수 (차후 Rvalue && 로 사용할 것)
float tickPerSecond;
string file;
Animation();
~Animation();
void LoadFile(string file);
void SaveFile(string file);
};
enum class AnimationState
{
LOOP,
ONCE,
STOP//Pause
};
class Animations
{
struct Animator
{
float frameWeight = 0.0f;
UINT currentFrame = 0;
UINT nextFrame = 1;
UINT animIdx = 0;
AnimationState animState = AnimationState::STOP;
};
void AnimatorUpdate(Animator& Animator);
public:
Animations();
~Animations();
void Update();
Animator currentAnimator;
Animator nextAnimator;
bool isChanging;
float blendtime;
float Changedtime;
float aniScale = 1.0f;
vector<shared_ptr<Animation>> playList;
Matrix GetFrameBone(int boneIndex);
void PlayAnimation(AnimationState state, UINT idx, float blendtime = 0.2f);
void RenderDetail();
float GetPlayTime();// 0처음 ~ 1 끝
};
FrameWork / GameObject / Member / Animation.cpp
#include "framework.h"
Animation::Animation() // 생성자( 초기화 )
{
frameMax = 0;
boneMax = 0;
tickPerSecond = 0;
arrFrameBone = nullptr;
file = "";
}
Animation::~Animation() // 소멸자
{
for (UINT i = 0; i < frameMax; i++)
{
delete[] arrFrameBone[i];
}
delete[] arrFrameBone;
}
void Animation::LoadFile(string file) // 파일 불러오기
{
this->file = file;
BinaryReader in;
wstring path = L"../Contents/Animation/" + Util::ToWString(file);
in.Open(path);
frameMax = in.Int();
boneMax = in.Int();
tickPerSecond = in.Float();
arrFrameBone = new Matrix * [frameMax]; // 읽어온 frameMax 만큼 배열로 생성
for (UINT i = 0; i < frameMax; i++)
{
arrFrameBone[i] = new Matrix[boneMax]; // 생성된 배열에 읽어온 boneMax 만큼 배열로 생성
}
for (UINT i = 0; i < frameMax; i++)
{
for (UINT j = 0; j < boneMax; j++)
{
arrFrameBone[i][j] = in.matrix();
}
}
in.Close();
}
void Animation::SaveFile(string file) // 읽어온 파일 저장
{
this->file = file;
BinaryWriter out;
wstring path = L"../Contents/Animation/" + Util::ToWString(file);
out.Open(path);
out.Int(frameMax);
out.Int(boneMax);
out.Float(tickPerSecond);
for (UINT i = 0; i < frameMax; i++)
{
for (UINT j = 0; j < boneMax; j++)
{
out.matrix(arrFrameBone[i][j]);
}
}
out.Close();
}
void Animations::AnimatorUpdate(Animator& Animator) // 애니메이터 업데이트
{
if (Animator.animState == AnimationState::LOOP) // 무한반복
{
Animator.frameWeight += DELTA * playList[Animator.animIdx]->tickPerSecond * aniScale;
if (Animator.frameWeight >= 1.0f)
{
Animator.frameWeight = 0.0f;
Animator.currentFrame++;
Animator.nextFrame++;
if (Animator.nextFrame >= playList[Animator.animIdx]->frameMax)
{
Animator.currentFrame = 0;
Animator.nextFrame = 1;
}
}
}
else if (Animator.animState == AnimationState::ONCE) // 애니메이션 프레임0~끝까지 1회만 재생
{
Animator.frameWeight += DELTA * playList[Animator.animIdx]->tickPerSecond * aniScale;
if (Animator.frameWeight >= 1.0f)
{
Animator.frameWeight = 0.0f;
Animator.currentFrame++;
Animator.nextFrame++;
if (Animator.nextFrame >= playList[Animator.animIdx]->frameMax)
{
Animator.currentFrame--;
Animator.nextFrame--;
/*Animator.currentFrame = 0;
Animator.nextFrame = 1;*/
Animator.animState = AnimationState::STOP;
}
}
}
}
Animations::Animations()
{
isChanging = false;
}
Animations::~Animations()
{
for (int i = 0; i < playList.size(); i++)
{
SafeReset(playList[i]); // vector<shared_ptr<Animation>>playList 스마트포인터 해제
}
}
void Animations::Update()
{
if (isChanging) // 전환시
{
AnimatorUpdate(nextAnimator);
Changedtime += DELTA;
if (Changedtime > blendtime)
{
Changedtime = 0.0f;
//다음애니메이션을 현재애니메이션으로 바꾼다.
currentAnimator = nextAnimator;
isChanging = false;
}
}
AnimatorUpdate(currentAnimator);
}
Matrix Animations::GetFrameBone(int boneIndex)
{
if (isChanging)
{
return
playList[currentAnimator.animIdx]->arrFrameBone[currentAnimator.nextFrame][boneIndex]
* (1.0f - Changedtime / blendtime)
+
(playList[nextAnimator.animIdx]->arrFrameBone[nextAnimator.nextFrame][boneIndex]
* nextAnimator.frameWeight +
playList[nextAnimator.animIdx]->arrFrameBone[nextAnimator.currentFrame][boneIndex]
* (1.0f - nextAnimator.frameWeight)) * (Changedtime / blendtime);
}
return playList[currentAnimator.animIdx]->arrFrameBone[currentAnimator.nextFrame][boneIndex]
* currentAnimator.frameWeight +
playList[currentAnimator.animIdx]->arrFrameBone[currentAnimator.currentFrame][boneIndex]
* (1.0f - currentAnimator.frameWeight);
}
void Animations::PlayAnimation(AnimationState state, UINT idx, float blendtime)
{
Changedtime = 0.0f;
isChanging = true;
currentAnimator.animState = AnimationState::STOP;
nextAnimator.animState = state;
this->blendtime = blendtime;
nextAnimator.animIdx = idx;
nextAnimator.currentFrame = 0;
nextAnimator.nextFrame = 1;
}
void Animations::RenderDetail() // ImGui 사용을 위한 함수
{
ImGui::Text("PlayTime : %f", GetPlayTime());
ImGui::InputFloat("AniScale", &aniScale, 0.1f, 1.0f); // SlideFloat 에서 편의상 InputFloat 으로 변경
for (UINT i = 0; i < playList.size(); i++)
{
string name = to_string(i) + playList[i]->file;
string button = name + "Stop";
if (ImGui::Button(button.c_str()))
{
PlayAnimation(AnimationState::STOP, i);
}
//ImGui::SameLine(); // 편의상 해제
button = name + "Once";
if (ImGui::Button(button.c_str()))
{
PlayAnimation(AnimationState::ONCE, i);
}
//ImGui::SameLine(); // 편의상 해제
button = name + "Loop";
if (ImGui::Button(button.c_str()))
{
PlayAnimation(AnimationState::LOOP, i);
}
}
}
float Animations::GetPlayTime() // 플레이타임은 전체프레임이 비율로 바뀐다. 0~1 사이값
{
if (isChanging)
{
return (float)nextAnimator.nextFrame /
(float)(playList[nextAnimator.animIdx]->frameMax - 1);
}
return (float)currentAnimator.nextFrame /
(float)(playList[currentAnimator.animIdx]->frameMax - 1);
}
복습필요!
Ps.특이사항
현재 Animation 적용후 재생시 각각의 bonse(관절)을 임의 조절 할 수 있도록 코드가 변경 되었다,
단, 런타임중 바뀌게되므로 연산량이 증가하기때문에 주의 해야한다!
기존 공식
OffSet * NodeTransform * Parent
변경 후
Tpose * (Tpose * Animation) * Parent
'공부 > Animation' 카테고리의 다른 글
[Animation] 스키닝(Skinning) 부분 정리 (0) | 2023.07.04 |
---|---|
[Animation] 애니메이션 블렌드(Animation Blend) (0) | 2023.07.04 |
[Skeleton] Skeleton.h & Skeleton.cpp 정리 (0) | 2023.06.28 |
[Animation] 키프레임(KeyFrame) 애니메이션 (0) | 2023.06.28 |
[Animation] 스키닝(Skinning) 애니메이션 (0) | 2023.06.22 |
댓글