응애맘마조
230705 강의 본문
어제일자에 이어서 애니메이션 효과의 자연스러운 움직임에 대해서 강의했습니다. 애니메이션 블렌드를 했는데 애니메이션에서 다른 애니메이션으로 전환 시 끝 부분과 다른 애니메이션의 첫 부분을 섞어서 자연스럽게 표현한다고 보면 이해하기 쉽습니다.
#pragma once
class Animation
{
public:
UINT frameMax;
UINT boneMax;
Matrix** arrFrameBone;// 프레임갯수* 본갯수 ( local Inverse * ani)
float tickPerSecond;
string file;
Animation();
~Animation();
void LoadFile(string file);
void SaveFile(string file);
};
enum class AnimationState
{
STOP,//Pause
LOOP,
ONCE_LAST,
ONCE_FIRST,
};
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.5f);
void RenderDetail();
float GetPlayTime();// 0처음 ~ 1 끝
};
#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];
for (UINT i = 0; i < frameMax; i++)
{
arrFrameBone[i] = new Matrix[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_LAST)
{
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)
{
if (Animator.animState == AnimationState::ONCE_LAST)
{
Animator.currentFrame--;
Animator.nextFrame--;
}
else
{
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]);
}
}
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)
{
/*currentAnimator.animState = state;
currentAnimator.animIdx = idx;
currentAnimator.currentFrame = 0;
currentAnimator.nextFrame = 1;*/
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::Text("PlayTime : %f", GetPlayTime());
ImGui::SliderFloat("AniScale", &aniScale, 0.001f, 10.0f);
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);
}
button = name + "OnceLast";
if (ImGui::Button(button.c_str()))
{
PlayAnimation(AnimationState::ONCE_LAST, i);
}
button = name + "OnceFirst";
if (ImGui::Button(button.c_str()))
{
PlayAnimation(AnimationState::ONCE_FIRST, i);
}
button = name + "Loop";
if (ImGui::Button(button.c_str()))
{
PlayAnimation(AnimationState::LOOP, i);
}
}
}
float Animations::GetPlayTime()
{
if (isChanging)
{
return (float)nextAnimator.nextFrame /
(float)(playList[nextAnimator.animIdx]->frameMax - 1);
}
return (float)currentAnimator.nextFrame /
(float)(playList[currentAnimator.animIdx]->frameMax - 1);
}
애니메이션 클래스의 헤더와 cpp 파일입니다. 이렇게 실행을 하게 되면 실행은 되나 만약 도중에 애니메이션 효과를 전환할 때 부드럽지 않고 프레임이 바로 바뀌게 됩니다.
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);
}
Changedtime = 0.0f;
isChanging = true;
currentAnimator.animState = AnimationState::STOP;
nextAnimator.animState = state;
this->blendtime = blendtime;
nextAnimator.animIdx = idx;
nextAnimator.currentFrame = 0;
nextAnimator.nextFrame = 1;
PlayAnimation 함수에 있는 주석으로 된 코드를 풀고 위 2칸으로 나눈 코드를 주석 처리 하면 애니메이션에서 다른 애니메이션으로 바꿔도 이어지는 느낌이 있는 것처럼 전환이 가능합니다.
읽어주셔서 감사합니다.
Comments