Programming/Dev
[009] 플레이어 컨트롤
LaivY
2023. 6. 6. 20:13
이번 게시글에서는 간단하게 플레이어를 조종하는 것을 구현해보려고 한다.
class Player : public IGameObject
{
public:
class InputComponent { ... };
class PhysicsComponent { ... };
class AnimationComponent { ... };
public:
Player();
~Player() = default;
virtual void Update(FLOAT deltaTime);
virtual void Render(const ComPtr<ID3D12GraphicsCommandList>& commandList) const;
private:
InputComponent m_inputComponent;
PhysicsComponent m_physicsComponent;
AnimationComponent m_animationComponent;
};
Player 클래스는 위와 같다.
나중에 바꿀지도 모르겠지만 일단 컴포넌트 패턴을 사용해보려고 한다.
void Player::Update(FLOAT deltaTime)
{
m_inputComponent.Update(deltaTime); // 1. 입력 처리
m_physicsComponent.Update(deltaTime); // 2. 이동 처리
m_animationComponent.Update(deltaTime); // 3. 애니메이션
}
코드를 컴포넌트로 분리했기 때문에 Player의 Update 함수에는 크게 내용이 없다.
단, 컴포넌트의 Update 함수 호출의 순서에 따라 결과가 달라지는 것은 주의해야 한다.
void Player::InputComponent::Update(FLOAT deltaTime)
{
// 좌우 이동
int dir{ 0 };
if (GetAsyncKeyState(VK_LEFT) & 0x8000)
--dir;
if (GetAsyncKeyState(VK_RIGHT) & 0x8000)
++dir;
m_player->m_physicsComponent.Move(static_cast<PhysicsComponent::Direction>(dir));
// 점프
bool isJumped{ false };
if (GetAsyncKeyState('C') & 0x8000)
{
m_player->m_physicsComponent.Jump();
isJumped = true;
}
// 애니메이션 재생
auto animationType{ AnimationComponent::AnimationType::NONE };
if (isJumped)
animationType = AnimationComponent::AnimationType::JUMP;
else if (m_player->m_animationComponent.GetAnimationType() != AnimationComponent::AnimationType::JUMP && m_player->m_animationComponent.GetAnimationType() != AnimationComponent::AnimationType::FALL)
{
if (dir != 0)
animationType = AnimationComponent::AnimationType::RUN;
else
animationType = AnimationComponent::AnimationType::IDLE;
}
if (animationType != AnimationComponent::AnimationType::NONE &&
m_player->m_animationComponent.GetAnimationType() != animationType)
m_player->m_animationComponent.PlayAnimation(animationType);
}
InputComponent에서는 입력에 대한 처리를 담당한다.
위 코드에서 볼 수 있듯이 각 컴포넌트끼리는 직접 접근하여 통신한다. 통신 방법 역시 나중에 바꿀 수도 있을 것 같다.
void Player::PhysicsComponent::Update(FLOAT deltaTime)
{
FLOAT2 beforePlayerPosition{ m_player->GetPosition() };
FLOAT2 afterPlayerPosition{};
const Platform* beforePlatform{ m_platform };
const Platform* afterPlatform{ nullptr };
// 이동. 현재 플렛폼의 높이와 플레이어의 높이가 같다면 플렛폼 위에 서있다는 것
if (m_speed.y < 0.0f && m_platform && m_platform->GetHeight(beforePlayerPosition.x) == beforePlayerPosition.y)
m_speed.y = 0.0f;
m_player->Move(FLOAT2{ static_cast<int>(m_direction) * m_speed.x * deltaTime, m_speed.y * deltaTime });
// 현재 위치에서 가장 높은 플렛폼 계산
afterPlayerPosition = m_player->GetPosition();
afterPlatform = GetTopPlatformBelowPosition(afterPlayerPosition);
// 움직이기 이전, 이후 플레이어 y좌표 사이에 이전 플렛폼 높이가 있다면 착지한 것
FLOAT platformHeight{ -FLT_MAX };
if (beforePlatform)
platformHeight = beforePlatform->GetHeight(beforePlayerPosition.x);
if (afterPlayerPosition.y < platformHeight && platformHeight < beforePlayerPosition.y)
{
afterPlatform = beforePlatform;
m_platform = beforePlatform;
OnLanding();
}
else
{
m_platform = afterPlatform;
}
// 이전, 이후 플렛폼이 다르고 이후 플렛폼이 없거나 이전 플렛폼의 높이가 이후 플렛폼의 높이보다 크면 플렛폼을 벗어나 떨어지는 것
if (beforePlatform != afterPlatform && (!afterPlatform || (beforePlatform && afterPlatform && beforePlatform->GetHeight(beforePlayerPosition.x) > afterPlatform->GetHeight(afterPlayerPosition.x))))
OnFalling();
// 중력 적용
m_speed.y -= 980.0f * deltaTime;
}
PhysicsComponent에서는 이동에 대한 처리를 담당한다.
이동 전, 후를 이용하여 착지했는지, 떨어지는 지를 계산하고 이벤트 함수를 호출한다.
void Player::AnimationComponent::Update(FLOAT deltaTime)
{
m_timer += deltaTime;
FLOAT interval{ DEFAULT_FRAME_INTERVAL };
do
{
if (auto currFrameInterval{ m_currFrameProp->Get<FLOAT>("interval") })
interval = *currFrameInterval;
if (m_timer >= interval)
{
if (m_frame >= m_currAniProp->GetChildCount() - 1)
{
// OnAnimationEnd에서 PlayAnimation을 호출하여 m_timer값이 0이되버리므로 저장해줬다가 다시 설정해줌
FLOAT timer{ m_timer };
OnAnimationEnd();
m_timer = timer - interval;
continue;
}
++m_frame;
m_timer -= interval;
OnFrameChange();
}
} while (m_timer >= interval);
}
AnimationComponent에서는 애니메이션에 대한 처리를 담당한다.
do-while문을 통해 deltaTime이 아무리 커도 그 사이 프레임에 대해 이벤트 함수를 호출 할 수 있도록 했다.
아직 플렛폼이 보이게 하지는 않았다.
잘 작동하는 것을 볼 수 있다!