ABOUT ME

🕹️게임 개발자가 되기를
✨꿈꾸는 사람

  • [009] 플레이어 컨트롤
    Programming/Dev 2023. 6. 6. 20:13

    DevLog

    이번 게시글에서는 간단하게 플레이어를 조종하는 것을 구현해보려고 한다.

     

     

     

     

    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이 아무리 커도 그 사이 프레임에 대해 이벤트 함수를 호출 할 수 있도록 했다.

     

     

     

     

    작동

    아직 플렛폼이 보이게 하지는 않았다.

    잘 작동하는 것을 볼 수 있다!

    'Programming > Dev' 카테고리의 다른 글

    [011] 이벤트 매니저  (0) 2023.08.29
    [010] 플레이어 컨트롤2  (0) 2023.07.29
    [008] 에디트 컨트롤  (0) 2023.03.25
    [007] 씬 전환  (0) 2023.03.18
    [006] 게임오브젝트 렌더링  (0) 2023.03.05

    댓글